Chůze po DOM

DOM nám umožňuje dělat cokoli s prvky a jejich obsahem, ale nejprve se musíme dostat k odpovídajícímu objektu DOM.

Všechny operace na DOM začínají document objekt. To je hlavní „vstupní bod“ do DOM. Z něj můžeme přistupovat k libovolnému uzlu.

Zde je obrázek odkazů, které umožňují cestování mezi uzly DOM:

Pojďme si je probrat podrobněji.

Nahoře:documentElement a tělo

Nejvyšší uzly stromu jsou dostupné přímo jako document vlastnosti:

<html> =document.documentElement
Nejvyšší uzel dokumentu je document.documentElement . To je uzel DOM <html> tag.
<body> =document.body
Další široce používaný uzel DOM je <body> prvek – document.body .
<head> =document.head
<head> tag je dostupný jako document.head .
Má to háček:document.body může být null

Skript nemůže přistupovat k prvku, který v okamžiku spuštění neexistuje.

Zejména pokud je skript uvnitř <head> a poté document.body není k dispozici, protože jej prohlížeč ještě nepřečetl.

Takže v příkladu níže první alert ukazuje null :

<html>

<head>
 <script>
 alert( "From HEAD: " + document.body ); // null, there's no <body> yet
 </script>
</head>

<body>

 <script>
 alert( "From BODY: " + document.body ); // HTMLBodyElement, now it exists
 </script>

</body>
</html>
Ve světě DOM null znamená „neexistuje“

V DOM, null hodnota znamená „neexistuje“ nebo „není takový uzel“.

Děti:childNodes, firstChild, lastChild

Od této chvíle budeme používat dva termíny:

  • Podřízené uzly (nebo děti) – prvky, které jsou přímými dětmi. Jinými slovy, jsou vnořeny přesně do daného. Například <head> a <body> jsou potomky <html> prvek.
  • Potomci – všechny prvky, které jsou vnořeny do daného, ​​včetně dětí, jejich dětí a tak dále.

Například zde <body> má potomky <div> a <ul> (a několik prázdných textových uzlů):

<html>
<body>
 <div>Begin</div>

 <ul>
 <li>
 <b>Information</b>
 </li>
 </ul>
</body>
</html>

…A potomci <body> nejsou pouze přímé potomky <div> , <ul> ale také hlouběji vnořené prvky, jako je <li> (podřízený prvek <ul> ) a <b> (podřízený prvek <li> ) – celý podstrom.

Číslo childNodes kolekce uvádí všechny podřízené uzly, včetně textových uzlů.

Níže uvedený příklad ukazuje potomky document.body :

<html>
<body>
 <div>Begin</div>

 <ul>
 <li>Information</li>
 </ul>

 <div>End</div>

 <script>
 for (let i = 0; i < document.body.childNodes.length; i++) {
 alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT
 }
 </script>
 ...more stuff...
</body>
</html>

Zde si všimněte zajímavého detailu. Pokud spustíme výše uvedený příklad, posledním zobrazeným prvkem je <script> . Dokument ve skutečnosti obsahuje více věcí níže, ale v okamžiku spuštění skriptu jej prohlížeč ještě nečetl, takže jej skript nevidí.

Vlastnosti firstChild a lastChild umožnit rychlý přístup k prvním a posledním dětem.

Jsou to jen zkratky. Pokud existují podřízené uzly, platí vždy následující:

elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild

K dispozici je také speciální funkce elem.hasChildNodes() zkontrolovat, zda existují nějaké podřízené uzly.

Sbírky DOM

Jak vidíme, childNodes vypadá jako pole. Ale ve skutečnosti to není pole, ale spíše kolekce – speciální iterovatelný objekt podobný poli.

Má to dva důležité důsledky:

  1. Můžeme použít for..of iterovat to:
for (let node of document.body.childNodes) {
 alert(node); // shows all nodes from the collection
}

Je to proto, že je iterovatelný (poskytuje Symbol.iterator majetku, podle potřeby).

  1. Metody pole nebudou fungovat, protože to není pole:
alert(document.body.childNodes.filter); // undefined (there's no filter method!)

První věc je pěkná. Druhý je tolerovatelný, protože můžeme použít Array.from k vytvoření „skutečného“ pole z kolekce, pokud chceme metody pole:

alert( Array.from(document.body.childNodes).filter ); // function
Kolekce DOM jsou pouze pro čtení

Kolekce DOM a ještě více – vše navigační vlastnosti uvedené v této kapitole jsou pouze pro čtení.

Nemůžeme nahradit dítě něčím jiným přiřazením childNodes[i] = ... .

Změna DOM vyžaduje jiné metody. Uvidíme je v další kapitole.

Sbírky DOM jsou aktivní

Téměř všechny kolekce DOM s malými výjimkami jsou živé . Jinými slovy, odrážejí aktuální stav DOM.

Pokud ponecháme odkaz na elem.childNodes a přidat/odebrat uzly do DOM, pak se automaticky objeví v kolekci.

Nepoužívejte for..in procházet sbírkami

Kolekce lze iterovat pomocí for..of . Někdy se lidé snaží použít for..in za to.

Prosím, ne. for..in smyčka iteruje přes všechny vyčíslitelné vlastnosti. A kolekce mají některé „extra“ zřídka používané vlastnosti, které obvykle nechceme získat:

<body>
<script>
 // shows 0, 1, length, item, values and more.
 for (let prop in document.body.childNodes) alert(prop);
</script>
</body>

Sourozenci a rodič

Sourozenci jsou uzly, které jsou potomky stejného rodiče.

Například zde <head> a <body> jsou sourozenci:

<html>
 <head>...</head><body>...</body>
</html>
  • <body> se říká, že je „dalším“ nebo „pravým“ sourozencem <head> ,
  • <head> je považován za „předchozího“ nebo „levého“ sourozence <body> .

Další sourozenec je v nextSibling vlastnost a předchozí – v previousSibling .

Rodič je dostupný jako parentNode .

Například:

// parent of <body> is <html>
alert( document.body.parentNode === document.documentElement ); // true

// after <head> goes <body>
alert( document.head.nextSibling ); // HTMLBodyElement

// before <body> goes <head>
alert( document.body.previousSibling ); // HTMLHeadElement

Navigace pouze pro prvky

Výše uvedené vlastnosti navigace odkazují na vše uzly. Například v childNodes můžeme vidět textové uzly, uzly prvků a dokonce i uzly komentářů, pokud existují.

Ale pro mnoho úkolů nechceme uzly textu nebo komentářů. Chceme manipulovat s uzly prvků, které představují značky a tvoří strukturu stránky.

Pojďme se tedy podívat na další navigační odkazy, které přebírají pouze uzly prvků v úvahu:

Odkazy jsou podobné těm, které jsou uvedeny výše, pouze s Element slovo uvnitř:

  • children – pouze ty potomky, které jsou uzly prvků.
  • firstElementChild , lastElementChild – děti prvního a posledního prvku.
  • previousElementSibling , nextElementSibling – sousední prvky.
  • parentElement – nadřazený prvek.
Proč parentElement ? Může být rodič ne prvek?

parentElement vlastnost vrací „element“ rodiče, zatímco parentNode vrátí rodič „libovolný uzel“. Tyto vlastnosti jsou obvykle stejné:obě získají rodiče.

S jedinou výjimkou document.documentElement :

alert( document.documentElement.parentNode ); // document
alert( document.documentElement.parentElement ); // null

Důvodem je kořenový uzel document.documentElement (<html> ) má document jako její rodič. Ale document není uzel prvku, takže parentNode vrátí jej a parentElement ne.

Tento detail může být užitečný, když chceme přejít nahoru od libovolného prvku elem na <html> , ale ne na document :

while(elem = elem.parentElement) { // go up till <html>
 alert( elem );
}

Upravme jeden z výše uvedených příkladů:nahraďte childNodes s children . Nyní zobrazuje pouze prvky:

<html>
<body>
 <div>Begin</div>

 <ul>
 <li>Information</li>
 </ul>

 <div>End</div>

 <script>
 for (let elem of document.body.children) {
 alert(elem); // DIV, UL, DIV, SCRIPT
 }
 </script>
 ...
</body>
</html>

Další odkazy:tabulky

Dosud jsme popsali základní vlastnosti navigace.

Některé typy prvků DOM mohou pro pohodlí poskytovat další vlastnosti specifické pro jejich typ.

Tabulky jsou toho skvělým příkladem a představují obzvláště důležitý případ:

<table> prvek podporuje (kromě výše uvedeného) tyto vlastnosti:

  • table.rows – kolekce <tr> prvky tabulky.
  • table.caption/tHead/tFoot – odkazy na prvky <caption> , <thead> , <tfoot> .
  • table.tBodies – kolekce <tbody> prvků (podle standardu může být mnoho, ale vždy bude alespoň jeden – i když není ve zdrojovém HTML, prohlížeč ho vloží do DOM).

<thead> , <tfoot> , <tbody> prvky poskytují rows vlastnost:

  • tbody.rows – kolekce <tr> uvnitř.

<tr> :

  • tr.cells – kolekce <td> a <th> buňky uvnitř daného <tr> .
  • tr.sectionRowIndex – pozice (index) daného <tr> uvnitř přiloženého <thead>/<tbody>/<tfoot> .
  • tr.rowIndex – číslo <tr> v tabulce jako celku (včetně všech řádků tabulky).

<td> a <th> :

  • td.cellIndex – číslo buňky uvnitř <tr> .

Příklad použití:

<table id="table">
 <tr>
 <td>one</td><td>two</td>
 </tr>
 <tr>
 <td>three</td><td>four</td>
 </tr>
</table>

<script>
 // get td with "two" (first row, second column)
 let td = table.rows[0].cells[1];
 td.style.backgroundColor = "red"; // highlight it
</script>

Specifikace:tabulková data.

Existují také další navigační vlastnosti pro formuláře HTML. Podíváme se na ně později, až začneme pracovat s formuláři.

Shrnutí

Vzhledem k uzlu DOM můžeme přejít k jeho bezprostředním sousedům pomocí navigačních vlastností.

Jsou dvě hlavní sady:

  • Pro všechny uzly:parentNode , childNodes , firstChild , lastChild , previousSibling , nextSibling .
  • Pouze pro uzly prvků:parentElement , children , firstElementChild , lastElementChild , previousElementSibling , nextElementSibling .

Některé typy prvků DOM, např. tabulky, poskytují další vlastnosti a kolekce pro přístup k jejich obsahu.