web-dev-qa-db-fra.com

Comment vérifier si un élément est vraiment visible avec JavaScript?

En JavaScript, comment vérifier si un élément est réellement visible?

Je ne parle pas seulement de vérifier les attributs visibility et display. Je veux dire, vérifier que l'élément n'est pas

  • visibility: hidden ou display: none
  • sous un autre élément
  • fait défiler le bord de l'écran

Pour des raisons techniques, je ne peux inclure aucun script. Je peux cependant utiliser Prototype tel qu'il est déjà sur la page.

118
Macha

Pour le point 2.

Je vois que personne n’a suggéré d’utiliser document.elementFromPoint(x,y), c’est le moyen le plus rapide de tester si un élément est imbriqué ou caché par un autre. Vous pouvez transmettre les décalages de l'élément ciblé à la fonction.

Voici la page de test PPK sur elementFromPoint .

94
Christophe Eblé

Je ne sais pas dans quelle mesure cela est pris en charge par les navigateurs plus anciens ou moins modernes, mais j'utilise quelque chose comme ceci (sans besoin de librairies):

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

Il vérifie que l'élément a une surface> 0, puis vérifie si une partie de l'élément se trouve dans la fenêtre et qu'elle n'est pas masquée "sous" un autre élément (en fait, je ne vérifie qu'un seul point au centre de l'élément de sorte que ce n’est pas sûr à 100% - mais vous pouvez simplement modifier le script pour parcourir tous les points de l’élément, si vous en avez vraiment besoin ...).

Mise à jour

Fonction on_top modifiée vérifiant chaque pixel:

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

Je ne sais pas pour la performance :)

38
Tobias

Comme l'a souligné jkl, vérifier la visibilité ou l'affichage de l'élément ne suffit pas. Vous devez vérifier ses ancêtres. Sélénium fait cela lorsqu'il vérifie la visibilité d'un élément.

Découvrez la méthode Selenium.prototype.isVisible dans le fichier Selenium-api.js.

http://svn.openqa.org/svn/Selenium-on-Rails/Selenium-on-Rails/Selenium-core/scripts/Selenium-api.js

8
Jason Tillery

C'est ce que j'ai jusqu'ici. Il couvre à la fois 1 et 3. Je suis toutefois toujours aux prises avec 2 puisque je ne suis pas familier avec Prototype (je suis plus du type 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;
}
4
Pim Jager

Question interessante.

Ce serait mon approche.

  1. Vérifiez d'abord que element.style.visibility! == 'hidden' && element.style.display! == 'none'
  2. Puis testez avec document.elementFromPoint (element.offsetLeft, element.offsetTop) si l'élément renvoyé est l'élément que je m'attendais, il est difficile de détecter si un élément en chevauche complètement un autre.
  3. Enfin, testez si offsetTop et offsetLeft sont situés dans la fenêtre en prenant en compte les décalages de défilement.

J'espère que ça aide.

4
Christophe Eblé

Le prototype Element library est l'une des bibliothèques de requêtes les plus puissantes en termes de méthodes. Je vous recommande de vérifier l'API.

Quelques astuces:

  1. Vérifier la visibilité peut être un problème, mais vous pouvez utiliser les méthodes Element.getStyle() et Element.visible() combinées dans une fonction personnalisée. Avec getStyle(), vous pouvez vérifier le style calculé.

  2. Je ne sais pas exactement ce que vous entendez par "dessous" :) Si vous entendez par là a un ancêtre spécifique, par exemple un div wrapper, vous pouvez utiliser Element.up(cssRule):

    var child = $("myparagraph");
    if(!child.up("mywrapper")){
      // I lost my mom!
    }
    else {
      // I found my mom!
    }
    

    Si vous souhaitez vérifier les frères et soeurs de l'élément enfant, vous pouvez également le faire:

    var child = $("myparagraph");
    if(!child.previous("mywrapper")){
      // I lost my bro!
    } 
    else {
      // I found my bro!
    }
    
  3. Encore une fois, Element lib peut vous aider si je comprends bien ce que vous voulez dire :) Vous pouvez vérifier le réel dimensions de la fenêtre et le décalage de votre élément afin de pouvoir calculer si votre élément est "hors écran".

Bonne chance!

J'ai collé un scénario de test pour prototypejs à l'adresse http://Gist.github.com/117125 . Il semble que dans votre cas, nous ne pouvons tout simplement pas faire confiance à getStyle() du tout. Pour optimiser la fiabilité de la fonction isMyElementReallyVisible, vous devez combiner les éléments suivants:

  • Vérification du style calculé (le dojo a un Nice implémentation que vous pouvez emprunter)
  • Vérification du viewportoffset (méthode native du prototype)
  • Vérification de l'index z pour le problème "sous" (sous Internet Explorer, cela peut être un problème)
3
yaanno

Une façon de le faire est:

isVisible(Elm) {
    while(Elm.tagName != 'BODY') {
        if(!$(Elm).visible()) return false;
        Elm = Elm.parentNode;
    }
    return true;
}

Crédits: https://github.com/atetlaw/Really-Easy-Field-Validation/blob/master/validation.js#L178

2
umpirsky

Je détesterais vous rediriger vers jQuery (comme souvent), mais cette discussion quand les éléments sont vraiment visibles est très perspicace.

Et depuis jQuery 1.3.2 c'est plus un problème .

1
Ólafur Waage

Je ne pense pas que la vérification de la visibilité et des propriétés d'affichage de l'élément soit suffisante pour l'exigence n ° 1, même si vous utilisez currentStyle/getComputedStyle. Vous devez également vérifier les ancêtres de l'élément. Si un ancêtre est masqué, l'élément l'est également.

1
user176652

Attrapez les événements de glisser-déposer et d'affichage (onmouseup, onresize, onscroll).

Lorsque le glissement prend fin, faites une comparaison de la limite de l'élément glissé avec tous les "éléments d'intérêt" (c'est-à-dire, les éléments avec la classe "dont_hide" ou un tableau d'identifiants). Faites la même chose avec window.onscroll et window.onresize. Marquez tous les éléments cachés avec un attribut spécial ou un nom de classe ou effectuez simplement l'action que vous voulez à ce moment-là.

Les tests cachés sont assez faciles. Pour "totalement caché", vous voulez savoir si TOUS les coins sont situés à l'intérieur des limites de l'élément déplacé ou à l'extérieur de la fenêtre. Pour partiellement masqué, vous recherchez un seul coin correspondant au même test.

1
SpliFF

Vérifiez la propriété offsetHeight des éléments. S'il est supérieur à 0, il est visible. Remarque: cette approche ne couvre pas une situation où visibilité: le style caché est défini. Mais ce style est quand même bizarre.

0
Sergey Ilinsky

Essayez element.getBoundingClientRect(). Il retournera un objet avec des propriétés

  • bas
  • haut
  • droite
  • la gauche
  • largeur - dépend du navigateur
  • hauteur - dépend du navigateur

Vérifiez que la largeur et la hauteur de l'élément BoundingClientRect de l'élément ne sont pas nulles, ce qui correspond à la valeur des éléments masqués ou non visibles. Si les valeurs sont supérieures à zéro, l'élément doit être visible dans le corps. Puis vérifiez si la propriété bottom est inférieure à screen.height _ ce qui impliquerait que l’élément se trouve dans la fenêtre de visualisation. (Techniquement, vous devez également prendre en compte le haut de la fenêtre du navigateur, barre de recherche, boutons, etc.).

0
j_v_wow_d
/**
 * Checks display and visibility of elements and it's parents
 * @param  DomElement  el
 * @param  boolean isDeep Watch parents? Default is true
 * @return {Boolean}
 *
 * @author Oleksandr Knyga <[email protected]>
 */
function isVisible(el, isDeep) {
    var elIsVisible = true;

    if("undefined" === typeof isDeep) {
        isDeep = true;
    }

    elIsVisible = elIsVisible && el.offsetWidth > 0 && el.offsetHeight > 0;

    if(isDeep && elIsVisible) {

        while('BODY' != el.tagName && elIsVisible) {
            elIsVisible = elIsVisible && 'hidden' != window.getComputedStyle(el).visibility;
            el = el.parentElement;
        }
    }

    return elIsVisible;
}
0
Oleksandr Knyga

Voici un exemple de script et de scénario de test. Couvre les éléments positionnés, visibilité: masquée, affichage: aucune. N'a pas testé z-index, supposons que cela fonctionne.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title></title>
    <style type="text/css">
    div {
      width: 200px;
      border: 1px solid red;
    }
    p {
      border: 2px solid green;
    }
    .r {
      border: 1px solid #BB3333;
      background: #EE9999;
      position: relative;
      top: -50px;
      height: 2em;
    }
    .of {
      overflow: hidden;
      height: 2em;
      Word-wrap: none; 
    }
    .of p {
      width: 100%;
    }

    .of pre {
      display: inline;
    }
    .iv {
      visibility: hidden;
    }
    .dn {
      display: none;
    }
    </style>
    <script src="http://www.prototypejs.org/assets/2008/9/29/prototype-1.6.0.3.js"></script>
    <script>
      function isVisible(elem){
        if (Element.getStyle(elem, 'visibility') == 'hidden' || Element.getStyle(elem, 'display') == 'none') {
          return false;
        }
        var topx, topy, botx, boty;
        var offset = Element.positionedOffset(elem);
        topx = offset.left;
        topy = offset.top;
        botx = Element.getWidth(elem) + topx;
        boty = Element.getHeight(elem) + topy;
        var v = false;
        for (var x = topx; x <= botx; x++) {
          for(var y = topy; y <= boty; y++) {
            if (document.elementFromPoint(x,y) == elem) {
              // item is visible
              v = true;
              break;
            }
          }
          if (v == true) {
            break;
          }
        }
        return v;
      }

      window.onload=function() {
        var es = Element.descendants('body');
        for (var i = 0; i < es.length; i++ ) {
          if (!isVisible(es[i])) {
            alert(es[i].tagName);
          }
        }
      }
    </script>
  </head>
  <body id='body'>
    <div class="s"><p>This is text</p><p>More text</p></div>
    <div class="r">This is relative</div>
    <div class="of"><p>This is too wide...</p><pre>hidden</pre>
    <div class="iv">This is invisible</div>
    <div class="dn">This is display none</div>
  </body>
</html>

Vous pouvez utiliser les propriétés clientHeight ou clientWidth

function isViewable(element){
  return (element.clientHeight > 0);
}
0
fred