有什么方法,我可以检查如果一个元素是可见的纯JS(没有jQuery) ?

因此,给定一个DOM元素,我如何检查它是否可见?我试着:

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

但这似乎并不奏效。我想知道我应该检查哪些属性。我想到了:

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

还有我可能漏掉的吗?


当前回答

const isVisible = (selector) => { let selectedElement let topElement let selectedData selectedElement = document.querySelector(selector) if (!selectedElement) { return false } selectedData = selectedElement.getBoundingClientRect() if (!selectedData || !Object.keys(selectedData)) { return false } if (!(selectedData.width > 0) || !(selectedData.height > 0)) { return false } topElement = document.elementFromPoint(selectedData.top, selectedData.left) if (selectedElement !== topElement) { return false } return true } const output = document.querySelector('.text') output.innerHTML = '.x element is visible: ' + isVisible('.x') .block { width: 100px; height: 100px; background: black; } .y { background: red; margin-top: -100px; } <div class="text"></div> <div class="x block"></div> <div class="y block"></div>

其他回答

根据MDN文档,元素的offsetParent属性将在它或它的任何父元素通过display style属性被隐藏时返回null。只要确保元素不是固定的。一个脚本来检查这个,如果你没有位置: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可能更简单一点,因为它考虑了更多的边缘情况,但我打赌它也会慢很多,所以如果你不得不多次重复这个操作,最好避免它。

如果我们只是收集检测能见度的基本方法,让我不要忘记:

opacity > 0.01; // probably more like .1 to actually be visible, but YMMV

至于如何获取属性:

element.getAttribute(attributename);

所以,在你的例子中:

document.getElementById('snDealsPanel').getAttribute('visibility');

But wha? It doesn't work here. Look closer and you'll find that visibility is being updated not as an attribute on the element, but using the style property. This is one of many problems with trying to do what you're doing. Among others: you can't guarantee that there's actually something to see in an element, just because its visibility, display, and opacity all have the correct values. It still might lack content, or it might lack a height and width. Another object might obscure it. For more detail, a quick Google search reveals this, and even includes a library to try solving the problem. (YMMV)

看看下面的问题,它们可能是这个问题的副本,有很好的答案,包括来自强大的约翰·雷西格的一些见解。但是,您的特定用例与标准用例略有不同,因此我将避免标记:

如何判断一个DOM元素是否在当前视口中可见? 如何检查一个元素是否真的可见javascript?

(EDIT: OP SAYS HE'S SCRAPING PAGES, NOT CREATING THEM, SO BELOW ISN'T APPLICABLE) A better option? Bind the visibility of elements to model properties and always make visibility contingent on that model, much as Angular does with ng-show. You can do that using any tool you want: Angular, plain JS, whatever. Better still, you can change the DOM implementation over time, but you'll always be able to read state from the model, instead of the DOM. Reading your truth from the DOM is Bad. And slow. Much better to check the model, and trust in your implementation to ensure that the DOM state reflects the model. (And use automated testing to confirm that assumption.)

这是对奥哈德·纳冯的回答的一点补充。

如果元素的中心属于另一个元素,我们就找不到它。

为了确保元素的其中一个点是可见的

function isElementVisible(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) return false;
    if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
        elem.getBoundingClientRect().width === 0) {
        return false;
    }
    var elementPoints = {
        'center': {
            x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
            y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
        },
        'top-left': {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().top
        },
        'top-right': {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().top
        },
        'bottom-left': {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().bottom
        },
        'bottom-right': {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().bottom
        }
    }

    for(index in elementPoints) {
        var point = elementPoints[index];
        if (point.x < 0) return false;
        if (point.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
        if (point.y < 0) return false;
        if (point.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
        let pointContainer = document.elementFromPoint(point.x, point.y);
        if (pointContainer !== null) {
            do {
                if (pointContainer === elem) return true;
            } while (pointContainer = pointContainer.parentNode);
        }
    }
    return false;
}

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

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

使用与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!