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

IT技术 javascript dom visibility
2021-01-13 07:43:49

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

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

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

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

6个回答

对于第 2 点。

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

这是elementFromPoint上的 PPK 测试页面

来自MDN 的文档

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

如果我们要检查父元素的可见性,则此方法失败。因此,在父级的指定点,子级将可用,而它是父级的一部分。只需考虑<div>包含多个嵌套元素的父级事实上, parent<div>是有效且可见的,但输出没有显示这一点。
2021-03-12 07:43:49
@e-satis:它适用于我的 Firefox。它在 Opera 中不起作用。
2021-03-14 07:43:49
那不是 IE 唯一的解决方案吗?
2021-03-16 07:43:49
元素的透明度怎么样?我想当您elementFromPoint()说该元素与另一个元素完全重叠(并且您将其视为不可见)但用户可以看到它您可能会遇到这种情况
2021-03-25 07:43:49
@KonstantinSmolyanin 您问的是与 OP 不同的东西,以查看某个元素下是否有元素(而不是元素是否在某个元素上方)。为此,您可以随时更改最顶层元素的 CSSdisplay: none并再次检查相同的区域。如果出现的其他东西不是之前最顶层元素的父元素,那么下面就是某些东西。
2021-03-29 07:43:49

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

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;
};

不知道性能如何:)

原来的 on_top 功能对我来说不太一致,修改后的功能太缺乏性能了。但其余的都很性感:)
2021-03-12 07:43:49
element.offsetWidth/offsetHeight 试图找到什么?他们似乎总是在 Chrome 中返回 0。而 document.documentElement.clientHeight 正在获取元素的高度;不应该是 document.body.clientHeight 吗?
2021-03-13 07:43:49
对于窗口的可见高度,这似乎适用于跨浏览器(甚至是旧 IE): height = window.innerHeight?window.innerHeight:document.documentElement.clientHeight;
2021-03-13 07:43:49
修改后的 on_top 函数为我创造了奇迹......谢谢@Tobias
2021-03-31 07:43:49
如果一个元素与另一个元素完全重叠(就这种方法而言)但该重叠元素具有一定的透明度怎么办?因此,下面的元素对用户可见,但被该方法视为不可见。
2021-04-05 07:43:49

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

这不是真的,Selenium 仅通过验证 css 'display' 和 'visibility' 属性来检查 isVisible,在您分享的链接github.com/SeleniumHQ/selenium/blob/master/javascript/ ... Selenium.prototype.isVisible ... 返回(可见性!=“隐藏”&& _isDisplayed);
2021-03-14 07:43:49
但是 ChromeDriver 在点异常时抛出 ElementNotClickable 是该元素实际上隐藏在另一个元素下并且不会收到点击。但我认为其他浏览器不会抛出它(我确定 Firefox 不会检查)
2021-03-23 07:43:49
2021-03-26 07:43:49
2021-03-29 07:43:49

有趣的问题。

这将是我的方法。

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

希望能帮助到你。

这是 Mozilla 的 MDC 摘要:从正在调用 elementFromPoint 方法的文档中返回元素,该元素是位于给定点下方的最顶层元素。该点是通过坐标指定的,以 CSS 像素为单位,相对于包含文档的窗口或框架中左上角的点。
2021-03-15 07:43:49
你能更详细地解释一下 document.elementFromPoint 吗?
2021-03-28 07:43:49

这就是我迄今为止所拥有的。它涵盖了 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;
}