web-dev-qa-db-fra.com

Comment savoir si un élément DOM est visible dans la fenêtre actuelle?

Existe-t-il un moyen efficace de savoir si un élément DOM (dans un document HTML) est actuellement visible (apparaît dans le viewport )?

(La question concerne Firefox)

807
benzaita

Mise à jour: L'heure avance, de même que nos navigateurs. Cette technique n'est plus recommandée et vous devez utiliser la solution de @ Dan ci-dessous ( https://stackoverflow.com/a/7557433/5628 ) si vous n'avez pas besoin de prendre en charge IE <7.

Solution originale (maintenant obsolète):

Ceci vérifiera si l'élément est entièrement visible dans la fenêtre courante:

function elementInViewport(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top >= window.pageYOffset &&
    left >= window.pageXOffset &&
    (top + height) <= (window.pageYOffset + window.innerHeight) &&
    (left + width) <= (window.pageXOffset + window.innerWidth)
  );
}

Vous pouvez modifier cela simplement pour déterminer si une partie de l'élément est visible dans la fenêtre:

function elementInViewport2(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top < (window.pageYOffset + window.innerHeight) &&
    left < (window.pageXOffset + window.innerWidth) &&
    (top + height) > window.pageYOffset &&
    (left + width) > window.pageXOffset
  );
}
313
Prestaul

Maintenant la plupart des navigateurs support getBoundingClientRect , qui est devenu la meilleure pratique. Utiliser une ancienne réponse est très lent , pas précis et a plusieurs bogues .

La solution choisie comme correcte est presque jamais précise . Vous pouvez en lire plus sur ses bugs.


Cette solution a été testée sur IE7 +, iOS5 + Safari, Android2 +, Blackberry, Opera Mobile et IE Mobile. dix.


function isElementInViewport (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

Comment utiliser:

Vous pouvez être sûr que la fonction donnée ci-dessus renvoie la réponse correcte au moment où elle est appelée, mais qu'en est-il du suivi de la visibilité de l'élément en tant qu'événement?

Placez le code suivant au bas de votre balise <body>:

function onVisibilityChange(el, callback) {
    var old_visible;
    return function () {
        var visible = isElementInViewport(el);
        if (visible != old_visible) {
            old_visible = visible;
            if (typeof callback == 'function') {
                callback();
            }
        }
    }
}

var handler = onVisibilityChange(el, function() {
    /* your code go here */
});


//jQuery
$(window).on('DOMContentLoaded load resize scroll', handler); 

/* //non-jQuery
if (window.addEventListener) {
    addEventListener('DOMContentLoaded', handler, false); 
    addEventListener('load', handler, false); 
    addEventListener('scroll', handler, false); 
    addEventListener('resize', handler, false); 
} else if (window.attachEvent)  {
    attachEvent('onDOMContentLoaded', handler); // IE9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

Si vous apportez des modifications DOM, elles peuvent bien sûr modifier la visibilité de votre élément.

Directives et pièges courants:

Peut-être devez-vous suivre le zoom de la page/le pincement de l'appareil mobile? jQuery devrait gérer zoom/pincer navigateur croisé, sinon premier ou deuxième lien devrait vous aider. 

Si vous modifiez DOM, cela peut affecter la visibilité de l'élément. Vous devriez en prendre le contrôle et appeler handler() manuellement. Malheureusement, nous n'avons pas d'événement onrepaint sur plusieurs navigateurs. En revanche, cela nous permet d’optimiser et de revérifier uniquement les modifications du DOM pouvant modifier la visibilité de l’élément.

Jamais utilisez-le dans jQuery $ (document) .ready () uniquement, car il n'y a pas de garantie CSS a été appliqué en ce moment. Votre code peut fonctionner localement avec votre CSS sur le disque dur, mais une fois placé sur un serveur distant, il échouera.

Après le déclenchement de DOMContentLoaded, les styles sont appliqués , mais les images ne sont pas encore chargées . Donc, nous devrions ajouter window.onload écouteur d'événement.

Nous ne pouvons pas encore attraper un événement zoom/pincement.

Le dernier recours pourrait être le code suivant:

/* TODO: this looks like a very bad code */
setInterval(handler, 600); 

Vous pouvez utiliser la fonctionnalité géniale pageVisibiliy HTML5 API si vous le souhaitez si l'onglet contenant votre page Web est actif et visible.

TODO: cette méthode ne gère pas deux situations:

1258
Dan

Mettre à jour

Dans les navigateurs modernes, il peut être intéressant de consulter Intersection Observer API , qui offre les avantages suivants:

  • Meilleure performance que d'écouter les événements de défilement
  • Fonctionne dans les iframes interdomaines
  • Peut dire si un élément obstrue/croise un autre

Intersection Observer est en passe de devenir un standard à part entière et est déjà pris en charge par Chrome 51+, Edge 15+ et Firefox 55+ et est en cours de développement pour Safari. Il y a aussi un polyfill disponible.


Réponse précédente

Il y a quelques problèmes avec le réponse fournie par Dan qui pourraient en faire une approche inappropriée dans certaines situations. Certaines de ces questions sont soulignées dans sa réponse vers le bas, que son code donnera de faux positifs pour des éléments qui sont:

  • Caché par un autre élément devant celui testé
  • En dehors de la zone visible d'un élément parent ou ancêtre
  • Un élément ou ses enfants cachés à l'aide de la propriété CSS clip

Ces limitations sont démontrées dans les résultats suivants d'un test simple :

Failed test, using isElementInViewport

La solution: isElementVisible()

Voici une solution à ces problèmes, avec le résultat du test ci-dessous et une explication de certaines parties du code.

function isElementVisible(el) {
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || doc.documentElement.clientWidth,
        vHeight  = window.innerHeight || doc.documentElement.clientHeight,
        efp      = function (x, y) { return document.elementFromPoint(x, y) };     

    // Return false if it's not in the viewport
    if (rect.right < 0 || rect.bottom < 0 
            || rect.left > vWidth || rect.top > vHeight)
        return false;

    // Return true if any of its four corners are visible
    return (
          el.contains(efp(rect.left,  rect.top))
      ||  el.contains(efp(rect.right, rect.top))
      ||  el.contains(efp(rect.right, rect.bottom))
      ||  el.contains(efp(rect.left,  rect.bottom))
    );
}

Réussite du test: http://jsfiddle.net/AndyE/cAY8c/

Et le résultat:

Passed test, using isElementVisible

Notes complémentaires

Cette méthode n’est cependant pas sans limites. Par exemple, un élément testé avec un indice z inférieur à un autre élément situé au même emplacement serait identifié comme étant masqué, même si l'élément situé au premier plan n'en masque aucune partie. Pourtant, cette méthode a des utilisations dans certains cas que la solution de Dan ne couvre pas.

element.getBoundingClientRect() et document.elementFromPoint() font tous deux partie de la spécification de travail CSSOM et sont pris en charge par au moins IE 6 et versions ultérieures et most . navigateurs de bureau depuis longtemps (mais pas parfaitement). Voir Quirksmode sur ces fonctions pour plus d'informations.

contains() est utilisé pour voir si l'élément renvoyé par document.elementFromPoint() est un nœud enfant de l'élément que nous testons pour la visibilité. Il renvoie également true si l'élément renvoyé est le même élément. Cela rend le contrôle plus robuste. Il est supporté par tous les principaux navigateurs, Firefox 9.0 étant le dernier à l’ajouter. Pour les anciennes versions de Firefox, consultez l’historique de cette réponse.

Si vous voulez tester plus de points autour de l'élément pour la visibilité, c'est-à-dire pour vous assurer que l'élément n'est pas couvert par plus de, disons, 50%, il ne vous en faudrait pas beaucoup pour ajuster la dernière partie de la réponse. Cependant, sachez que ce serait probablement très lent si vous vérifiiez chaque pixel pour vous assurer qu'il était visible à 100%.

151
Andy E

J'ai essayé Réponse de Dancependant l'algèbre utilisée pour déterminer les limites signifie que l'élément doit être à la fois ≤ la taille de la fenêtre d'affichage et complètement à l'intérieur de la fenêtre d'affichage pour obtenir true, conduisant facilement à de faux négatifs. Si vous voulez déterminer si un élément est dans la fenêtre d'affichage, la réponse de ryanve est proche, mais l'élément testé doit chevaucher la fenêtre d'affichage, essayez ceci:

function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();

    return rect.bottom > 0 &&
        rect.right > 0 &&
        rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ &&
        rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;
}
51
Walf

En tant que service public:
La réponse de Dan avec les calculs corrects (l'élément peut être> fenêtre, en particulier sur les écrans de téléphone mobile), les tests jQuery corrects, ainsi que l'ajout de isElementPartiallyInViewport:

A propos, la difference entre window.innerWidth et document.documentElement.clientWidth est que clientWidth/clientHeight n'inclut pas la barre de défilement, contrairement à window.innerWidth/Height.

function isElementPartiallyInViewport(el)
{
    //special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

    var rect = el.getBoundingClientRect();
    // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
    var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
    var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

    return (vertInView && horInView);
}


// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el) 
{
    //special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

    var rect = el.getBoundingClientRect();
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    return (
           (rect.left >= 0)
        && (rect.top >= 0)
        && ((rect.left + rect.width) <= windowWidth)
        && ((rect.top + rect.height) <= windowHeight)
    );

}


function fnIsVis(ele)
{
    var inVpFull = isElementInViewport(ele);
    var inVpPartial = isElementPartiallyInViewport(ele);
    console.clear();
    console.log("Fully in viewport: " + inVpFull);
    console.log("Partially in viewport: " + inVpPartial);
}

Cas de test

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Test</title>
    <!--
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>    
    <script src="scrollMonitor.js"></script>
    -->

    <script type="text/javascript">

        function isElementPartiallyInViewport(el)
        {
            //special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

            var rect = el.getBoundingClientRect();
            // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
            var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
            var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

            return (vertInView && horInView);
        }


        // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
        function isElementInViewport (el) 
        {
            //special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];


            var rect = el.getBoundingClientRect();
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            return (
                   (rect.left >= 0)
                && (rect.top >= 0)
                && ((rect.left + rect.width) <= windowWidth)
                && ((rect.top + rect.height) <= windowHeight)
            );

        }


        function fnIsVis(ele)
        {
            var inVpFull = isElementInViewport(ele);
            var inVpPartial = isElementPartiallyInViewport(ele);
            console.clear();
            console.log("Fully in viewport: " + inVpFull);
            console.log("Partially in viewport: " + inVpPartial);
        }


        // var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft,
        // var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

    </script>

</head>
<body>

    <div style="display: block; width: 2000px; height: 10000px; background-color: green;">

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <div style="background-color: crimson; display: inline-block; width: 800px; height: 500px;" ></div>
        <div id="myele" onclick="fnIsVis(this);" style="display: inline-block; width: 100px; height: 100px; background-color: hotpink;">
        t
        </div>

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

    </div>

    <!--
    <script type="text/javascript">

        var element = document.getElementById("myele");
        var watcher = scrollMonitor.create( element );

        watcher.lock();

        watcher.stateChange(function() {
            console.log("state changed");
            // $(element).toggleClass('fixed', this.isAboveViewport)
        });

    </script>
    -->
</body>
</html>
30
Stefan Steiger

Il y a un plugin jQuery appelé inview qui fait le travail

25

Voir la source de verge , qui utilise getBoundingClientRect C'est comme:

function inViewport (el) {

    var r, html;
    if ( !el || 1 !== el.nodeType ) { return false; }
    html = document.documentElement;
    r = el.getBoundingClientRect();

    return ( !!r 
      && r.bottom >= 0 
      && r.right >= 0 
      && r.top <= html.clientHeight 
      && r.left <= html.clientWidth 
    );

}

Renvoie true si une partie de l'élément est dans la fenêtre.

24
ryanve

ma version plus courte et plus rapide.

function isElementOutViewport(el){
    var rect = el.getBoundingClientRect();
    return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}

ajoutez jsFiddle au besoin https://jsfiddle.net/on1g619L/1/

22
Eric Chen

J'ai trouvé troublant qu'il n'y ait pas de version jQuery de la fonctionnalité disponible. Quand je suis tombé sur la solution de Dan j’ai saisi l’opportunité de fournir quelque chose aux personnes qui aiment programmer dans le style jQuery OO Assurez-vous de faire défiler et de laisser un vote positif sur le code de Dan. C'est gentil et vif et fonctionne comme un charme pour moi.

bada bing bada boom

$.fn.inView = function(){
    if(!this.length) return false;
    var rect = this.get(0).getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );

};

//additional examples for other use cases
//true false whether an array of elements are all in view
$.fn.allInView = function(){
    var all = [];
    this.forEach(function(){
        all.Push( $(this).inView() );
    });
    return all.indexOf(false) === -1;
};

//only the class elements in view
$('.some-class').filter(function(){
    return $(this).inView();
});

//only the class elements not in view
$('.some-class').filter(function(){
    return !$(this).inView();
});

usage

$(window).on('scroll',function(){ 

    if( $('footer').inView() ) {
        // do cool stuff
    }

});
19
r3wt

Je trouve que la réponse acceptée ici est trop compliquée pour la plupart des cas d'utilisation. Ce code fait bien son travail (avec JQuery) et différencie les éléments entièrement visibles des éléments partiellement visibles. 

var element         = $("#element");
var topOfElement    = element.offset().top;
var bottomOfElement = element.offset().top + element.outerHeight(true);
var $window         = $(window);

$window.bind('scroll', function() {

    var scrollTopPosition   = $window.scrollTop()+$window.height();
    var windowScrollTop     = $window.scrollTop()

    if( windowScrollTop > topOfElement && windowScrollTop < bottomOfElement) {
       // Element is partially visible (above viewable area)
       console.log("Element is partially visible (above viewable area)");

    }else if( windowScrollTop > bottomOfElement && windowScrollTop > topOfElement ) {
        // Element is hidden (above viewable area)
       console.log("Element is hidden (above viewable area)");

    }else if( scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement ) {
        // Element is hidden (below viewable area)
        console.log("Element is hidden (below viewable area)");

    }else if( scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement ) {
        // Element is partially visible (below viewable area)
        console.log("Element is partially visible (below viewable area)");

    }else{
        // Element is completely visible
        console.log("Element is completely visible");
    }
});
8
Adam Rehal

Toutes les réponses que j'ai rencontrées ici ne font que vérifier si l'élément est positionné dans la fenêtre courante . Mais cela ne signifie pas qu'il est visible .
Que se passe-t-il si l'élément donné est dans une div avec un contenu débordant et s'il est défilé?

Pour résoudre ce problème, vous devez vérifier si l'élément est contenu par tous les parents.
Ma solution fait exactement cela:

Cela vous permet également de spécifier la quantité d'éléments visibles.

Element.prototype.isVisible = function(percentX, percentY){
    var tolerance = 0.01;   //needed because the rects returned by getBoundingClientRect provide the position up to 10 decimals
    if(percentX == null){
        percentX = 100;
    }
    if(percentY == null){
        percentY = 100;
    }

    var elementRect = this.getBoundingClientRect();
    var parentRects = [];
    var element = this;

    while(element.parentElement != null){
        parentRects.Push(element.parentElement.getBoundingClientRect());
        element = element.parentElement;
    }

    var visibleInAllParents = parentRects.every(function(parentRect){
        var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
        var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
        var visiblePercentageX = visiblePixelX / elementRect.width * 100;
        var visiblePercentageY = visiblePixelY / elementRect.height * 100;
        return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY;
    });
    return visibleInAllParents;
};

Cette solution a ignoré le fait que des éléments peuvent ne pas être visibles en raison d'autres faits, tels que opacity: 0

J'ai testé cette solution sous Chrome et Internet Explorer 11.

5
Domysee

La nouvelle API Intersection Observer répond très directement à cette question.

Cette solution nécessitera un polyfill car Safari, Opera et IE ne le supportent pas encore. (le polyfill est inclus dans la solution).

Dans cette solution, il y a une boîte hors de vue qui est la cible (observée). Lorsqu'il apparaît, le bouton en haut de l'en-tête est masqué. Il est montré une fois que la boîte a quitté la vue.

const buttonToHide = document.querySelector('button');

const hideWhenBoxInView = new IntersectionObserver((entries) => {
  if (entries[0].intersectionRatio <= 0) { // If not in view
    buttonToHide.style.display = "inherit";
  } else {
    buttonToHide.style.display = "none";
  }
});

hideWhenBoxInView.observe(document.getElementById('box'));
header {
  position: fixed;
  top: 0;
  width: 100vw;
  height: 30px;
  background-color: lightgreen;
}

.wrapper {
  position: relative;
  margin-top: 600px;
}

#box {
  position: relative;
  left: 175px;
  width: 150px;
  height: 135px;
  background-color: lightblue;
  border: 2px solid;
}
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<header>
  <button>NAVIGATION BUTTON TO HIDE</button>
</header>
  <div class="wrapper">
    <div id="box">
    </div>
  </div>
4
Randy Casburn

Je pense que c'est une façon plus fonctionnelle de le faire .. La réponse de Dan ne fonctionne pas dans un contexte récursif.

Cette fonction résout le problème lorsque votre élément se trouve à l'intérieur d'autres divisions défilables en testant de manière récursive tous les niveaux supérieurs à la balise HTML et s'arrête au premier faux.

/**
 * fullVisible=true only returns true if the all object rect is visible
 */
function isReallyVisible(el, fullVisible) {
    if ( el.tagName == "HTML" )
            return true;
    var parentRect=el.parentNode.getBoundingClientRect();
    var rect = arguments[2] || el.getBoundingClientRect();
    return (
            ( fullVisible ? rect.top    >= parentRect.top    : rect.bottom > parentRect.top ) &&
            ( fullVisible ? rect.left   >= parentRect.left   : rect.right  > parentRect.left ) &&
            ( fullVisible ? rect.bottom <= parentRect.bottom : rect.top    < parentRect.bottom ) &&
            ( fullVisible ? rect.right  <= parentRect.right  : rect.left   < parentRect.right ) &&
            isReallyVisible(el.parentNode, fullVisible, rect)
    );
};
3
ton

Sur la base de la solution de @ dan ci-dessus ( https://stackoverflow.com/a/7557433/5628 ), je me suis lancé dans le nettoyage de la mise en œuvre afin de pouvoir l'utiliser plusieurs fois sur la même page:

$(function() {

  $(window).on('load resize scroll', function() {
    addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon');
    addClassToElementInViewport($('.another-thing'), 'animate-thing');
    // ???? repeat as needed ...
  });

  function addClassToElementInViewport(element, newClass) {
    if (inViewport(element)) {
      element.addClass(newClass);
    }
  }

  function inViewport(element) {
    if (typeof jQuery === "function" && element instanceof jQuery) {
      element = element[0];
    }
    var elementBounds = element.getBoundingClientRect();
    return (
      elementBounds.top >= 0 &&
      elementBounds.left >= 0 &&
      elementBounds.bottom <= $(window).height() &&
      elementBounds.right <= $(window).width()
    );
  }

});

La façon dont je l'utilise, c'est que lorsque l'élément défile dans l'affichage, j'ajoute une classe qui déclenche une animation d'image clé CSS. C'est assez simple et fonctionne particulièrement bien lorsque vous avez environ 10 choses à animer de manière conditionnelle sur une page.

J'espère que ça aide!

2
Pirijan

Cela dépend de ce que vous entendez par visible. Si vous voulez dire si elle est actuellement affichée sur la page, compte tenu de la position du défilement, vous pouvez la calculer en fonction du décalage y des éléments et de la position de défilement actuelle.

2
roryf

Aussi simple que cela puisse arriver à l'OMI:

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  return Math.abs(coords.top) <= coords.height;
}
1
JuanM.

La solution facile et petite qui a fonctionné pour moi.

Exemple Vous voulez voir si l'élément est visible dans l'élément parent qui a le défilement de débordement.

$(window).on('scroll', function () {  

     var container = $('#sidebar');
     var containerHeight = container.height();
     var scrollPosition = $('#row1').offset().top - container.offset().top;

     if (containerHeight < scrollPosition) {
         console.log('not visible');
     } else {
         console.log('visible');
     }
})
1
Stevan Tosic

Voici ma solution, cela fonctionnera si un élément est caché dans un conteneur pouvant défiler. 

Voici une démo (essayez de redimensionner la fenêtre)

var visibleY = function(el){
    var top = el.getBoundingClientRect().top, rect, el = el.parentNode;
    do {
        rect = el.getBoundingClientRect();
        if (top <= rect.bottom === false)
            return false;
        el = el.parentNode;
    } while (el != document.body);
    // Check its within the document viewport
    return top <= document.documentElement.clientHeight;
};

Je devais seulement vérifier si elle était visible sur l'axe Y (pour une fonction de défilement ajax, charger plus d'enregistrements). 

1
Ally

J'avais la même question et l'ai trouvée en utilisant getBoundingClientRect (). Ce code est complètement 'générique' et ne doit être écrit qu'une seule fois pour qu'il fonctionne (vous n'avez pas à l'écrire pour chaque élément que vous voulez savoir dans la fenêtre d'affichage). Ce code vérifie uniquement s'il est vertical dans la fenêtre pas horizontalement . Dans ce cas, la variable (tableau) 'éléments' contient tous les éléments que vous vérifiez que la liste est verticalement dans la fenêtre. Vous devez donc saisir tous les éléments souhaités n'importe où et les stocker à cet emplacement. Le 'for loop', parcourt chaque élément et vérifie s'il se trouve verticalement dans la fenêtre. Ce code s'exécute à chaque fois l'utilisateur fait défiler! Si getBoudingClientRect (). Top correspond à moins de 3/4 de la fenêtre (l'élément représente un quart dans la fenêtre), il est enregistré comme "dans la fenêtre". Comme le code est générique, vous voudrez savoir quel élément est dans la fenêtre. Pour le savoir, vous pouvez le déterminer par attribut personnalisé, nom de noeud, identifiant, nom de classe, etc. Voici mon code (Indiquez-moi si cela ne fonctionne pas, il a été testé dans IE 11, FireFox 40.0.3, Chrome Version 45.0.2454.85 m, Opera 31.0.1889.174 et Edge avec Windows 10, [ pas encore Safari]) ...

//scrolling handlers...
window.onscroll = function(){
  var elements = document.getElementById('whatever').getElementsByClassName('whatever');
  for(var i = 0; i != elements.length; i++)
  {
   if(elements[i].getBoundingClientRect().top <= window.innerHeight*0.75 && elements[i].getBoundingClientRect().top > 0)
   {
      console.log(elements[i].nodeName + ' ' + elements[i].className + ' ' + elements[i].id + ' is in the viewport; proceed with whatever code you want to do here.');
   }
};

J'espère que cela aide quelqu'un :-)

0
www139

Une meilleure solution:

function getViewportSize(w) {
    var w = w || window;
    if(w.innerWidth != null) return {w:w.innerWidth, h:w.innerHeight};
    var d = w.document;
    if (document.compatMode == "CSS1Compat") {
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    }
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}
function isViewportVisible(e) {
    var box = e.getBoundingClientRect();
    var height = box.height || (box.bottom - box.top);
    var width = box.width || (box.right - box.left);
    var viewport = getViewportSize();
    if(!height || !width) return false;
    if(box.top > viewport.h || box.bottom < 0) return false;
    if(box.right < 0 || box.left > viewport.w) return false;
    return true;    
}
0
rainyjune

Vérifie si l'élément est au moins partiellement en vue (dimension verticale):

function inView(element) {
                var box = element.getBoundingClientRect();
                return inViewBox(box);
}

function inViewBox(box) {
                return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true;
}


function getWindowSize() { 
        return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight} 
}
0
Lumic

La solution la plus simple en tant que compatibilité de Element.getBoundingClientRect () est devenue parfaite:

function inView(el) {
    let box = el.getBoundingClientRect();
    return box.top < window.innerHeight && box.bottom >= 0;
}
0
MiXT4PE

Voici une fonction qui indique si un élément est visible dans la fenêtre actuelle d’un élément parent :

function inParentViewport(el, pa) {
    if (typeof jQuery === "function"){
        if (el instanceof jQuery)
            el = el[0];
        if (pa instanceof jQuery)
            pa = pa[0];
    }

    var e = el.getBoundingClientRect();
    var p = pa.getBoundingClientRect();

    return (
        e.bottom >= p.top &&
        e.right >= p.left &&
        e.top <= p.bottom &&
        e.left <= p.right
    );
}
0
ssten