jQuery の :visible
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ display:none になってるかをチェックしてる
◆ offsetWidth/offsetHeight/clientRects
◆ 実際のサイズが 0 や visiblisty が hidden でも clientRects はとれる
◆ offsetWidth/offsetHeight/clientRects
◆ 実際のサイズが 0 や visiblisty が hidden でも clientRects はとれる
JavaScript で要素が表示されてるかを調べようとしたのですが 簡単な API って用意されてなさそうです
そういえば jQuery は :visible や :hidden っていう独自 pseudo class あったけどどう実装されてるんだろう? と調べてみました
とりあえず 1 系 2 系 3 系それぞれの新しい 2 バージョンのコードです
新しい 3 系は 2 バージョンとも変更なくてシンプルなコードでした
hidden は visible じゃないときで visible は offset の width/height があって client rects が取れる時です
「visibility: hidden」 は visible として扱うみたいです
また width/height と padding など全部 0 にして 「overflow:hidden」 にして実質存在しない状態にしたものも visible として扱っています
2 系最後の 2.2.4 では 3 系で 「||」 で略していたところをちゃんと 「> 0」 にしています
マイナスがないなら一緒なのですがコメントをみると Opera 12.12 以下は offsetWidth/offsetHeight にマイナスが来ることがあるらしく古い Opera の対応をするためにこう書いてたみたいです
旧 Opera を捨てた最新版ではシンプルになってます
2.1.4 では 2.2.4 とは逆で hidden の方を実装して visible を NOT hidden という書き方です
このときは client rects は使わず offset 系だけです
offset 系だけだと width/height を 0 にしてしまえば 0 になるので正確には 「display:none」 とは違います
1.12.4 はちょっと長いです
reliableHiddenOffsets で hidden の offset が信頼できるかをまずみて できるなら offset と client rects を見ます
信頼できない場合は document にアタッチ前なら hidden でそれ以外なら親を辿りながら表示されてるかをチェックしています
1 系は古いブラウザも対応してるせいでこんなことになってるのでしょう
それでも 2 で使われてない client rects もみてるあたりこっちのほうが新しいのかもです
ただちょっと気になったところがあります
elem.style.display が未設定なら jQuery.css (中で computed style を取得してます) を返すつくりです
これだと 要素の style では 「display:block」 を指定していても css で 「display:none !important」 がある場合に対応できてないと思います
返り値は none じゃなくて block です
新しい jQuery では問題ありませんが
古いブラウザ対応のために 1 系使ってると問題でそうです
といっても信頼できない offset なブラウザしかここに行かないのでかなり限られた条件ですけどね
最後に 1.11.3 ですが 1.12.4 とは違っています
offset が信頼できないときに親を再帰的にチェックしたり document にアタッチ済みかのチェックをしていましたがそれが無い状態です
軽い気持ちでそれぞれ 2 バージョンずつ見てみたら 3.1 と 3.2 以外全部違って予想以上に長くなりました
結局のところ 実際に見えてるかやサイズが確保されてるかとは違って 「display:none」 をチェックしてるだけでした
一部例外なバージョンもありましたけど
そういえば jQuery は :visible や :hidden っていう独自 pseudo class あったけどどう実装されてるんだろう? と調べてみました
とりあえず 1 系 2 系 3 系それぞれの新しい 2 バージョンのコードです
3.2.1 / 3.1.1
jQuery.expr.pseudos.hidden = function( elem ) {
return !jQuery.expr.pseudos.visible( elem );
};
jQuery.expr.pseudos.visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};
return !jQuery.expr.pseudos.visible( elem );
};
jQuery.expr.pseudos.visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};
2.2.4
jQuery.expr.filters.hidden = function( elem ) {
return !jQuery.expr.filters.visible( elem );
};
jQuery.expr.filters.visible = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
// Use OR instead of AND as the element is not visible if either is true
// See tickets #10406 and #13132
return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
};
return !jQuery.expr.filters.visible( elem );
};
jQuery.expr.filters.visible = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
// Use OR instead of AND as the element is not visible if either is true
// See tickets #10406 and #13132
return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
};
2.1.4
jQuery.expr.filters.hidden = function(elem) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
};
jQuery.expr.filters.visible = function(elem) {
return !jQuery.expr.filters.hidden(elem);
};
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
};
jQuery.expr.filters.visible = function(elem) {
return !jQuery.expr.filters.hidden(elem);
};
1.12.4
function getDisplay( elem ) {
return elem.style && elem.style.display || jQuery.css( elem, "display" );
}
function filterHidden( elem ) {
// Disconnected elements are considered hidden
if ( !jQuery.contains( elem.ownerDocument || document, elem ) ) {
return true;
}
while ( elem && elem.nodeType === 1 ) {
if ( getDisplay( elem ) === "none" || elem.type === "hidden" ) {
return true;
}
elem = elem.parentNode;
}
return false;
}
jQuery.expr.filters.hidden = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return support.reliableHiddenOffsets() ?
( elem.offsetWidth <= 0 && elem.offsetHeight <= 0 &&
!elem.getClientRects().length ) :
filterHidden( elem );
};
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
return elem.style && elem.style.display || jQuery.css( elem, "display" );
}
function filterHidden( elem ) {
// Disconnected elements are considered hidden
if ( !jQuery.contains( elem.ownerDocument || document, elem ) ) {
return true;
}
while ( elem && elem.nodeType === 1 ) {
if ( getDisplay( elem ) === "none" || elem.type === "hidden" ) {
return true;
}
elem = elem.parentNode;
}
return false;
}
jQuery.expr.filters.hidden = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return support.reliableHiddenOffsets() ?
( elem.offsetWidth <= 0 && elem.offsetHeight <= 0 &&
!elem.getClientRects().length ) :
filterHidden( elem );
};
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
1.11.3
jQuery.expr.filters.hidden = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
(!support.reliableHiddenOffsets() &&
((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
};
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
(!support.reliableHiddenOffsets() &&
((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
};
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
新しい 3 系は 2 バージョンとも変更なくてシンプルなコードでした
hidden は visible じゃないときで visible は offset の width/height があって client rects が取れる時です
「visibility: hidden」 は visible として扱うみたいです
また width/height と padding など全部 0 にして 「overflow:hidden」 にして実質存在しない状態にしたものも visible として扱っています
2 系最後の 2.2.4 では 3 系で 「||」 で略していたところをちゃんと 「> 0」 にしています
マイナスがないなら一緒なのですがコメントをみると Opera 12.12 以下は offsetWidth/offsetHeight にマイナスが来ることがあるらしく古い Opera の対応をするためにこう書いてたみたいです
旧 Opera を捨てた最新版ではシンプルになってます
2.1.4 では 2.2.4 とは逆で hidden の方を実装して visible を NOT hidden という書き方です
このときは client rects は使わず offset 系だけです
offset 系だけだと width/height を 0 にしてしまえば 0 になるので正確には 「display:none」 とは違います
1.12.4 はちょっと長いです
reliableHiddenOffsets で hidden の offset が信頼できるかをまずみて できるなら offset と client rects を見ます
信頼できない場合は document にアタッチ前なら hidden でそれ以外なら親を辿りながら表示されてるかをチェックしています
1 系は古いブラウザも対応してるせいでこんなことになってるのでしょう
それでも 2 で使われてない client rects もみてるあたりこっちのほうが新しいのかもです
ただちょっと気になったところがあります
function getDisplay( elem ) {
return elem.style && elem.style.display || jQuery.css( elem, "display" );
}
return elem.style && elem.style.display || jQuery.css( elem, "display" );
}
elem.style.display が未設定なら jQuery.css (中で computed style を取得してます) を返すつくりです
これだと 要素の style では 「display:block」 を指定していても css で 「display:none !important」 がある場合に対応できてないと思います
返り値は none じゃなくて block です
新しい jQuery では問題ありませんが
古いブラウザ対応のために 1 系使ってると問題でそうです
といっても信頼できない offset なブラウザしかここに行かないのでかなり限られた条件ですけどね
最後に 1.11.3 ですが 1.12.4 とは違っています
offset が信頼できないときに親を再帰的にチェックしたり document にアタッチ済みかのチェックをしていましたがそれが無い状態です
軽い気持ちでそれぞれ 2 バージョンずつ見てみたら 3.1 と 3.2 以外全部違って予想以上に長くなりました
結局のところ 実際に見えてるかやサイズが確保されてるかとは違って 「display:none」 をチェックしてるだけでした
一部例外なバージョンもありましたけど