マッスル・メモリー

筋肉エンジニアのブログ

【js 】DOM 文書ツリー間を行き来する ノードウォーキング

getElementByIdやquerySelectorAllメソッドはいずれもピンポイントで特定の要素ノード(群)を取得するためのメソッドです。

しかし、いちいち文書全体から目的の要素を検索するのは無駄が多くそれはそのままパフォーマンス低下の原因にもなります。

そこでDOMではあるノードを起点として相対的な位置関係からノードを取得することもできます。

これをノードウォーキングと呼びます。

コードを使用したページ

Qiita

// カレントノード
const currentNode = document.querySelectorAll('.tr-Item')[3];
currentNode
// <div class=<200b>"tr-Item"><200b><a class=<200b>"tr-Item_userImage" href=<200b>"/<200b>tomikiya"><200b>…<200b></a><200b><div class=<200b>"tr-Item_body"><200b>…<200b></div><200b></div><200b>

// 親ノードを取得
currentNode.parentNode
// <div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>…<200b></div><200b><div class=<200b>"tr-Item"><200b>....


// 最初の子ノードを取得
currentNode.firstChild
// <a class=<200b>"tr-Item_userImage" href=<200b>"/<200b>tomikiya"><200b><img src=<200b>"https:<200b>/<200b>/<200b>qiita-user-profile-images.imgix.net/<200b>https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F97601%2Fprofile-images%2F1473707244?ixlib=rb-1.2.2&auto=compress%2Cformat&lossless=0&w=48&s=f1b22bd97397df6085c15efc4fbc0ad4" alt=<200b>"tomikiya"><200b></a><200b>

// 最後の子ノードを取得
currentNode.lastChild
// <div class=<200b>"tr-Item_body"><200b><a class=<200b>"tr-Item_title" href=<200b>"/<200b>tomikiya/<200b>items/<200b>11d7a21ce778038567c9"><200b>[Excel]実務で役に立たない!?VBAを使わないで3D迷路を作る<200b></a><200b><div class=<200b>"tr-Item_meta"><200b>…<200b></div><200b></div><200b>

// 子ノードに含まれるノード数を取得
currentNode.childNodes.length
// 2

// 直前のノード(兄ノード)
currentNode.previousSibling
// <div class=<200b>"tr-Item"><200b><a class=<200b>"tr-Item_userImage" href=<200b>"/<200b>youwht"><200b>…<200b></a><200b><div class=<200b>"tr-Item_body"><200b>…<200b></div><200b></div><200b>
// カレントノードが[3]番目を取得していたのに対し、previousSiblingは1つ手前の[2]番目のノードを取得している
currentNode.previousSibling === document.querySelectorAll('.tr-Item')[2];
//true

// 直後のノード(弟ノード)
currentNode.nextSibling
// <div class=<200b>"tr-Item"><200b><a class=<200b>"tr-Item_userImage" href=<200b>"/<200b>mi_upto"><200b>…<200b></a><200b><div class=<200b>"tr-Item_body"><200b>…<200b></div><200b></div><200b>
// カレントノードが[3]番目を取得していたのに対し、nextSiblingは1つ後の[4]番目のノードを取得している
currentNode.nextSibling === document.querySelectorAll('.tr-Item')[4];
// true

これでノードウォーキングをすることができますが、以下の例を見てください。

コードを使用したページ

スタック・オーバーフロー

// カレントノード取得
const currentNode = document.getElementsByTagName('li')[0];

// 弟ノード取得?
currentNode.nextSibling;
// #text

// ノードの種類判定
currentNode.nextSibling.nodeType
// 3(テキストノード)

実はnextSiblingなど上記で紹介した取得方法では、要素ノード以外も取得してしまいます。

この場合だとテキストノードを取得してしまいます。

currentNode.nodeType
// 1(要素ノード)

要素ノードを取り出したい場合はnodeTypeが1であるのでその条件で取得するのもありですが、

const nextNode = document.getElementsByTagName('li')[1];
nextNode
// <li class=<200b>"-item inbox-button-item"><200b>…<200b></li><200b>

currentNode.nextElementSibling;
// <li class=<200b>"-item inbox-button-item"><200b>…<200b></li><200b>

currentNode.nextElementSibling.nodeType
// 1

currentNode.nextElementSibling === nextNode;
// true

のようにnextElementSiblingを使えば要素ノードに限定してくれます。

// 最初の子要素ノードを取得
currentNode.firstElementChild

// 最後の子要素ノードを取得
currentNode.lastElementChild

// 直前の要素ノードを取得
currentNode.perviousElementSibling

// 直後の要素ノードを取得
currentNode.nextElementSibling

// 子ノードに含まれる要素ノード数を取得
currentNode.childElementCount

// 親ノードを取得
currentNode.parentNode

ちなみにnodeTypeの一覧です。

要素ノード    = 1
属性ノード    = 2
テキストノード  = 3
CDATAセクション = 4
実体参照ノード  = 5
実体宣言ノード  = 6
処理命令ノード  = 7
コメントノード  = 8
文書ノード    = 9
文書型宣言ノード = 10
文書の断片    = 11
記法宣言ノード  = 12