この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。きんくまです。
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
感想
細かいプロパティの違いがわかって面白かったです。