[Перевод] В чём разница между узлом и элементом DOM?

Объектная модель документа (Document Object Model, DOM) — это интерфейс, который рассматривает HTML- или XML-документы в виде древовидных структур, каждый узел которых является объектом документа. DOM, кроме того, предоставляет набор методов для выполнения запросов к дереву документа, для изменения его структуры и для выполнения с ним некоторых других действий.

pgrnen4vgpxvbcf3tyywsx267l4.png

При работе с DOM, кроме того, используется термин «элемент». Элементы очень похожи на узлы, но, всё же, это — не одно и то же. В чём же разница?

1. Узел DOM


Ключ к пониманию различия между узлом и элементом заключается в знании о том, что собой представляет узел.

Если рассматривать ситуацию в общих чертах, то оказывается, что DOM-документ включает в себя иерархию узлов. У каждого узла может быть родитель и (или) потомок.

Посмотрим на следующий HTML-документ:



  
    My Page
  
  
    
    

My Page

    

Thank you for visiting my web page!

  

В документ входит следующая иерархия узлов:
b498762b66db828720b0daa35a643c6b.png

DOM-представление документа

 — это узел в дереве документа. У него есть два дочерних узла — и .

У  есть три дочерних узла — комментарий , заголовок

и абзац

. Родительским элементом узла является узел .

Теги в HTML-документе представляют узлы. Интересно то, что обычный текст — это тоже узел. Узел-абзац

имеет потомка — текстовый узел Thank you for visiting my web page!.

▍Типы узлов


Как различать узлы разных типов? Ответ кроется в интерфейсе DOM, который носит имя Node. В частности — речь идёт о свойстве Node.nodeType.

Это свойство может иметь одно из следующих значений, представляющих тип узла:

  • Node.ELEMENT_NODE
  • Node.ATTRIBUTE_NODE
  • Node.TEXT_NODE
  • Node.CDATA_SECTION_NODE
  • Node.PROCESSING_INSTRUCTION_NODE
  • Node.COMMENT_NODE
  • Node.DOCUMENT_NODE
  • Node.DOCUMENT_TYPE_NODE
  • Node.DOCUMENT_FRAGMENT_NODE
  • Node.NOTATION_NODE

Имена констант указывают на тип узла. Например, Node.ELEMENT_NODE представляет узел-элемент, Node.TEXT_NODE — это текстовый узел, Node.DOCUMENT_NODE — это узел-документ и так далее.

Например, давайте выберем узел-абзац и посмотрим на его свойство nodeType:

const paragraph = document.querySelector('p');

paragraph.nodeType === Node.ELEMENT_NODE; // => true

Свойство paragraph.nodeType, как и ожидалось, содержит значение Node.ELEMENT_NODE, которое указывает на то, что абзац — это элемент.

В абзаце, кроме того, имеется текстовый узел:

const paragraph = document.querySelector('p');
const firstChild = paragraph.childNodes[0];

firstChild.nodeType === Node.TEXT_NODE; // => true

Есть тип узла, который представляет всё дерево узлов документа. Это — Node.DOCUMENT_NODE:
document.nodeType === Node.DOCUMENT_NODE; // => true

2. Элемент DOM


После того, как мы разобрались с тем, что такое узел DOM, пришло время поговорить том, чем различаются узлы и элементы DOM.

Если вы как следует вникли в сущность термина «узел», то вам уже всё должно быть понятно. Элемент — это узел особого типа — Node.ELEMENT_NODE. Это — такой же тип узла, как и другие, представляющие весь документ, комментарии, тексты и прочие узлы DOM.

Если говорить простыми словами, то элемент — это узел, который объявлен с использованием тега в HTML-документе. , , </code>, <code><body></code>, <code><h2><font color="#3AC1EF"></code>, <code><p></code> — это всё элементы, так как они представлены тегами.</p><p>А вот сам документ, комментарий, текст — это не элементы, так как они не представлены соответствующими тегами: </p><pre><code class="html"><!DOCTYPE html> <html>   <body>     <!-- Page Body -->     <p>       Thank you for visiting my web page!     </p>   </body> </html> </code></pre><br />В DOM-API JavaScript конструктор узла — это <code>Node</code>, а <code>HTMLElement</code> — это конструктор элемента. Абзац, хотя это и узел DOM, является ещё и элементом, соответствующий объект является и экземпляром <code>Node</code>, и экземпляром <code>HTMLElement</code>: <pre><code class="javascript">const paragraph = document.querySelector('p'); paragraph instanceof Node;        // => true paragraph instanceof HTMLElement; // => true </code></pre><br />Если говорить простыми словами, элемент — это подтип узла — так же как кошка — подтип животного.<h2><span>3. Свойства DOM: узлы и элементы</span></h2><br />Помимо различения узлов и элементов нужно ещё различать свойства DOM, которые содержат исключительно узлы или исключительно элементы.<p>Следующие свойства могут содержать либо узел (<code>Node</code>), либо коллекцию узлов (<code>NodeList</code>): </p><pre><code class="javascript">node.parentNode; // Node или null node.firstChild; // Node или null node.lastChild;  // Node или null node.childNodes; // NodeList </code></pre><br />А вот следующие свойства могут содержать либо элементы (<code>HTMLElement</code>), либо коллекции элементов (<code>HTMLCollection</code>): <pre><code class="javascript">node.parentElement; // HTMLElement или null node.children;      // HTMLCollection </code></pre><br />Так как и свойство <code>node.childNodes</code>, и свойство <code>node.children</code> возвращает коллекцию сущностей-потомков, возникает вопрос о том, почему существуют оба этих свойства. На самом деле это — хороший вопрос! <p>Рассмотрим следующий элемент-абзац, содержащий какой-то текст: </p><pre><code class="html"><p>   <b>Thank you</b> for visiting my web page! </p> </code></pre><br />Откройте этот демонстрационный пример и посмотрите на свойства <code>childNodes</code> и <code>children</code> узла-абзаца: <pre><code class="javascript">const paragraph = document.querySelector('p'); paragraph.childNodes; // NodeList:       [HTMLElement, Text] paragraph.children;   // HTMLCollection: [HTMLElement] </code></pre><br />Коллекция <code>paragraph.childNodes</code> содержит 2 узла: текст, оформленный полужирным шрифтом с помощью тега <code><b></code> (<code><b>Thank you</b></code>), и текстовый узел (<code>for visiting my web page!</code>).<p>Но в коллекции <code>paragraph.children</code> имеется лишь 1 элемент, представленный тегом <code><b></code> (<code><b>Thank you</b></code>).</p><p>Так как свойство <code>paragraph.children</code> содержит только элементы, текстовый узел в него не включён. Произошло это из-за того, что с точки зрения системы это — текст (<code>Node.TEXT_NODE</code>), а не элемент (<code>Node.ELEMENT_NODE</code>).</p><p>То, что у нас есть и <code>node.childNodes</code>, и <code>node.children</code>, позволяет нам выбирать именно ту коллекцию элементов-потомков некоего узла DOM, с которой нужно работать. Это может быть либо коллекция, содержащая все узлы-потомки, либо только те узлы-потомки, которые являются элементами.</p><h2><span>4. Итоги</span></h2><br />Документ DOM — это иерархическая коллекция узлов. У каждого узла могут быть родители и (или) потомки.<p>Отличие между узлами и элементами DOM становится очевидным в том случае, если есть понимание того, что такое узел.</p><p>У узлов имеется свойство, указывающее на их тип. Один из этих типов соответствует элементам DOM. Элементы представлены тегами в HTML-документе.</p><p>Сталкивались ли вы со сложностями, касающимися различения узлов и элементов DOM? </p><p><img src="https://habrastorage.org/webt/ou/g5/kh/oug5kh6sjydt9llengsiebnp40w.png" alt="oug5kh6sjydt9llengsiebnp40w.png" /></p></div> <p class="copyrights"><span class="source">© <a target="_blank" rel="nofollow" href="https://habr.com/ru/post/539096/?utm_campaign=539096&amp;utm_source=habrahabr&amp;utm_medium=rss">Habrahabr.ru</a></span></p> </div> <br> <!--<div align="left"> <script type="text/topadvert"> load_event: page_load feed_id: 12105 pattern_id: 8187 tech_model: </script><script type="text/javascript" charset="utf-8" defer="defer" async="async" src="//loader.topadvert.ru/load.js"></script> </div> <br>--> <div style="padding-left: 20px;"> <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-2514821055276660" crossorigin="anonymous"></script> <!-- PCNews 336x280 --> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-2514821055276660" data-ad-slot="1200562049" data-ad-format="auto"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> </div> <!-- comments --> <noindex> <div style="margin: 25px;" id="disqus_thread"></div> <script type="text/javascript"> var disqus_shortname = 'pcnewsru'; var disqus_identifier = '1054243'; var disqus_title = '[Перевод] В чём разница между узлом и элементом DOM?'; var disqus_url = 'http://pcnews.ru/blogs/%5Bperevod%5D_v_cem_raznica_mezdu_uzlom_i_elementom_dom-1054243.html'; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); </script> <!--<noscript>Please enable JavaScript to view the <a rel="nofollow" href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>--> <!--<a href="http://disqus.com" rel="nofollow" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>--> </noindex> </div> <br class="clearer"/> </div> <br class="clearer"/> <div id="footer-2nd"></div> <div id="footer"> <br/><br/> <ul class="horz-menu"> <li class="about"><a href="/info/about.html" title="О проекте">О проекте</a></li> <li class="additional-menu"><a href="/archive.html" title="Архив материалов">Архив</a> </li> <li class="additional-menu"><a href="/info/reklama.html" title="Реклама" class="menu-item"><strong>Реклама</strong></a> <a href="/info/partners.html" title="Партнёры" class="menu-item">Партнёры</a> <a href="/info/legal.html" title="Правовая информация" class="menu-item">Правовая информация</a> <a href="/info/contacts.html" title="Контакты" class="menu-item">Контакты</a> <a href="/feedback.html" title="Обратная связь" class="menu-item">Обратная связь</a></li> <li class="email"><a href="mailto:pcnews@pcnews.ru" title="Пишите нам на pcnews@pcnews.ru"><img src="/media/i/email.gif" alt="e-mail"/></a></li> <li style="visibility: hidden"> <noindex> <!-- Rating@Mail.ru counter --> <script type="text/javascript"> var _tmr = window._tmr || (window._tmr = []); _tmr.push({id: "93125", type: "pageView", start: (new Date()).getTime()}); (function (d, w, id) { if (d.getElementById(id)) return; var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id; ts.src = (d.location.protocol == "https:" ? "https:" : "http:") + "//top-fwz1.mail.ru/js/code.js"; var f = function () { var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s); }; if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } })(document, window, "topmailru-code"); </script> <noscript> <div style="position:absolute;left:-10000px;"> <img src="//top-fwz1.mail.ru/counter?id=93125;js=na" style="border:0;" height="1" width="1" alt="Рейтинг@Mail.ru"/> </div> </noscript> <!-- //Rating@Mail.ru counter --> </noindex> </li> </ul> </div> <!--[if lte IE 7]> <iframe id="popup-iframe" frameborder="0" scrolling="no"></iframe> <![endif]--> <!--<div id="robot-image"><img class="rbimg" src="i/robot-img.png" alt="" width="182" height="305" /></div>--> <!--[if IE 6]> <script>DD_belatedPNG.fix('#robot-image, .rbimg');</script><![endif]--> </div> <!--[if lte IE 7]> <iframe id="ie-popup-iframe" frameborder="0" scrolling="no"></iframe> <![endif]--> <div id="footer-adlinks"></div> <noindex> <!--LiveInternet counter--><script type="text/javascript"> document.write("<a rel='nofollow' href='//www.liveinternet.ru/click' "+ "target=_blank><img src='//counter.yadro.ru/hit?t45.6;r"+ escape(document.referrer)+((typeof(screen)=="undefined")?"": ";s"+screen.width+"*"+screen.height+"*"+(screen.colorDepth? screen.colorDepth:screen.pixelDepth))+";u"+escape(document.URL)+ ";"+Math.random()+ "' alt='' title='LiveInternet' "+ "border='0' width='1' height='1'><\/a>") </script><!--/LiveInternet--> <!-- Rating@Mail.ru counter --> <script type="text/javascript"> var _tmr = window._tmr || (window._tmr = []); _tmr.push({id: "93125", type: "pageView", start: (new Date()).getTime()}); (function (d, w, id) { if (d.getElementById(id)) return; var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id; ts.src = "https://top-fwz1.mail.ru/js/code.js"; var f = function () {var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s);}; if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } })(document, window, "topmailru-code"); </script><noscript><div> <img src="https://top-fwz1.mail.ru/counter?id=93125;js=na" style="border:0;position:absolute;left:-9999px;" alt="Top.Mail.Ru" /> </div></noscript> <!-- //Rating@Mail.ru counter --> <!-- Yandex.Metrika counter --> <script type="text/javascript"> (function (d, w, c) { (w[c] = w[c] || []).push(function () { try { w.yaCounter23235610 = new Ya.Metrika({ id: 23235610, clickmap: true, trackLinks: true, accurateTrackBounce: true, webvisor: true, trackHash: true }); } catch (e) { } }); var n = d.getElementsByTagName("script")[0], s = d.createElement("script"), f = function () { n.parentNode.insertBefore(s, n); }; s.type = "text/javascript"; s.async = true; s.src = "https://mc.yandex.ru/metrika/watch.js"; if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } })(document, window, "yandex_metrika_callbacks"); </script> <noscript> <div><img src="https://mc.yandex.ru/watch/23235610" style="position:absolute; left:-9999px;" alt=""/> </div> </noscript> <!-- /Yandex.Metrika counter --> <!-- Default Statcounter code for PCNews.ru http://pcnews.ru--> <script type="text/javascript"> var sc_project=9446204; var sc_invisible=1; var sc_security="14d6509a"; </script> <script type="text/javascript" src="https://www.statcounter.com/counter/counter.js" async></script> <!-- End of Statcounter Code --> <script> (function (i, s, o, g, r, a, m) { i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () { (i[r].q = i[r].q || []).push(arguments) }, i[r].l = 1 * new Date(); a = s.createElement(o), m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m) })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); ga('create', 'UA-46280051-1', 'pcnews.ru'); ga('send', 'pageview'); </script> <script async="async" src="/assets/uptolike.js?pid=49295"></script> </noindex> <!--<div id="AdwolfBanner40x200_842695" ></div>--> <!--AdWolf Asynchronous Code Start --> <script type="text/javascript" src="https://pcnews.ru/js/blockAdblock.js"></script> </body> </html>