检查元素是否在 DOM 中可见

IT技术 javascript dom
2021-01-17 01:22:55

有什么方法可以检查元素在纯 JS(无 jQuery)中是否可见?

因此,例如,在此页面中:Performance Bikes,如果您将鼠标悬停在 Deals(在顶部菜单上)上,则会出现一个交易窗口,但在开始时并未显示。它在 HTML 中,但不可见。

那么,给定一个 DOM 元素,我如何检查它是否可见?我试过:

window.getComputedStyle(my_element)['display']);

但它似乎不起作用。我想知道我应该检查哪些属性。我想到了:

display !== 'none'
visibility !== 'hidden'

还有其他我可能会失踪的吗?

6个回答

根据此 MDN 文档只要元素或其任何父元素通过显示样式属性隐藏,元素的offsetParent属性就会返回null只需确保该元素未固定即可。如果position: fixed;页面上没有元素,则用于检查这一点的脚本可能如下所示:

// Where el is the DOM element you'd like to test for visibility
function isHidden(el) {
    return (el.offsetParent === null)
}

另一方面,如果您确实有可能在此搜索中被捕获的位置固定元素,您将遗憾地(并且慢慢地)不得不使用window.getComputedStyle(). 这种情况下的功能可能是:

// Where el is the DOM element you'd like to test for visibility
function isHidden(el) {
    var style = window.getComputedStyle(el);
    return (style.display === 'none')
}

选项#2 可能更简单一些,因为它考虑了更多的边缘情况,但我敢打赌它也慢很多,所以如果你必须多次重复这个操作,最好避免它。

另外仅供参考,刚刚发现它el.offsetParent不适用于非固定元素的 IE9。或者看起来是这样,无论如何。(不过,对于 IE11 来说还可以。)getComputedStyle毕竟是这样。
2021-03-11 01:22:55
我看到在其祖先正在制作的某些元素(例如 TABLE 元素)中getComputedStyle(element).display具有 的值本质上是无用的。tabledisplay:none
2021-03-15 01:22:55
对于 ie9+ie10,您可以检查 offsetParent = body 是否为不可见元素。
2021-03-29 01:22:55
只是对 window.getComputedStyle 方法的一个说明,它返回一个实时的CSSStyleDeclaration 对象。所以你只需要抓取一次样式,如果你需要多次执行检查,那么可以重新检查对象。
2021-03-29 01:22:55
唉!getComputedStyle无法正常工作:plnkr.co/edit/6CSCA2fe4Gqt4jCBP2wu?p=preview但是,也是如此offsetParent- 也许应该使用两者的组合?
2021-04-10 01:22:55

对我来说,所有其他解决方案都因某些情况而崩溃。

查看获胜答案:

http://plnkr.co/edit/6CSCA2fe4Gqt4jCBP2wu?p=preview

最终,我决定最好的解决方案是$(elem).is(':visible')- 然而,这不是纯 javascript。这是jquery..

所以我偷看了他们的来源并找到了我想要的

jQuery.expr.filters.visible = function( elem ) {
    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

这是来源:https : //github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js

@Michael 您可以轻松浏览 jQuery 代码,如果您使用的是任何现代 IDE(如果没有,请尝试),您可以在使用 jQuery 或任何其他库时跳转到正确的代码部分。您可以在浏览开源项目的代码库时学到很多东西。
2021-03-16 01:22:55
对于寻找也适用于 的版本的任何人visibility:hidden,添加&& window.getComputedStyle(elem).visibility !== "hidden"到此返回行的末尾似乎有效
2021-03-30 01:22:55
OP 要求没有 jQuery 的解决方案
2021-04-02 01:22:55
@YuvalA。:是的,因为该元素仍然可见。将元素设置为visibility:hidden不再显示任何内容,但仍采用元素的宽度和高度!
2021-04-08 01:22:55
这将返回true一个元素visibility:hidden
2021-04-10 01:22:55

如果您对用户可见感兴趣:

function isVisible(elem) {
    if (!(elem instanceof Element)) throw Error('DomUtil: elem is not an element.');
    const style = getComputedStyle(elem);
    if (style.display === 'none') return false;
    if (style.visibility !== 'visible') return false;
    if (style.opacity < 0.1) return false;
    if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
        elem.getBoundingClientRect().width === 0) {
        return false;
    }
    const elemCenter   = {
        x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
        y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
    };
    if (elemCenter.x < 0) return false;
    if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
    if (elemCenter.y < 0) return false;
    if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
    let pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y);
    do {
        if (pointContainer === elem) return true;
    } while (pointContainer = pointContainer.parentNode);
    return false;
}

测试(使用摩卡术语):

describe.only('visibility', function () {
    let div, visible, notVisible, inViewport, leftOfViewport, rightOfViewport, aboveViewport,
        belowViewport, notDisplayed, zeroOpacity, zIndex1, zIndex2;
    before(() => {
        div = document.createElement('div');
        document.querySelector('body').appendChild(div);
        div.appendChild(visible = document.createElement('div'));
        visible.style       = 'border: 1px solid black; margin: 5px; display: inline-block;';
        visible.textContent = 'visible';
        div.appendChild(inViewport = visible.cloneNode(false));
        inViewport.textContent = 'inViewport';
        div.appendChild(notDisplayed = visible.cloneNode(false));
        notDisplayed.style.display = 'none';
        notDisplayed.textContent   = 'notDisplayed';
        div.appendChild(notVisible = visible.cloneNode(false));
        notVisible.style.visibility = 'hidden';
        notVisible.textContent      = 'notVisible';
        div.appendChild(leftOfViewport = visible.cloneNode(false));
        leftOfViewport.style.position = 'absolute';
        leftOfViewport.style.right = '100000px';
        leftOfViewport.textContent = 'leftOfViewport';
        div.appendChild(rightOfViewport = leftOfViewport.cloneNode(false));
        rightOfViewport.style.right       = '0';
        rightOfViewport.style.left       = '100000px';
        rightOfViewport.textContent = 'rightOfViewport';
        div.appendChild(aboveViewport = leftOfViewport.cloneNode(false));
        aboveViewport.style.right       = '0';
        aboveViewport.style.bottom       = '100000px';
        aboveViewport.textContent = 'aboveViewport';
        div.appendChild(belowViewport = leftOfViewport.cloneNode(false));
        belowViewport.style.right       = '0';
        belowViewport.style.top       = '100000px';
        belowViewport.textContent = 'belowViewport';
        div.appendChild(zeroOpacity = visible.cloneNode(false));
        zeroOpacity.textContent   = 'zeroOpacity';
        zeroOpacity.style.opacity = '0';
        div.appendChild(zIndex1 = visible.cloneNode(false));
        zIndex1.textContent = 'zIndex1';
        zIndex1.style.position = 'absolute';
        zIndex1.style.left = zIndex1.style.top = zIndex1.style.width = zIndex1.style.height = '100px';
        zIndex1.style.zIndex = '1';
        div.appendChild(zIndex2 = zIndex1.cloneNode(false));
        zIndex2.textContent = 'zIndex2';
        zIndex2.style.left = zIndex2.style.top = '90px';
        zIndex2.style.width = zIndex2.style.height = '120px';
        zIndex2.style.backgroundColor = 'red';
        zIndex2.style.zIndex = '2';
    });
    after(() => {
        div.parentNode.removeChild(div);
    });
    it('isVisible = true', () => {
        expect(isVisible(div)).to.be.true;
        expect(isVisible(visible)).to.be.true;
        expect(isVisible(inViewport)).to.be.true;
        expect(isVisible(zIndex2)).to.be.true;
    });
    it('isVisible = false', () => {
        expect(isVisible(notDisplayed)).to.be.false;
        expect(isVisible(notVisible)).to.be.false;
        expect(isVisible(document.createElement('div'))).to.be.false;
        expect(isVisible(zIndex1)).to.be.false;
        expect(isVisible(zeroOpacity)).to.be.false;
        expect(isVisible(leftOfViewport)).to.be.false;
        expect(isVisible(rightOfViewport)).to.be.false;
        expect(isVisible(aboveViewport)).to.be.false;
        expect(isVisible(belowViewport)).to.be.false;
    });
});
当重叠元素(准确地说:所有重叠元素)的不透明度 < 1 时,情况如何?
2021-03-11 01:22:55
救生员。Js 就是这样一种......语言
2021-03-16 01:22:55
如果你想检查用户是否可能看到它,你必须使用一个scrollIntoView权利?!这是相当昂贵的。还有其他巧妙的方法吗?
2021-03-28 01:22:55
如果 elem 位于视口之外,则可以通过“if (!pointContainer) return false;”捕获边缘情况。检查第一个 pointContainer
2021-03-31 01:22:55

使用与 jQuery 相同的代码:

jQuery.expr.pseudos.visible = function( elem ) {
    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

所以,在一个函数中:

function isVisible(e) {
    return !!( e.offsetWidth || e.offsetHeight || e.getClientRects().length );
}

在我的 Win/IE10、Linux/Firefox.45、Linux/Chrome.52...

非常感谢没有 jQuery 的 jQuery!

强制结果为布尔值。作为e.offsetWidth一个整数,如果大于零!e.offsetWidth则返回(元素可见)。因此如果大于零添加另一个as in将返回它是or 显然的简写falsee.offsetWidth!!!e.offsetWidthtruee.offsetWidthreturn e.offsetWidth > 0 ? true : falsereturn e.offsetWidth > 0
2021-03-13 01:22:55
很好,但为什么!(双重否定)?
2021-04-07 01:22:55
不错,但不包括被溢出隐藏的元素。
2021-04-10 01:22:55

这可能有帮助: 通过将元素定位在最左边的位置来隐藏元素,然后检查 offsetLeft 属性。如果你想使用 jQuery,你可以简单地检查:visible选择器并获取元素的可见性状态。

HTML :

<div id="myDiv">Hello</div>

CSS :

<!-- for javaScript-->
#myDiv{
   position:absolute;
   left : -2000px;
}

<!-- for jQuery -->
#myDiv{
    visibility:hidden;
}

脚本:

var myStyle = document.getElementById("myDiv").offsetLeft;

if(myStyle < 0){
     alert("Div is hidden!!");
}

jQuery :

if(  $("#MyElement").is(":visible") == true )
{  
     alert("Div is visible!!");        
}

js小提琴

OP 请求无 jQuery 答案。
2021-03-23 01:22:55
对于 jQuery 示例,警报不应该说“Div 可见?”
2021-03-31 01:22:55
@StephenQuan,我已经用 jQuery 和 javaScript 解决方案更新了答案。
2021-04-01 01:22:55
我不想仅仅因为它的 offsetLeft 小于 0 就得出一个元素是完全隐藏的结论:如果它只有少数小于 0 的像素并且它的右侧部分是可见的怎么办?(我同意这看起来像一个愚蠢的设计,但你永远不会知道这些天的网页设计师。)最好将宽度添加到 offsetLeft 并查看结果是否仍然小于 0。以防万一。
2021-04-05 01:22:55
我猜是后来编辑的。当我回答时,线程中没有提到它。
2021-04-09 01:22:55