Question

Existe-t-il un moyen efficace de déterminer si un élément DOM (dans un document HTML) est actuellement visible (apparaît dans la fenêtre de visualisation )?

(La question concerne Firefox)

Était-ce utile?

La solution

Mettre à 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 d'origine (maintenant obsolète):

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

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

Autres conseils

Maintenant, la la plupart des navigateurs prend en charge getBoundingClientRect , qui est devenue la meilleure pratique. Utiliser une ancienne réponse est très lent , inexact et a plusieurs bogues .

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

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

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 <body> balise:

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 au DOM, elles peuvent bien sûr modifier la visibilité de votre élément.

Consignes et pièges courants:

Peut-être devez-vous suivre le pincement de la page / du périphérique mobile? jQuery devrait gérer le zoom / pinch , sinon en premier ou second devrait vous aider.

Si vous modifiez DOM , cela peut affecter la visibilité de l'élément. Vous devez en prendre le contrôle et appeler handler() manuellement. Malheureusement, nous n'avons pas d'événement multi-navigateurs onrepaint. 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 ne l'utilisez jamais dans jQuery $ (document) .ready () seulement, car la garantie CSS n'a pas été appliquée à ce moment . Votre code peut fonctionner localement avec votre CSS sur le disque dur, mais une fois placé sur un serveur distant, il échouera.

Une fois DOMContentLoaded activé, les styles sont appliqués , mais les images ne sont pas encore chargées . Nous devrions donc ajouter window.onload un écouteur d’événements.

Nous ne pouvons pas encore capturer l'é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 de l'API HTML5 si vous vous souciez de l'onglet contenant votre page Web est actif et visible.

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

Mettre à jour

Dans les navigateurs modernes, vous pouvez consulter la API Intersection Observer. qui offre les avantages suivants:

  • Meilleures performances que l'écoute d'é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 une norme à 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. Un polyfill est également disponible.

Réponse précédente

Il existe certains problèmes avec la réponse fournie par Dan qui pourraient en faire une approche inappropriée dans certaines situations. Certains de ces problèmes sont soulignés 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 masqué à l'aide de la propriété CSS clip

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

Échec du test, utilisation de 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:

Test réussi, utilisation de 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. Néanmoins, 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 CSSOM Working Draft et sont pris en charge dans au moins IE 6 et versions ultérieures et la plupart des navigateurs de bureau depuis longtemps (bien que pas tout à fait) . Pour plus d'informations, voir Quirksmode sur ces fonctions .

contains() permet de voir si l'élément renvoyé par <=> 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 souhaitez tester la visibilité de davantage d'éléments autour de l'élément & # 8213, c'est-à-dire pour vous assurer que l'élément n'est pas couvert par plus de, disons, 50% & # 8213; prenez 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%.

J'ai essayé la réponse de Dan cependant, l'algèbre utilisée pour déterminer les limites signifie que l'élément doit être à la fois & # 8804; la taille de la fenêtre et complètement à l'intérieur de la fenêtre pour obtenir true, conduisant facilement à de faux négatifs. Si vous souhaitez déterminer si un élément figure 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() */;
}

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

Au fait, la différence entre window.innerWidth et document.documentElement.clientWidth correspond à 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>

Il existe un plug-in jQuery appelé aperçu qui fait le travail

Voir la source de version , 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 d'affichage.

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/

J'ai trouvé troublant l'absence d'une jQuery version centrée de la fonctionnalité disponible. Lorsque j’ai découvert la solution de Dan , j’ai eu l’occasion de proposer quelque chose aux personnes qui aiment programmer dans le style <=> OO . Assurez-vous de faire défiler et de laisser un vote positif sur le code de Dan. C'est sympa et accrocheur et fonctionne comme un charme pour moi.

boom de bada bing bada

$.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();
});

utilisation

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

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

});

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

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 d'affichage actuelle . Mais cela ne veut pas dire qu'il est visible .
Que se passe-t-il si l'élément donné se trouve à l'intérieur d'une div avec un contenu débordant et s'il est masqué?

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:

Il vous permet également de spécifier combien d'éléments doivent être 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.

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

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 s'affiche 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>

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 div à défilement 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)
    );
};

D'après la solution de @dan ci-dessus ( https://stackoverflow.com/a/7557433/5628 ), je a essayé de nettoyer la mise en œuvre de sorte que son utilisation à plusieurs reprises sur la même page est plus facile:

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

});

Selon mon utilisation, 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 comme plus de 10 choses à animer conditionnellement sur une page.

J'espère que ça aide!

Voici ma solution: cela fonctionnera si un élément est caché dans un conteneur à défilement.

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

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 n'avais besoin que de vérifier si elle est visible sur l'axe Y (pour une fonction de défilement ajax, charger plus d'enregistrements).

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

Exemple Vous souhaitez savoir si l'élément est visible dans un élément parent présentant un défilement en 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');
     }
})

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

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

J'avais la même question et j'ai compris 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 se trouve verticalement dans la fenêtre et non horizontalement . Dans ce cas, la variable (array) 'elements' contient tous les éléments que vous vérifiez que sont en train de se vérifier verticalement dans la fenêtre, saisissez donc les éléments souhaités n'importe où et stockez-les là. La boucle for, parcourt chaque élément et vérifie s'il se trouve verticalement dans la fenêtre. Ce code est exécuté à chaque fois que l'utilisateur fait défiler! Si getBoudingClientRect (). Top correspond à moins de 3/4 de la fenêtre (l'élément représente un quart de 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 (Dites-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: -)

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

Aussi simple que possible, IMO:

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

J'utilise cette fonction (elle vérifie uniquement si le y est à l'écran puisque le x n'est pas nécessaire)

function elementInViewport(el) {
    var elinfo = {
        "top":el.offsetTop,
        "height":el.offsetHeight,
    };

    if (elinfo.top + elinfo.height < window.pageYOffset || elinfo.top > window.pageYOffset + window.innerHeight) {
        return false;
    } else {
        return true;
    }

}

Pour un défi similaire, j’ai vraiment apprécié cet extrait qui expose un polyfill pour scrollIntoViewIfNeeded () .

Tout le Kung Fu nécessaire pour répondre se trouve dans ce bloc:

var parent = this.parentNode,
    parentComputedStyle = window.getComputedStyle(parent, null),
    parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
    parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
    overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
    overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
    overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
    overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
    alignWithTop = overTop && !overBottom;

this fait référence à l'élément que vous voulez savoir s'il s'agit de overTop ou de overBottom - vous devriez obtenir la dérive ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top