127

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

我不仅仅意味着检查visibilityanddisplay属性。我的意思是,检查元素不是

  • visibility: hidden或者display: none
  • 在另一个元素下方
  • 滚动到屏幕边缘

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

4

15 回答 15

102

对于第 2 点。

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

这是elementFromPoint上的 PPK 测试页面。

来自MDN 的文档

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

于 2009-05-25T04:41:21.953 回答
40

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

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,然后检查元素的任何部分是否在视口内,并且它没有隐藏在另一个元素的“下方”(实际上我只检查元素中心的一个点,所以它不是 100% 保证的——但是如果你真的需要的话,你可以修改脚本来迭代元素的所有点......)。

更新

修改了检查每个像素的 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;
};

不知道性能如何:)

于 2009-10-09T10:00:07.307 回答
7

正如 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

于 2010-05-17T06:39:19.867 回答
4

有趣的问题。

这将是我的方法。

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

希望能帮助到你。

于 2009-05-24T18:33:39.683 回答
3

这就是我到目前为止所拥有的。它涵盖了 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;
}
于 2009-05-24T15:47:18.703 回答
2

Prototype 的Element 库在方法方面是最强大的查询库之一。我建议您查看 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!
    }
    

    如果您想检查子元素的兄弟姐妹,您也可以这样做:

    var child = $("myparagraph");
    if(!child.previous("mywrapper")){
      // I lost my bro!
    } 
    else {
      // I found my bro!
    }
    
  3. 同样,如果我正确理解您的意思,Element lib 可以帮助您:) 您可以检查视口的实际尺寸和元素的偏移量,以便计算您的元素是否“离屏”。

祝你好运!

我在http://gist.github.com/117125上粘贴了prototypejs 的测试用例。在您的情况下,我们似乎根本无法信任getStyle()。为了最大限度地提高 isMyElementReallyVisible 函数的可靠性,您应该结合以下内容:

  • 检查计算的样式(dojo 有一个很好的实现,你可以借用)
  • 检查 viewportoffset(原型原生方法)
  • 检查 z-index 是否存在“下方”问题(在 Internet Explorer 下可能有问题)
于 2009-05-23T19:47:41.380 回答
1

一种方法是:

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

于 2011-09-12T06:54:55.207 回答
1
/**
 * 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;
}
于 2014-02-11T16:07:18.517 回答
1

您可以使用 clientHeight 或 clientWidth 属性

function isViewable(element){
  return (element.clientHeight > 0);
}
于 2014-02-13T14:33:44.627 回答
1

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

  • 底部
  • 最佳
  • 剩下
  • 宽度——取决于浏览器
  • 高度——取决于浏览器

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

于 2014-06-05T05:13:12.103 回答
0

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

当拖动结束时,将拖动的项目边界与所有“感兴趣的元素”(即具有类“dont_hide”的元素或 ids 数组)进行比较。对 window.onscroll 和 window.onresize 执行相同的操作。用特殊的属性或类名标记任何隐藏的元素,或者简单地执行您想要的任何操作。

隐藏测试非常简单。对于“完全隐藏”,您想知道所有角是在拖动项目边界内还是在视口外。对于部分隐藏,您正在寻找与相同测试匹配的单个角。

于 2009-05-24T14:13:56.450 回答
0

我认为检查元素自身的可见性和显示属性对于要求 #1 来说不够好,即使您使用 currentStyle/getComputedStyle。您还必须检查元素的祖先。如果祖先是隐藏的,那么元素也是如此。

于 2009-09-21T16:16:05.597 回答
-1

检查元素的 offsetHeight 属性。如果大于 0,则可见。注意:此方法不涵盖设置可见性:隐藏样式的情况。但无论如何,这种风格很奇怪。

于 2009-04-01T09:34:15.007 回答
-1

这是一个示例脚本和测试用例。涵盖定位元素,可见性:隐藏,显示:无。没有测试 z-index,假设它有效。

<!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>
于 2009-05-25T15:45:19.083 回答
-2

这是响应的一部分,它告诉您元素是否在视口中。您可能需要使用 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;
}
于 2014-08-26T07:56:51.913 回答