web-dev-qa-db-fra.com

Vérifier si l'élément est visible dans le DOM

Est-il possible de vérifier si un élément est visible dans JS pur (pas de jQuery)?

Ainsi, par exemple, dans cette page: Performance Bikes , si vous survolez Deals (dans le menu supérieur), une fenêtre d’offres apparaît, mais elle ne s’affiche pas au début. C'est dans le HTML mais ce n'est pas visible.

Alors, étant donné un élément DOM, comment puis-je vérifier s'il est visible ou non? J'ai essayé: 

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

mais cela ne semble pas fonctionner. Je me demande quels attributs devrais-je vérifier? Cela me vient à l'esprit:

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

Tous les autres que je pourrais manquer?

254
Hommer Smith

Selon cette documentation MDN , la propriété offsetParent d'un élément retournera null chaque fois que lui-même ou l'un de ses parents est masqué via la propriété de style d'affichage. Assurez-vous simplement que l'élément n'est pas corrigé. Un script pour vérifier cela, si vous n'avez pas d'éléments position: fixed; sur votre page, pourrait ressembler à ceci:

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

D’autre part, si vous avez des éléments fixes de position susceptibles d’être pris dans cette recherche, vous devrez malheureusement (et lentement) utiliser window.getComputedStyle() . La fonction dans ce cas pourrait être:

// 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')
}

L'option 2 est probablement un peu plus simple puisqu'elle représente davantage de cas Edge, mais je parie que c'est beaucoup plus lent aussi, donc si vous devez répéter cette opération plusieurs fois, il est préférable de l'éviter.

436
AlexZ

Toutes les autres solutions ont cassé pour une situation pour moi .. 

Voir la réponse gagnante dans: 

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

Finalement, j'ai décidé que la meilleure solution était $(elem).is(':visible') - cependant, ce n'est pas du javascript pur. c'est jquery .. 

alors j'ai jeté un coup d'œil à leur source et trouvé ce que je voulais

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

Ceci est la source: https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js

84
guy mograbi

Si vous êtes intéressé par visible par l'utilisateur:

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

Testé sur (avec moka terminologie): 

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;
    });
});
33
Ohad Navon

Ceci peut aider: Cachez l'élément en le positionnant à l'extrême gauche, puis vérifiez la propriété offsetLeft. Si vous souhaitez utiliser jQuery, vous pouvez simplement vérifier le sélecteur : visible et obtenir l'état de visibilité de l'élément.

HTML:

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

CSS:

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

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

javaScript:

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

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

jQuery:

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

jsFiddle

30
Ashad Shanto

Utilisez le même code que jQuery:

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

Donc, dans une fonction:

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

Fonctionne comme un charme dans mes Win/IE10, Linux/Firefox 45, Linux/Chrome 52 ...

Merci beaucoup à jQuery sans jQuery!

21
Yvan

Combinant quelques réponses ci-dessus:

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

Comme AlexZ l'a dit, cela peut être plus lent que certaines de vos autres options si vous savez plus précisément ce que vous recherchez, mais cela devrait englober tous les principaux moyens de cacher les éléments. 

Mais cela dépend aussi de ce qui compte pour vous. Par exemple, la hauteur d'un div peut être définie sur 0px mais le contenu reste visible en fonction des propriétés de débordement. Ou bien le contenu d'une div pourrait être de la même couleur que l'arrière-plan, de sorte qu'il ne soit pas visible pour les utilisateurs mais restitué sur la page. Ou un div peut être déplacé hors de l'écran ou caché derrière d'autres divs, ou son contenu peut ne pas être visible mais la bordure reste toujours visible. Dans une certaine mesure, "visible" est un terme subjectif.

8
Matthew

Si element est visible régulièrement (display: block et visibillity: visible), mais qu'un conteneur parent est masqué, nous pouvons utiliser clientWidth et clientHeight pour vérifier cela.

function isVisible (ele) {
  return  ele.clientWidth !== 0 &&
    ele.clientHeight !== 0 &&
    ele.style.opacity !== 0 &&
    ele.style.visibility !== 'hidden';
}

Plunker (cliquez ici)

6
Vlada

J'ai une solution plus performante que la solution getComputedStyle () d'AlexZZ quand on a des éléments 'fixes' de position, si on veut ignorer certains cas Edge (vérifier les commentaires):

function isVisible(el) {
    /* offsetParent would be null if display 'none' is set.
       However Chrome, IE and MS Edge returns offsetParent as null for elements
       with CSS position 'fixed'. So check whether the dimensions are zero.

       This check would be inaccurate if position is 'fixed' AND dimensions were
       intentionally set to zero. But..it is good enough for most cases.*/
    if (!el.offsetParent && el.offsetWidth === 0 && el.offsetHeight === 0) {
        return false;
    }
    return true;
}

Note latérale: À proprement parler, la «visibilité» doit d'abord être définie. Dans mon cas, je considère un élément visible tant que je peux exécuter toutes les méthodes/propriétés du DOM sur cet élément sans problème (même si l'opacité est 0 ou la propriété de visibilité CSS est "masquée" etc.).

6
Munawwar

Si nous ne faisons que rassembler des méthodes de base pour détecter la visibilité, ne me laissez pas oublier:

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

Et quant à la façon d'obtenir des attributs:

element.getAttribute(attributename);

Donc, dans votre exemple:

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

Mais quoi? Ça ne marche pas ici. Si vous regardez de plus près, vous constaterez que la visibilité est mise à jour non pas en tant qu'attribut sur l'élément, mais en utilisant la propriété style. C'est l'un des nombreux problèmes à essayer de faire ce que vous faites. Entre autres: vous ne pouvez pas garantir qu'il y a réellement quelque chose à voir dans un élément, simplement parce que sa visibilité, son affichage et son opacité ont tous les valeurs correctes. Il peut encore manquer de contenu ou de hauteur et de largeur. Un autre objet pourrait le masquer. Pour plus de détails, une recherche rapide sur Google révèle this , et inclut même une bibliothèque pour tenter de résoudre le problème. (YMMV)

Découvrez ce qui suit, qui sont des doublons possibles de cette question, avec d’excellentes réponses, y compris un aperçu du puissant John Resig. Cependant, votre cas d'utilisation spécifique est légèrement différent de celui standard, aussi je m'abstiendrai de signaler:

(ÉDITER: OP DIT QU'IL DÉGALE DES PAGES, NE PAS LES CRÉER, SO SOUS LA BASSE N'EST PAS APPLICABLE). Liez la visibilité des éléments aux propriétés du modèle et rendez toujours la visibilité subordonnée à ce modèle, comme le fait Angular avec ng-show. Vous pouvez le faire en utilisant n’importe quel outil: angulaire, simple JS, peu importe. Mieux encore, vous pouvez modifier l’implémentation du DOM dans le temps, mais vous pourrez toujours lire l’état à partir du modèle, au lieu du DOM. Lire votre vérité du DOM est mauvais. Et lent. Il est préférable de vérifier le modèle et de faire confiance à votre implémentation pour que l’état DOM reflète le modèle. (Et utilisez des tests automatisés pour confirmer cette hypothèse.) 

5
XML

Donc, ce que j'ai trouvé est la méthode la plus faisable:

function visible(Elm) {
  if(!Elm.offsetHeight && !Elm.offsetWidth) { return false; }
  if(getComputedStyle(Elm).visibility === 'hidden') { return false; }
  return true;
}

Ceci est construit sur ces faits:

  • Un élément display: none (même imbriqué) n'a pas de largeur ni de hauteur.
  • visiblity est hidden même pour les éléments imbriqués.

Il n'est donc pas nécessaire de tester offsetParent ou de boucler dans l'arborescence DOM pour déterminer quel parent a visibility: hidden. Cela devrait fonctionner même dans IE 9.

Vous pouvez discuter si opacity: 0 et les éléments réduits (a une largeur mais pas de hauteur - ou vice versa) ne sont pas vraiment visibles non plus. Mais là encore, ils ne sont pas cachés.

3
Tokimon

À titre de référence, il convient de noter que getBoundingClientRect() peut fonctionner dans certains cas.

Par exemple, une simple vérification que l'élément est masqué à l'aide de display: none pourrait ressembler à ceci:

var box = element.getBoundingClientRect();
var visible = box.width && box.height;

Ceci est également pratique car il couvre également les cas de largeur nulle, de hauteur nulle et de position: fixed. Cependant, il ne doit pas signaler les éléments cachés avec opacity: 0 ou visibility: hidden (mais offsetParent non plus).

2
incarnate

Le code jQuery de http://code.jquery.com/jquery-1.11.1.js a un paramètre isHidden

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

Il semble donc y avoir une vérification supplémentaire liée au document du propriétaire

Je me demande si cela concerne vraiment les cas suivants:

  1. Éléments cachés derrière d'autres éléments basés sur zIndex
  2. Eléments avec transparence complète les rendant invisibles
  3. Éléments positionnés hors écran (c.-à-d. À gauche: -1000px)
  4. Eléments avec visibilité: caché
  5. Eléments avec affichage: aucun
  6. Éléments sans texte visible ou sous-éléments
  7. Éléments dont la hauteur ou la largeur est définie sur 0
2
Scott Izu

Un petit ajout à la réponse de ohad navon.

Si le centre de l'élément appartient à un autre élément, nous ne le trouverons pas.

Donc, pour s'assurer que l'un des points de l'élément est visible

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;
}
2
Guy Messika

Cela renvoie true si et seulement si l'élément et tous ses ancêtres sont visibles. Il ne regarde que les propriétés display et visibilitystyle:

    var isVisible = function(el){
        // returns true iff el and all its ancestors are visible
        return el.style.display !== 'none' && el.style.visibility !== 'hidden'
        && (el.parentElement? isVisible(el.parentElement): true)
    };
1
Max Heiber

Améliorer la réponse de la réponse de @Guy Messika ci-dessus , rompre et renvoyer la valeur false si le point central 'X est <0 est erroné, l'élément de droite pouvant passer dans la vue. voici un correctif:

private isVisible(elem) {
    const style = getComputedStyle(elem);

    if (style.display === 'none') return false;
    if (style.visibility !== 'visible') return false;
    if ((style.opacity as any) === 0) return false;

    if (
        elem.offsetWidth +
        elem.offsetHeight +
        elem.getBoundingClientRect().height +
        elem.getBoundingClientRect().width === 0
    ) return false;

    const elementPoints = {
        center: {
            x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
            y: elem.getBoundingClientRect().top + elem.offsetHeight / 2,
        },
        topLeft: {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().top,
        },
        topRight: {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().top,
        },
        bottomLeft: {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().bottom,
        },
        bottomRight: {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().bottom,
        },
    };

    const docWidth = document.documentElement.clientWidth || window.innerWidth;
    const docHeight = document.documentElement.clientHeight || window.innerHeight;

    if (elementPoints.topLeft.x > docWidth) return false;
    if (elementPoints.topLeft.y > docHeight) return false;
    if (elementPoints.bottomRight.x < 0) return false;
    if (elementPoints.bottomRight.y < 0) return false;

    for (let index in elementPoints) {
        const point = elementPoints[index];
        let pointContainer = document.elementFromPoint(point.x, point.y);
        if (pointContainer !== null) {
            do {
                if (pointContainer === elem) return true;
            } while (pointContainer = pointContainer.parentNode);
        }
    }
    return false;
}
0
Israel

C'est un moyen de le déterminer pour toutes les propriétés css, y compris la visibilité:

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

Cela fonctionne pour n'importe quelle propriété css et est très polyvalent et fiable.

0
William Green

C'est ce que j'ai fait:

HTML & CSS: Fait de l'élément caché par défaut

<html>
<body>

<button onclick="myFunction()">Click Me</button>

<p id="demo" style ="visibility: hidden;">Hello World</p> 

</body>
</html> 

JavaScript: ajout d'un code pour vérifier si la visibilité est masquée ou non:

<script>
function myFunction() {
   if ( document.getElementById("demo").style.visibility === "hidden"){
   document.getElementById("demo").style.visibility = "visible";
   }
   else document.getElementById("demo").style.visibility = "hidden";
}
</script>
0
Ajas Jansher

Voici le code que j'ai écrit pour trouver le seul visible parmi quelques éléments similaires et renvoyer la valeur de son attribut "class" sans jQuery:

  // Build a NodeList:
  var nl = document.querySelectorAll('.myCssSelector');

  // convert it to array:
  var myArray = [];for(var i = nl.length; i--; myArray.unshift(nl[i]));

  // now find the visible (= with offsetWidth more than 0) item:
  for (i =0; i < myArray.length; i++){
    var curEl = myArray[i];
    if (curEl.offsetWidth !== 0){
      return curEl.getAttribute("class");
    }
  }
0
not_specified