[JavaScript] nodeツリーが見えるか見えないかを判定したい
こんにちは。きんくまです。
htmlのnodeツリーが不可視状態かどうかを判定したかたったです。
簡単にいうと、ユーザーからブラウザで見えているのか、見えてないのかを判定したいのです。
不可視の例
空のdiv
<div></div>
改行タグや半角・全角の空白文字だけだったり。
<div> <br><br> <p> </p> </div>
scriptタグ
<script> //何か書かれている </script>
要素は存在しているけど、スタイルで不可視になっている
<div> <p style="display:none;">あいうえお</p> </div>
上の組み合わせでこんなやつとか
<head> <style> .sample { display:none; } </style> </head> <div> <p style="display:none;">あいうえお</p> <script> // 何か </script> <div class="sample"> <p><img src="xxx"></p> </div> </div>
判定のソースコード
// 見えるNodeか function isVisibleNode(node){ if(node.nodeType === Node.TEXT_NODE){ let text = node.nodeValue.trim(); if(text.length === 0){ return false; } return true; } if(node.nodeType !== Node.ELEMENT_NODE){ return false; } const styles = window.getComputedStyle(node); const displayValue = styles.getPropertyValue('display'); if(displayValue === 'none'){ return false; } const visibility = styles.getPropertyValue('visibility'); if(visibility === 'hidden'){ return false; } const name = node.tagName.toLowerCase(); if(name === 'br' || name === 'script' || name === 'style'){ return false; // Nodeの末端 }else if(node.children.length === 0){ // 画像のみが見えるものとする return name === 'img'; } return true; } // 自分自身が見えるNodeか。見えていたら子供Nodeもチェック function checkNodeVisibleWithChildren(node){ let isVisible = isVisibleNode(node); const children = node.children; if(isVisible && children.length > 0){ let invisibleCount = 0; for(let i = 0; i < children.length; i++){ const child = children[i]; // 再帰的にチェック const isChildVisible = checkNodeVisibleWithChildren(child); if (!isChildVisible) { invisibleCount++; } } // 全ての子供Nodeが見えなかった if(invisibleCount === children.length){ isVisible = false; } } return isVisible; } // 空の文字列か?半角、全角スペース、改行文字は空文字と判断 function checkIsEmptyText(node){ // 見えるtextだけを取り出す const text = node.innerText.trim(); return text.length === 0; } // 空のコンテンツか。空ならtrue function checkEmptyContents(containerId){ const container = document.getElementById(containerId); let isEmpty = checkIsEmptyText(container); if(isEmpty){ let isVisible = checkNodeVisibleWithChildren(container); isEmpty = !isVisible; } return isEmpty; }
判定基準は、CSSで不可視になっているnodeは見えないと判断。 テキストか画像が見えていたら、見える要素と判断します。
親nodeから再帰的に子供nodeをチェックしていって、全部が見えない要素だったら見えないと判断します。
なので、もしdivに背景色をつけているものがあったとしても、それは空nodeの見えないものとして判断します。
使い方
checkEmptyContents({親のid});
例. htmlでこれを判定したい場合
<div id="wrapper"> <p style="display:none;">あいうえお</p> </div>
jsでこう書く
const result = checkEmptyContents('wrapper'); // result -> true
作ってわかったこと
childNodesとchildrenの違い
childNodes
テキストノード以外の全ての子供Nodeを取れる。
コメントノードなども入ってくる
参考: Node.nodeType
children
Elementノードのみ取得できる(いわゆるhtmlタグです。scriptタグなども含まれます)
参考: Element
innerTextとtextContentの違い
innerText
見えている文字列のみ取得する。もしcssで不可視になっていたら取得しない
textContent
見えない文字も取得する。scriptの文字さえも取得する
参考: Node.textContent
感想
細かいプロパティの違いがわかって面白かったです。