Comment vérifier si l'élément est visible après le défilement?

1170

Je charge des éléments via AJAX. Certains d'entre eux ne sont visibles que si vous faites défiler la page.
Existe-t-il un moyen de savoir si un élément se trouve maintenant dans la partie visible de la page?

yoavf
la source
42
il signifie qu'il veut qu'une méthode sache si un élément donné est affiché dans la fenêtre du navigateur, ou si l'utilisateur doit faire défiler pour le voir.
Romain Linsolas
1
Pour vérifier si un élément est entièrement visible dans un conteneur, ajoutez simplement un paramètre de sélecteur supplémentaire et réutilisez le code elem pour celui-ci. Library.IsElementVisibleInContainer = function (elementSelector, containerSelector) { var containerViewTop = $(containerSelector).offset().top; var containerViewBottom = containerViewTop + $(containerSelector).height();
Lifes
1
Toutes les réponses déclencheront la refusion afin qu'il puisse s'agir d'un goulot d'étranglement, vous criez d'utiliser IntersectionObserver s'il est pris en charge. Il aura de meilleures performances sur les navigateurs modernes,
jcubic

Réponses:

1259

Cela devrait faire l'affaire:

function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}

Fonction utilitaire simple Cela vous permettra d'appeler une fonction utilitaire qui accepte l'élément que vous recherchez et si vous souhaitez que l'élément soit entièrement visible ou partiellement.

function Utils() {

}

Utils.prototype = {
    constructor: Utils,
    isElementInView: function (element, fullyInView) {
        var pageTop = $(window).scrollTop();
        var pageBottom = pageTop + $(window).height();
        var elementTop = $(element).offset().top;
        var elementBottom = elementTop + $(element).height();

        if (fullyInView === true) {
            return ((pageTop < elementTop) && (pageBottom > elementBottom));
        } else {
            return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
        }
    }
};

var Utils = new Utils();

Usage

var isElementInView = Utils.isElementInView($('#flyout-left-container'), false);

if (isElementInView) {
    console.log('in view');
} else {
    console.log('out of view');
}
Scott Dowding
la source
52
Notez que cela ne fonctionne que si le document est l'élément en cours de défilement, c'est-à-dire que vous ne vérifiez pas la visibilité d'un élément à l'intérieur d'un volet intérieur défilant.
Andrew B.12
8
comment ajouter un peu de décalage?
Jürgen Paul
5
Ne fonctionnait que lorsque j'utilisais à la window.innerHeightplace
Christian Schnorr
2
Car elemTopj'ai utilisé $(elem).position().topet elemBottomj'ai utilisé elemTop + $(elem).outerHeight(true).
Sarah Vessels
13
Pour: "N'importe quelle partie de l'élément en vue", j'ai utilisé: (((elemTop> = docViewTop) && (elemTop <= docViewBottom)) || ((elemBottom> = docViewTop) && (elemBottom <= docViewBottom)))
Grizly
415

Cette réponse à la vanille:

function isScrolledIntoView(el) {
    var rect = el.getBoundingClientRect();
    var elemTop = rect.top;
    var elemBottom = rect.bottom;

    // Only completely visible elements return true:
    var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
    // Partially visible elements return true:
    //isVisible = elemTop < window.innerHeight && elemBottom >= 0;
    return isVisible;
}
bravedick
la source
27
ne devrait-il pas en être ainsi isVisible = elementTop < window.innerHeight && elementBottom >= 0? Sinon, un élément à moitié sur l'écran renvoie faux.
gman
7
non. je vérifie si certains éléments sont bien visibles sur la page. si vous souhaitez vérifier la visibilité d'une partie - vous pouvez personnaliser cet extrait.
bravedick
15
Je trouve que cette réponse fonctionne mieux que la réponse choisie. Plus simple aussi.
Adam Venezia
12
Par rapport à la réponse approuvée, cela fonctionne beaucoup mieux avec des centaines d'éléments.
ncla
5
voir un petit violon démontrant ici - jsfiddle.net/shaaraddalvi/4rp09jL0
upInCloud
122

Mise à jour: utilisez IntersectionObserver


La meilleure méthode que j'ai trouvée jusqu'à présent est le plugin jQuery appear . Fonctionne comme un charme.

Imite un événement "d'apparition" personnalisé, qui se déclenche lorsqu'un élément défile dans la vue ou devient visible par l'utilisateur.

$('#foo').appear(function() {
  $(this).text('Hello world');
});

Ce plugin peut être utilisé pour éviter les demandes inutiles de contenu caché ou en dehors de la zone visible.

Joe Lencioni
la source
30
C'est un plugin cool, sans aucun doute, mais ne répond pas à la question.
Jon Adams
5
Alors que le plugin jQuery-apparaît est bon pour le contenu de la zone de page principale, il a malheureusement des problèmes avec les divs de défilement de taille fixe avec débordement. L'événement peut se déclencher prématurément lorsque l'élément lié se trouve dans la zone visible de la page mais en dehors de la zone visible de la division, puis ne pas se déclencher comme prévu lorsque l'élément apparaît dans la division.
Peter
17
Existe-t-il un plugin disparaître?
Shamoon
3
@Shamoon vérifie la source de la appear pluginet vous aurez probablement juste besoin d'ajouter un !quelque part pour obtenir un disappearplugin.
Lucky Soni
5
Notez que cela ne fonctionne pas avec jQuery 1.11.X github.com/morr/jquery.appear/issues/37
Jason Parham
86

Voici ma solution JavaScript pure qui fonctionne si elle est également cachée dans un conteneur défilant.

Démo ici (essayez aussi de redimensionner la fenêtre)

var visibleY = function(el){
  var rect = el.getBoundingClientRect(), top = rect.top, height = rect.height, 
    el = el.parentNode
  // Check if bottom of the element is off the page
  if (rect.bottom < 0) return false
  // Check its within the document viewport
  if (top > document.documentElement.clientHeight) return false
  do {
    rect = el.getBoundingClientRect()
    if (top <= rect.bottom === false) return false
    // Check if the element is out of view due to a container scrolling
    if ((top + height) <= rect.top) return false
    el = el.parentNode
  } while (el != document.body)
  return true
};

EDIT 2016-03-26: J'ai mis à jour la solution pour tenir compte du défilement au-delà de l'élément afin qu'il soit masqué au-dessus du haut du conteneur à défilement. EDIT 2018-10-08: mis à jour pour gérer le défilement hors de la vue au-dessus de l'écran.

Allié
la source
merci, peut-être mieux return top <= document.documentElement.clientHeight && top >= 0;
Yousef Salimpour
16
+1 Il s'agit de la seule réponse codée (c'est-à-dire pas d'un tiers) qui prend en compte la nature récursive des éléments. Je me suis étendu pour gérer le défilement horizontal, vertical et de page: jsfiddle.net/9nuqpgqa
Pebbl
3
Cette solution vérifie uniquement le haut de l'élément. Si le premier pixel supérieur est visible, il retournera vrai même si le reste de l'élément n'est pas visible. Pour vérifier si l'élément entier est visible, vous devez également vérifier la propriété du bas.
Wojciech Jakubas
2
Oui, bien! Utilisé pour aider à écrire cette réponse (avec crédit comme commentaire js).
Roamer-1888
Manquant ; après le deuxième "retour faux" dans la boucle
Mikhail Ramendik
46

Utilisation de l' API IntersectionObserver (native dans les navigateurs modernes)

Il est facile et efficace de déterminer si un élément est visible dans la fenêtre de visualisation, ou dans n'importe quel conteneur déroulant, en utilisant un observateur .

La nécessité d'attacher un scrollévénement et de vérifier manuellement le rappel de l'événement est éliminée, d'où l'efficacité:

// this is the target which is observed
var target = document.querySelector('div');

// configure the intersection observer instance
var intersectionObserverOptions = {
  root: null,
  rootMargin: '150px',
  threshold: 1.0
}
    
var observer = new IntersectionObserver(onIntersection, intersectionObserverOptions);

// provide the observer with a target
observer.observe(target);

function onIntersection(entries){
  entries.forEach(entry => {
    console.clear();
    console.log(entry.intersectionRatio)
    target.classList.toggle('visible', entry.intersectionRatio > 0);
    
    // Are we in viewport?
    if (entry.intersectionRatio > 0) {
      // Stop watching 
      // observer.unobserve(entry.target);
    }
  });
}
.box{ width:100px; height:100px; background:red; margin:1000px; }
.box.visible{ background:green; }
Scroll both Vertically & Horizontally...
<div class='box'></div>


Afficher le tableau de prise en charge des navigateurs (non pris en charge dans IE / Safari)

vsync
la source
4
Merci! Cela fonctionne pour moi et l'a également fait fonctionner dans IE11 avec github.com/w3c/IntersectionObserver
Matt Wilson
De loin la meilleure solution. Fonctionné dans IE11 sans polyfill!
Fabian von Ellerts
Notez que ce STILL n'est pas encore pris en charge dans iOS / macOS Safari, malheureusement. Assurez-vous de vérifier les problèmes de perf si vous choisissez de polyfill, c'est un grand groupe d'utilisateurs
Leland
@Leland - cela dépend du projet. pour tous mes projets c'est un groupe absolu de 0 utilisateurs. Je ne construis pas de sites Web mais un système Web;)
vsync
j'essaye d'exécuter ceci dans une boucle sur plusieurs éléments, mais cela ne fonctionne pas. Des idées? J'ajoute l'élément à cibler dans cette boucle.
Sascha Grindau
42

Le plugin jQuery Waypoints est très bien ici.

$('.entry').waypoint(function() {
   alert('You have scrolled to an entry.');
});

Il y a quelques exemples sur le site du plugin .

Fedir RYKHTIK
la source
3
Pour moi, cela ne fonctionnait qu'avec un décalage $('#my-div').waypoint(function() { console.log('Hello there!'); }, { offset: '100%' });
leymannx
21

Que diriez-vous

function isInView(elem){
   return $(elem).offset().top - $(window).scrollTop() < $(elem).height() ;
}

Après cela, vous pouvez déclencher ce que vous voulez une fois que l'élément est en vue comme celui-ci

$(window).scroll(function(){
   if (isInView($('.classOfDivToCheck')))
      //fire whatever you what 
      dothis();
})

Cela fonctionne très bien pour moi

webicy
la source
1
Cela fonctionne pour moi, mais j'ai utilisé la fonction isScrolledIntoView, apparemment plus complète, sur stackoverflow.com/questions/487073/… :)
Meetai.com
3
Je pense que ce devrait être $ (window) .scrollTop () <$ (elem) .offset (). Top + $ (elem) .height ();
Jeune
Ma modification serait la suivante: `return $ (window) .scrollTop () + $ (window) .height ()> $ (elem) .offset (). Top + $ (elem) .height (); `
bubencode
16

Tweeked la fonction cool de Scott Dowding pour mes besoins - ceci est utilisé pour trouver si l'élément vient de défiler dans l'écran, c'est-à-dire son bord supérieur.

function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();
    var elemTop = $(elem).offset().top;
    return ((elemTop <= docViewBottom) && (elemTop >= docViewTop));
}
Snigdha Batra
la source
15

WebResourcesDepot a écrit un script à charger lors du défilement qui utilise jQuery il y a quelque temps. Vous pouvez voir leur démo en direct ici . Le bœuf de leur fonctionnalité était le suivant:

$(window).scroll(function(){
  if  ($(window).scrollTop() == $(document).height() - $(window).height()){
    lastAddedLiveFunc();
  }
});

function lastAddedLiveFunc() { 
  $('div#lastPostsLoader').html('<img src="images/bigLoader.gif">');
  $.post("default.asp?action=getLastPosts&lastPostID="+$(".wrdLatest:last").attr("id"),
    function(data){
        if (data != "") {
          $(".wrdLatest:last").after(data);         
        }
      $('div#lastPostsLoader').empty();
    });
};
Sampson
la source
12

Vanille simple pour vérifier si element ( el) est visible dans div ( holder) défilant

function isElementVisible (el, holder) {
  holder = holder || document.body
  const { top, bottom, height } = el.getBoundingClientRect()
  const holderRect = holder.getBoundingClientRect()

  return top <= holderRect.top
    ? holderRect.top - top <= height
    : bottom - holderRect.bottom <= height
},

Utilisation avec jQuery:

var el = $('tr:last').get(0);
var holder = $('table').get(0);
isVisible =  isScrolledIntoView(el, holder);
Denis Matafonov
la source
2
À l'ère des applications à page unique, il est devenu plus courant de vérifier si un élément est visible dans un autre élément que la fenêtre . C'est pourquoi celui-ci obtient mon vote positif.
H Dog
8

isScrolledIntoView est une fonction très nécessaire, donc je l'ai essayée, elle fonctionne pour des éléments pas plus élevés que la fenêtre, mais si l'élément est plus grand que la fenêtre, cela ne fonctionne pas. Pour résoudre ce problème, modifiez facilement la condition

return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));

pour ça:

return (docViewBottom >= elemTop && docViewTop <= elemBottom);

Voir la démo ici: http://jsfiddle.net/RRSmQ/

Robert
la source
8

La plupart des réponses ici ne prennent pas en compte le fait qu'un élément peut également être masqué car il défile hors de la vue d'un div, pas seulement de la page entière.

Pour couvrir cette possibilité, vous devez essentiellement vérifier si l'élément est positionné à l'intérieur des limites de chacun de ses parents.

Cette solution fait exactement cela:

function(element, 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 = element.getBoundingClientRect();
    var parentRects = [];

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

Il vous permet également de spécifier à quel pourcentage il doit être visible dans chaque direction.
Il ne couvre pas la possibilité qu'il puisse être caché en raison d'autres facteurs, comme display: hidden.

Cela devrait fonctionner dans tous les principaux navigateurs, car il n'utilise que getBoundingClientRect. Je l'ai personnellement testé dans Chrome et Internet Explorer 11.

Domysee
la source
Merci pour ce code. Je me demande comment vous ajouteriez l'écouteur d'événements sur scroll dans ce cas où vous avez plusieurs éléments scrollables imbriqués? Il semble que l'ajout de l'écouteur à la fenêtre seule ne suffit pas, devons-nous revenir au parent supérieur pour ajouter l'écouteur à chaque conteneur déroulant?
mr1031011
@ mr1031011 Il devrait être possible d'ajouter le gestionnaire à la fenêtre, puis de rechercher la cible pour identifier le conteneur qui a été déroulé.
Domysee
à droite, cela ne fonctionne pas avec l'exemple donné par @vanowm,
mr1031011
7
function isScrolledIntoView(elem) {
    var docViewTop = $(window).scrollTop(),
        docViewBottom = docViewTop + $(window).height(),
        elemTop = $(elem).offset().top,
     elemBottom = elemTop + $(elem).height();
   //Is more than half of the element visible
   return ((elemTop + ((elemBottom - elemTop)/2)) >= docViewTop && ((elemTop + ((elemBottom - elemTop)/2)) <= docViewBottom));
}
Pascal Gagneur
la source
7

Voici une autre solution de http://web-profile.com.ua/

<script type="text/javascript">
$.fn.is_on_screen = function(){
    var win = $(window);
    var viewport = {
        top : win.scrollTop(),
        left : win.scrollLeft()
    };
    viewport.right = viewport.left + win.width();
    viewport.bottom = viewport.top + win.height();

    var bounds = this.offset();
    bounds.right = bounds.left + this.outerWidth();
    bounds.bottom = bounds.top + this.outerHeight();

    return (!(viewport.right < bounds.left || viewport.left > bounds.right ||    viewport.bottom < bounds.top || viewport.top > bounds.bottom));
 };

if( $('.target').length > 0 ) { // if target element exists in DOM
    if( $('.target').is_on_screen() ) { // if target element is visible on screen after DOM loaded
        $('.log').html('<div class="alert alert-success">target element is visible on screen</div>'); // log info       
    } else {
        $('.log').html('<div class="alert">target element is not visible on screen</div>'); // log info
    }
}
$(window).scroll(function(){ // bind window scroll event
if( $('.target').length > 0 ) { // if target element exists in DOM
    if( $('.target').is_on_screen() ) { // if target element is visible on screen after DOM loaded
        $('.log').html('<div class="alert alert-success">target element is visible on screen</div>'); // log info
    } else {
        $('.log').html('<div class="alert">target element is not visible on screen</div>'); // log info
    }
}
});
</script>

Voir dans JSFiddle

Adrian P.
la source
7

Cela prend en compte le remplissage, la bordure ou la marge de l'élément ainsi que les éléments plus grands que la fenêtre elle-même.

function inViewport($ele) {
    var lBound = $(window).scrollTop(),
        uBound = lBound + $(window).height(),
        top = $ele.offset().top,
        bottom = top + $ele.outerHeight(true);

    return (top > lBound && top < uBound)
        || (bottom > lBound && bottom < uBound)
        || (lBound >= top && lBound <= bottom)
        || (uBound >= top && uBound <= bottom);
}

Pour l'appeler, utilisez quelque chose comme ceci:

var $myElement = $('#my-element'),
    canUserSeeIt = inViewport($myElement);

console.log(canUserSeeIt); // true, if element is visible; false otherwise
Brent Barbata
la source
7

Il existe un plugin pour jQuery appelé inview qui ajoute un nouvel événement "inview".


Voici du code pour un plugin jQuery qui n'utilise pas d'événements:

$.extend($.expr[':'],{
    inView: function(a) {
        var st = (document.documentElement.scrollTop || document.body.scrollTop),
            ot = $(a).offset().top,
            wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();
        return ot > st && ($(a).height() + ot) < (st + wh);
    }
});

(function( $ ) {
    $.fn.inView = function() {
        var st = (document.documentElement.scrollTop || document.body.scrollTop),
        ot = $(this).offset().top,
        wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();

        return ot > st && ($(this).height() + ot) < (st + wh);
    };
})( jQuery );

J'ai trouvé cela dans un commentaire ici ( http://remysharp.com/2009/01/26/element-in-view-event-plugin/ ) par un mec appelé James

ness-EE
la source
Hélas, jQuery inview n'est plus maintenu et ne fonctionne pas avec les versions actuelles de jQuery.
mikemaccana
1
JQuery 1 est pour la prise en charge de navigateur hérité, de nouvelles fonctionnalités sont dans jQuery 2.
mikemaccana
Le lien n'affiche pas l'exemple car la page a été mise à jour.
Professeur de programmation
6

Je devais vérifier la visibilité dans les éléments à l'intérieur du conteneur DIV déroulant

    //p = DIV container scrollable
    //e = element
    function visible_in_container(p, e) {
        var z = p.getBoundingClientRect();
        var r = e.getBoundingClientRect();

        // Check style visiblilty and off-limits
        return e.style.opacity > 0 && e.style.display !== 'none' &&
               e.style.visibility !== 'hidden' &&
               !(r.top > z.bottom || r.bottom < z.top ||
                 r.left > z.right || r.right < z.left);
    }
Pigmalión
la source
cela fonctionne pour moi si je change le e.style.opacity > 0en (!e.style.opacity || e.style.opacity > 0)car par défaut c'est la chaîne vide pour moi dans FF.
Brett Zamir
6

À partir de cette excellente réponse , vous pouvez la simplifier un peu plus en utilisant ES2015 +:

function isScrolledIntoView(el) {
  const { top, bottom } = el.getBoundingClientRect()
  return top >= 0 && bottom <= window.innerHeight
}

Si vous ne vous souciez pas du haut qui sort de la fenêtre et que vous vous souciez simplement que le bas a été vu, cela peut être simplifié pour

function isSeen(el) {
  return el.getBoundingClientRect().bottom <= window.innerHeight
}

ou même le one-liner

const isSeen = el => el.getBoundingClientRect().bottom <= window.innerHeight
rpearce
la source
4

Vous pouvez utiliser le plugin jquery "onScreen" pour vérifier si l'élément est dans la fenêtre courante lorsque vous faites défiler. Le plugin définit le ": onScreen" du sélecteur sur true lorsque le sélecteur apparaît à l'écran. Il s'agit du lien du plugin que vous pouvez inclure dans votre projet. " http://benpickles.github.io/onScreen/jquery.onscreen.min.js "

Vous pouvez essayer l'exemple ci-dessous qui fonctionne pour moi.

$(document).scroll(function() {
    if($("#div2").is(':onScreen')) {
        console.log("Element appeared on Screen");
        //do all your stuffs here when element is visible.
    }
    else {
        console.log("Element not on Screen");
        //do all your stuffs here when element is not visible.
    }
});

Code HTML:

<div id="div1" style="width: 400px; height: 1000px; padding-top: 20px; position: relative; top: 45px"></div> <br>
<hr /> <br>
<div id="div2" style="width: 400px; height: 200px"></div>

CSS:

#div1 {
    background-color: red;
}
#div2 {
    background-color: green;
}
Vasuki Dileep
la source
3

J'ai une telle méthode dans mon application, mais elle n'utilise pas jQuery:

/* Get the TOP position of a given element. */
function getPositionTop(element){
    var offset = 0;
    while(element) {
        offset += element["offsetTop"];
        element = element.offsetParent;
    }
    return offset;
}

/* Is a given element is visible or not? */
function isElementVisible(eltId) {
    var elt = document.getElementById(eltId);
    if (!elt) {
        // Element not found.
        return false;
    }
    // Get the top and bottom position of the given element.
    var posTop = getPositionTop(elt);
    var posBottom = posTop + elt.offsetHeight;
    // Get the top and bottom position of the *visible* part of the window.
    var visibleTop = document.body.scrollTop;
    var visibleBottom = visibleTop + document.documentElement.offsetHeight;
    return ((posBottom >= visibleTop) && (posTop <= visibleBottom));
}

Edit: Cette méthode fonctionne bien pour IE (au moins la version 6). Lisez les commentaires pour la compatibilité avec FF.

Romain Linsolas
la source
2
Pour une raison quelconque, document.body.scrollTop renvoie toujours 0 (sur ff3). Remplacez-le par var visibleTop = (document.documentElement.scrollTop? Document.documentElement.scrollTop: document.body.scrollTop);
yoavf
Désolé. Mon application doit fonctionner uniquement dans IE 6 (oui, je n'ai pas de chance :(), donc je n'ai jamais testé ça dans FF ...
Romain Linsolas
Ce serait la meilleure réponse ici si elle était correcte. Corrigez une de vos lignes à ceci: var visibleBottom = visibleTop + window.innerHeight;je n'utilise pas jQuery et vous m'avez aidé à trouver la bonne réponse.
Bitterblue
3

Si vous souhaitez modifier cela pour faire défiler l'élément dans une autre div,

function isScrolledIntoView (elem, divID) 

{

    var docViewTop = $('#' + divID).scrollTop();


    var docViewBottom = docViewTop + $('#' + divID).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); 
}
Samiya Akhtar
la source
3

Modification de la réponse acceptée afin que l'élément ait sa propriété d'affichage définie sur autre chose que «aucun» pour que la qualité soit visible.

function isScrolledIntoView(elem) {
   var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();

  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();
  var elemDisplayNotNone = $(elem).css("display") !== "none";

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop) && elemDisplayNotNone);
}
evanmcd
la source
3

Voici un moyen de réaliser la même chose en utilisant Mootools, en horizontal, vertical ou les deux.

Element.implement({
inVerticalView: function (full) {
    if (typeOf(full) === "null") {
        full = true;
    }

    if (this.getStyle('display') === 'none') {
        return false;
    }

    // Window Size and Scroll
    var windowScroll = window.getScroll();
    var windowSize = window.getSize();
    // Element Size and Scroll
    var elementPosition = this.getPosition();
    var elementSize = this.getSize();

    // Calculation Variables
    var docViewTop = windowScroll.y;
    var docViewBottom = docViewTop + windowSize.y;
    var elemTop = elementPosition.y;
    var elemBottom = elemTop + elementSize.y;

    if (full) {
        return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
            && (elemBottom <= docViewBottom) && (elemTop >= docViewTop) );
    } else {
        return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    }
},
inHorizontalView: function(full) {
    if (typeOf(full) === "null") {
        full = true;
    }

    if (this.getStyle('display') === 'none') {
        return false;
    }

    // Window Size and Scroll
    var windowScroll = window.getScroll();
    var windowSize = window.getSize();
    // Element Size and Scroll
    var elementPosition = this.getPosition();
    var elementSize = this.getSize();

    // Calculation Variables
    var docViewLeft = windowScroll.x;
    var docViewRight = docViewLeft + windowSize.x;
    var elemLeft = elementPosition.x;
    var elemRight = elemLeft + elementSize.x;

    if (full) {
        return ((elemRight >= docViewLeft) && (elemLeft <= docViewRight)
            && (elemRight <= docViewRight) && (elemLeft >= docViewLeft) );
    } else {
        return ((elemRight <= docViewRight) && (elemLeft >= docViewLeft));
    }
},
inView: function(full) {
    return this.inHorizontalView(full) && this.inVerticalView(full);
}});
bmlkc
la source
3

Un exemple basé sur cette réponse pour vérifier si un élément est visible à 75% (c'est-à-dire que moins de 25% de celui-ci est hors de l'écran).

function isScrolledIntoView(el) {
  // check for 75% visible
  var percentVisible = 0.75;
  var elemTop = el.getBoundingClientRect().top;
  var elemBottom = el.getBoundingClientRect().bottom;
  var elemHeight = el.getBoundingClientRect().height;
  var overhang = elemHeight * (1 - percentVisible);

  var isVisible = (elemTop >= -overhang) && (elemBottom <= window.innerHeight + overhang);
  return isVisible;
}
Brendan Nee
la source
3

Il y a plus de 30 réponses à cette question, et aucune d'entre elles n'utilise la solution JS incroyablement simple et pure que j'utilise. Il n'est pas nécessaire de charger jQuery juste pour résoudre ce problème, car beaucoup d'autres poussent.

Afin de savoir si l'élément se trouve dans la fenêtre, nous devons d'abord déterminer la position des éléments dans le corps. Nous n'avons pas besoin de le faire récursivement comme je le pensais autrefois. Au lieu de cela, nous pouvons utiliser element.getBoundingClientRect().

pos = elem.getBoundingClientRect().top - document.body.getBoundingClientRect().top;

Cette valeur est la différence Y entre le haut de l'objet et le haut du corps.

Nous devons alors dire si l'élément est en vue. La plupart des implémentations demandent si l'élément complet se trouve dans la fenêtre d'affichage, c'est donc ce que nous allons couvrir.

Tout d' abord, la position de haut de la fenêtre est: window.scrollY.

Nous pouvons obtenir la position inférieure de la fenêtre en ajoutant la hauteur de la fenêtre à sa position supérieure:

var window_bottom_position = window.scrollY + window.innerHeight;

Permet de créer une fonction simple pour obtenir la position supérieure de l'élément:

function getElementWindowTop(elem){
    return elem && typeof elem.getBoundingClientRect === 'function' ? elem.getBoundingClientRect().top - document.body.getBoundingClientRect().top : 0;
}

Cette fonction renverra la position supérieure de l'élément dans la fenêtre ou elle reviendra 0si vous lui passez autre chose qu'un élément avec la .getBoundingClientRect()méthode. Cette méthode existe depuis longtemps, vous ne devriez donc pas avoir à vous soucier que votre navigateur ne la supporte pas.

Maintenant, la première position de notre élément est:

var element_top_position = getElementWindowTop(element);

Et la position inférieure de l'élément ou est:

var element_bottom_position = element_top_position + element.clientHeight;

Nous pouvons maintenant déterminer si l'élément se trouve dans la fenêtre en vérifiant si la position inférieure de l'élément est inférieure à la position supérieure de la fenêtre et en vérifiant si la position supérieure de l'élément est supérieure à la position inférieure de la fenêtre:

if(element_bottom_position >= window.scrollY 
&& element_top_position <= window_bottom_position){
    //element is in view
else
    //element is not in view

De là, vous pouvez effectuer la logique pour ajouter ou supprimer une in-viewclasse sur votre élément, que vous pourrez ensuite gérer plus tard avec des effets de transition dans votre CSS.

Je suis absolument étonné de ne pas avoir trouvé cette solution ailleurs, mais je pense que c'est la solution la plus propre et la plus efficace, et elle ne vous oblige pas à charger jQuery!

WebWanderer
la source
Très belle explication! Mais il y a déjà des réponses qui font exactement ce que vous faites, comme la réponse d'Ally
Domysee
1
@Domysee Hmm, j'ai en quelque sorte ignoré cela. C'est suffisant. Merci de l'avoir souligné. C'est agréable de voir cela se faire d'une autre manière.
WebWanderer
3

Une version plus efficace de cette réponse :

 /**
 * Is element within visible region of a scrollable container
 * @param {HTMLElement} el - element to test
 * @returns {boolean} true if within visible region, otherwise false
 */
 function isScrolledIntoView(el) {
      var rect = el.getBoundingClientRect();
      return (rect.top >= 0) && (rect.bottom <= window.innerHeight);
 }
John Doherty
la source
2

Cette méthode renvoie true si une partie de l'élément est visible sur la page. Cela a mieux fonctionné dans mon cas et peut aider quelqu'un d'autre.

function isOnScreen(element) {
  var elementOffsetTop = element.offset().top;
  var elementHeight = element.height();

  var screenScrollTop = $(window).scrollTop();
  var screenHeight = $(window).height();

  var scrollIsAboveElement = elementOffsetTop + elementHeight - screenScrollTop >= 0;
  var elementIsVisibleOnScreen = screenScrollTop + screenHeight - elementOffsetTop >= 0;

  return scrollIsAboveElement && elementIsVisibleOnScreen;
}
Rafael Garcia
la source
2

Modification simple pour div défilable (conteneur)

var isScrolledIntoView = function(elem, container) {
    var containerHeight = $(container).height();
    var elemTop = $(elem).position().top;
    var elemBottom = elemTop + $(elem).height();
    return (elemBottom > 0 && elemTop < containerHeight);
}

REMARQUE: cela ne fonctionne pas si l'élément est plus grand que la div défilable.

Derrick J Wippler
la source
2

J'ai adapté cette courte extension de fonction jQuery, que vous pouvez utiliser librement (licence MIT).

/**
 * returns true if an element is visible, with decent performance
 * @param [scope] scope of the render-window instance; default: window
 * @returns {boolean}
 */
jQuery.fn.isOnScreen = function(scope){
    var element = this;
    if(!element){
        return;
    }
    var target = $(element);
    if(target.is(':visible') == false){
        return false;
    }
    scope = $(scope || window);
    var top = scope.scrollTop();
    var bot = top + scope.height();
    var elTop = target.offset().top;
    var elBot = elTop + target.height();

    return ((elBot <= bot) && (elTop >= top));
};
Lorenz Lo Sauer
la source
2

J'ai écrit un composant pour la tâche, conçu pour gérer un grand nombre d'éléments extrêmement rapidement (à hauteur de <10 ms pour 1000 éléments sur un mobile lent ).

Il fonctionne avec tous les types de conteneurs de défilement auxquels vous avez accès - fenêtre, éléments HTML, iframe intégré, fenêtre enfant générée - et est très flexible dans ce qu'il détecte ( visibilité complète ou partielle , zone de bordure ou zone de contenu , zone de tolérance personnalisée , etc. ).

Une énorme suite de tests, principalement générée automatiquement, garantit qu'elle fonctionne comme un navigateur croisé annoncé .

Essayez-le si vous le souhaitez: jQuery.isInView . Sinon, vous pourriez trouver l'inspiration dans le code source, par exemple ici .

hashchange
la source