Check if element is visible in DOM

后端 未结 18 1686

Is there any way that I can check if an element is visible in pure JS (no jQuery) ?

So, for example, in this page: Performance Bikes, if you hover over Deals (on the

相关标签:
18条回答
  • 2020-11-22 08:03

    If we're just collecting basic ways of detecting visibility, let me not forget:

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

    And as to how to obtain attributes:

    element.getAttribute(attributename);
    

    So, in your example:

    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)

    Check out the following, which are possible duplicates of this question, with excellent answers, including some insight from the mighty John Resig. However, your specific use-case is slightly different from the standard one, so I'll refrain from flagging:

    • How to tell if a DOM element is visible in the current viewport?
    • How to check if an element is really visible with 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.)

    0 讨论(0)
  • 2020-11-22 08:06

    If you're interested in visible by the user:

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

    Tested on (using mocha terminology):

    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;
        });
    });
    
    0 讨论(0)
  • 2020-11-22 08:07

    The accepted answer did not worked for me.Year 2020 answer:

    1) The (elem.offsetParent !== null) method works fine in Firefox but not in Chrome. For Chrome "position: fixed;" will also make offsetParent return "null" even the element if visible in the page.

    Demo:

    //different results in Chrome and Firefox
    console.log(document.querySelector('#hidden1').offsetParent); //null Chrome & Firefox
    console.log(document.querySelector('#fixed1').offsetParent); //null in Chrome, not null in Firefox
        <div id="hidden1" style="display:none;"></div>
        <div id="fixed1" style="position:fixed;"></div>

    you can see this guy megatest https://stackoverflow.com/a/11639664/4481831 (run it with multiple browsers to see the differences).

    2) The (getComputedStyle(elem).display !== 'none') do not work because the element can be invisible because one of the parents display property is set to none, getComputedStyle will not catch that.

    Demo:

    var child1 = document.querySelector('#child1');
    console.log(getComputedStyle(child1).display);
    //child will show "block" instead of "none"
    <div id="parent1" style="display:none;">
      <div id="child1" style="display:block"></div>
    </div>

    3) The (elem.clientHeight !== 0). This method is not influenced by position fixed and it also check if element parents are not-visible. But it has problems with simple elements that do not have a css layout, see more here

    Demo:

    console.log(document.querySelector('#div1').clientHeight); //not zero
    console.log(document.querySelector('#span1').clientHeight); //zero
    <div id="div1">test1 div</div>
    <span id="span1">test2 span</span>

    4) The (elem.getClientRects().length !== 0) that seem to overpass the problems of the previous 3 methods. However it has problems with elements that use CSS tricks (other then "display:none") to hide in the page.

    console.log(document.querySelector('#notvisible1').getClientRects().length);
    console.log(document.querySelector('#notvisible1').clientHeight);
    console.log(document.querySelector('#notvisible2').getClientRects().length);
    console.log(document.querySelector('#notvisible2').clientHeight);
    console.log(document.querySelector('#notvisible3').getClientRects().length);
    console.log(document.querySelector('#notvisible3').clientHeight);
    <div id="notvisible1" style="height:0; overflow:hidden; background-color:red;">not visible 1</div>
    
    <div id="notvisible2" style="visibility:hidden; background-color:yellow;">not visible 2</div>
    
    <div id="notvisible3" style="opacity:0; background-color:blue;">not visible 3</div>

    CONCLUSION: So what I have showed you is that no method is perfect. To make a proper visibility check you must use a combination of the last 3 methods.

    0 讨论(0)
  • 2020-11-22 08:07

    This is a way to determine it for all css properties including visibility:

    html:

    <div id="element">div content</div>
    

    css:

    #element
    {
    visibility:hidden;
    }
    

    javascript:

    var element = document.getElementById('element');
     if(element.style.visibility == 'hidden'){
    alert('hidden');
    }
    else
    {
    alert('visible');
    }
    

    It works for any css property and is very versatile and reliable.

    0 讨论(0)
  • 2020-11-22 08:09

    The jQuery code from http://code.jquery.com/jquery-1.11.1.js has an isHidden param

    var isHidden = function( elem, el ) {
        // isHidden might be called from jQuery#filter function;
        // in that case, element will be second argument
        elem = el || elem;
        return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
    };
    

    So it looks like there is an extra check related to the owner document

    I wonder if this really catches the following cases:

    1. Elements hidden behind other elements based on zIndex
    2. Elements with transparency full making them invisible
    3. Elements positioned off screen (ie left: -1000px)
    4. Elements with visibility:hidden
    5. Elements with display:none
    6. Elements with no visible text or sub elements
    7. Elements with height or width set to 0
    0 讨论(0)
  • 2020-11-22 08:10

    Combining a couple answers above:

    function isVisible (ele) {
        var style = window.getComputedStyle(ele);
        return  style.width !== "0" &&
        style.height !== "0" &&
        style.opacity !== "0" &&
        style.display!=='none' &&
        style.visibility!== 'hidden';
    }
    

    Like AlexZ said, this may be slower than some of your other options if you know more specifically what you're looking for, but this should catch all of the main ways elements are hidden.

    But, it also depends what counts as visible for you. Just for example, a div's height can be set to 0px but the contents still visible depending on the overflow properties. Or a div's contents could be made the same color as the background so it is not visible to users but still rendered on the page. Or a div could be moved off screen or hidden behind other divs, or it's contents could be non-visible but the border still visible. To a certain extent "visible" is a subjective term.

    0 讨论(0)
提交回复
热议问题