如何用 JavaScript 检查元素是否真正可见?

在 JavaScript 中,如何检查元素是否实际可见?

我的意思不仅仅是检查 visibilitydisplay属性,我的意思是检查元素不是

  • visibility: hiddendisplay: none
  • 在另一个元素下面
  • 在屏幕边缘滚动

由于技术原因,我不能包括任何脚本。但我可以使用 原型机,因为它已经在页面上。

137044 次浏览

检查元素的 offsetHeight 属性。如果它大于0,则它是可见的。注意: 这种方法不包括设置了可见性: 隐藏样式的情况。但这种风格本来就很奇怪。

Prototype 的 元素库是方法方面最强大的查询库之一。我建议您查看 API。

一些提示:

  1. 检查可见性可能很麻烦,但是您可以使用 Element.getStyle()方法和 Element.visible()方法组合成一个自定义函数。使用 getStyle(),您可以检查实际的计算样式。

  2. 我不知道你说的“下面”到底是什么意思:)如果你的意思是它有一个特定的祖先,例如,一个包装器 div,你可以使用 Element.up(cssRule):

    var child = $("myparagraph");
    if(!child.up("mywrapper")){
    // I lost my mom!
    }
    else {
    // I found my mom!
    }
    

    如果想检查 child 元素的兄弟元素,也可以这样做:

    var child = $("myparagraph");
    if(!child.previous("mywrapper")){
    // I lost my bro!
    }
    else {
    // I found my bro!
    }
    
  3. Again, Element lib can help you if I understand correctly what you mean :) You can check the actual dimensions of the viewport and the offset of your element so you can calculate if your element is "off screen".

Good luck!

I pasted a test case for prototypejs at http://gist.github.com/117125. It seems in your case we simply cannot trust in getStyle() at all. For maximizing the reliability of the isMyElementReallyVisible function you should combine the following:

  • Checking the computed style (dojo has a nice implementation that you can borrow)
  • Checking the viewportoffset (prototype native method)
  • Checking the z-index for the "beneath" problem (under Internet Explorer it may be buggy)

捕获鼠标拖动和视口事件(onmouseup、 onresize、 onroll)。

当拖动结束时,将所拖动的项边界与所有“感兴趣的元素”(即,具有类“ don _ hide”或 id 数组的元素)进行比较。对 window.onroll 和 window.onresize 执行同样的操作。用一个特殊的属性或类名标记隐藏的任何元素,或者当场执行任何您想要的操作。

隐藏测试很简单。对于“完全隐藏”,您想知道是否所有角落都在拖动项边界内或外面的视图。对于部分隐藏,你要寻找一个单一的角落匹配相同的测试。

这就是我目前所知道的。它同时涵盖了1和3。但是我仍然在为2而挣扎,因为我对 Prototype 不是很熟悉(我更喜欢 jQuery 类型的家伙)。

function isVisible( elem ) {
var $elem = $(elem);


// First check if elem is hidden through css as this is not very costly:
if ($elem.getStyle('display') == 'none' || $elem.getStyle('visibility') == 'hidden' ) {
//elem is set through CSS stylesheet or inline to invisible
return false;
}


//Now check for the elem being outside of the viewport
var $elemOffset = $elem.viewportOffset();
if ($elemOffset.left < 0 || $elemOffset.top < 0) {
//elem is left of or above viewport
return false;
}
var vp = document.viewport.getDimensions();
if ($elemOffset.left > vp.width || $elemOffset.top > vp.height) {
//elem is below or right of vp
return false;
}


//Now check for elements positioned on top:
//TODO: Build check for this using Prototype...
//Neither of these was true, so the elem was visible:
return true;
}

有趣的问题。

这就是我的方法。

  1. 首先检查 element.style.visible! = = ‘ hide’& & element.style.display! = = ‘ none’
  2. 然后使用 document.elementFromPoint (element.offsetLeft,element.offsetTop)测试返回的元素是否是我期望的元素,这很难检测一个元素是否与另一个元素完全重叠。
  3. 最后测试 offsetTop 和 offsetLeft 是否位于视图端口中,并将滚动偏移量考虑在内。

希望能有帮助。

第二点。

我看到没有人建议使用 document.elementFromPoint(x,y),对我来说,它是测试一个元素是否被另一个元素嵌套或隐藏的最快方法。可以将目标元素的偏移量传递给函数。

这是 ElementFromPoint的 PPK 测试页。

来自 MDN 的文档:

elementFromPoint()方法ー在 Document 和 ShadowRoot 对象上都可用ー返回指定坐标(相对于视口)处的最顶层元素。

下面是一个示例脚本和测试用例。覆盖定位元素,可见性: 隐藏,显示: 无。没有测试 Z 指数,假设它有效。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<style type="text/css">
div {
width: 200px;
border: 1px solid red;
}
p {
border: 2px solid green;
}
.r {
border: 1px solid #BB3333;
background: #EE9999;
position: relative;
top: -50px;
height: 2em;
}
.of {
overflow: hidden;
height: 2em;
word-wrap: none;
}
.of p {
width: 100%;
}


.of pre {
display: inline;
}
.iv {
visibility: hidden;
}
.dn {
display: none;
}
</style>
<script src="http://www.prototypejs.org/assets/2008/9/29/prototype-1.6.0.3.js"></script>
<script>
function isVisible(elem){
if (Element.getStyle(elem, 'visibility') == 'hidden' || Element.getStyle(elem, 'display') == 'none') {
return false;
}
var topx, topy, botx, boty;
var offset = Element.positionedOffset(elem);
topx = offset.left;
topy = offset.top;
botx = Element.getWidth(elem) + topx;
boty = Element.getHeight(elem) + topy;
var v = false;
for (var x = topx; x <= botx; x++) {
for(var y = topy; y <= boty; y++) {
if (document.elementFromPoint(x,y) == elem) {
// item is visible
v = true;
break;
}
}
if (v == true) {
break;
}
}
return v;
}


window.onload=function() {
var es = Element.descendants('body');
for (var i = 0; i < es.length; i++ ) {
if (!isVisible(es[i])) {
alert(es[i].tagName);
}
}
}
</script>
</head>
<body id='body'>
<div class="s"><p>This is text</p><p>More text</p></div>
<div class="r">This is relative</div>
<div class="of"><p>This is too wide...</p><pre>hidden</pre>
<div class="iv">This is invisible</div>
<div class="dn">This is display none</div>
</body>
</html>

即使使用 currentStyle/getComputedStyle,我认为检查元素自身的可见性和显示属性也不足以满足需求 # 1。您还必须检查元素的祖先。如果一个祖先是隐藏的,那么元素也是隐藏的。

我不知道在旧的或者不太现代的浏览器中有多少是支持的,但是我正在使用类似的东西(不需要任何库) :

function visible(element) {
if (element.offsetWidth === 0 || element.offsetHeight === 0) return false;
var height = document.documentElement.clientHeight,
rects = element.getClientRects(),
on_top = function(r) {
var x = (r.left + r.right)/2, y = (r.top + r.bottom)/2;
return document.elementFromPoint(x, y) === element;
};
for (var i = 0, l = rects.length; i < l; i++) {
var r = rects[i],
in_viewport = r.top > 0 ? r.top <= height : (r.bottom > 0 && r.bottom <= height);
if (in_viewport && on_top(r)) return true;
}
return false;
}

它检查元素的面积 > 0,然后检查元素的任何部分是否在 viewport 中,是否隐藏在另一个元素之下(实际上我只检查元素中心的一个点,所以不能百分之百确定——但是如果你真的需要的话,你可以修改脚本来遍历元素的所有点)。

更新

修改 on _ top 函数,检查每个像素:

on_top = function(r) {
for (var x = Math.floor(r.left), x_max = Math.ceil(r.right); x <= x_max; x++)
for (var y = Math.floor(r.top), y_max = Math.ceil(r.bottom); y <= y_max; y++) {
if (document.elementFromPoint(x, y) === element) return true;
}
return false;
};

不知道表演情况:)

正如 jkl 指出的,检查元素的可见性或显示是不够的。你必须检查它的祖先。Selenium 在验证元素的可见性时执行此操作。

查看 selenium-api. js 文件中的 Selenium.Prototype.isVisible 方法。

Http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails/selenium-core/scripts/selenium-api.js

一种方法是:

isVisible(elm) {
while(elm.tagName != 'BODY') {
if(!$(elm).visible()) return false;
elm = elm.parentNode;
}
return true;
}

来源: https://github.com/atetlaw/Really-Easy-Field-Validation/blob/master/validation.js#L178

/**
* Checks display and visibility of elements and it's parents
* @param  DomElement  el
* @param  boolean isDeep Watch parents? Default is true
* @return {Boolean}
*
* @author Oleksandr Knyga <oleksandrknyga@gmail.com>
*/
function isVisible(el, isDeep) {
var elIsVisible = true;


if("undefined" === typeof isDeep) {
isDeep = true;
}


elIsVisible = elIsVisible && el.offsetWidth > 0 && el.offsetHeight > 0;


if(isDeep && elIsVisible) {


while('BODY' != el.tagName && elIsVisible) {
elIsVisible = elIsVisible && 'hidden' != window.getComputedStyle(el).visibility;
el = el.parentElement;
}
}


return elIsVisible;
}

可以使用 clientHeight 或 clientWidth 属性

function isViewable(element){
return (element.clientHeight > 0);
}

试试 element.getBoundingClientRect()。 它将返回一个具有属性的对象

  • 底部
  • 头儿
  • 左边
  • 宽度-依赖于浏览器
  • 高度-依赖于浏览器

检查元素的 BoundingClientRect的宽度和高度是否为零,这是隐藏元素或不可见元素的值。如果值大于零,元素应该在主体中可见。然后检查 bottom属性是否小于 screen.height,这意味着元素在 viewport 中。(从技术上讲,你还必须考虑到浏览器窗口的顶部,包括搜索栏、按钮等。)

下面是响应的一部分,它告诉您一个元素是否在 viewport 中。 您可能需要使用 elementFromPoint 检查它上面是否没有任何内容,但是它稍微长一些。

function isInViewport(element) {
var rect = element.getBoundingClientRect();
var windowHeight = window.innerHeight || document.documentElement.clientHeight;
var windowWidth = window.innerWidth || document.documentElement.clientWidth;


return rect.bottom > 0 && rect.top < windowHeight && rect.right > 0 && rect.left < windowWidth;
}