web-dev-qa-db-fra.com

Obtenez la hauteur visible d'une div avec jQuery

J'ai besoin de récupérer la hauteur visible d'un div dans une zone de défilement. Je me considère assez décent avec jQuery, mais cela me rejette complètement.

Disons que j'ai un div rouge dans un emballage noir:

Dans le graphique ci-dessus, la fonction jQuery renverrait 248, la partie visible de la div.

Une fois que l'utilisateur a dépassé le haut de la div, comme indiqué dans le graphique ci-dessus, il enregistre 296.

Maintenant, une fois que l'utilisateur a fait défiler le div, il en signalera à nouveau 248.

De toute évidence, mes chiffres ne seront pas aussi cohérents et clairs que dans cette démo, sinon je coderais simplement pour ces chiffres.

J'ai un peu de théorie:

  • Obtenez la hauteur de la fenêtre
  • Obtenez la hauteur de la div
  • Obtenir le décalage initial de la div à partir du haut de la fenêtre
  • Obtenir le décalage lorsque l'utilisateur fait défiler.
    • Si le décalage est positif, cela signifie que le haut de la div est toujours visible.
    • si c'est négatif, le haut de la div a été éclipsé par la fenêtre. À ce stade, la div peut soit occuper toute la hauteur de la fenêtre, soit le bas de la div peut montrer
    • Si le bas de la div est visible, déterminez l’écart entre elle et le bas de la fenêtre.

Cela semble assez simple, mais je ne peux tout simplement pas comprendre. Je vais prendre une autre fissure demain matin; Je pensais que certains de vos génies pourraient peut-être vous aider.

Merci!

MISE À JOUR: J'ai découvert cela par moi-même, mais il semblerait qu'une des réponses ci-dessous soit plus élégante. Je vais donc l'utiliser à la place. Pour les curieux, voici ce que j'ai inventé:

$(document).ready(function() {
    var windowHeight = $(window).height();
    var overviewHeight = $("#overview").height();
    var overviewStaticTop = $("#overview").offset().top;
    var overviewScrollTop = overviewStaticTop - $(window).scrollTop();
    var overviewStaticBottom = overviewStaticTop + $("#overview").height();
    var overviewScrollBottom = windowHeight - (overviewStaticBottom - $(window).scrollTop());
    var visibleArea;
    if ((overviewHeight + overviewScrollTop) < windowHeight) {
        // alert("bottom is showing!");
        visibleArea = windowHeight - overviewScrollBottom;
        // alert(visibleArea);
    } else {
        if (overviewScrollTop < 0) {
            // alert("is full height");
            visibleArea = windowHeight;
            // alert(visibleArea);
        } else {
            // alert("top is showing");
            visibleArea = windowHeight - overviewScrollTop;
            // alert(visibleArea);
        }
    }
});
82
JacobTheDev

Voici un concept rapide et sale. Il compare fondamentalement la offset().top de l'élément au haut de la fenêtre et la offset().top + height() au bas de la fenêtre:

function getVisible() {    
    var $el = $('#foo'),
        scrollTop = $(this).scrollTop(),
        scrollBot = scrollTop + $(this).height(),
        elTop = $el.offset().top,
        elBottom = elTop + $el.outerHeight(),
        visibleTop = elTop < scrollTop ? scrollTop : elTop,
        visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
    $('#notification').text(visibleBottom - visibleTop);
}

$(window).on('scroll resize', getVisible);

Exemple de violon

edit - petite mise à jour pour exécuter également la logique lorsque la fenêtre est redimensionnée.

56
Rory McCrossan

Calculer la quantité de px qu'un élément (hauteur) est dans la fenêtre

Démo Fiddle

Cette petite fonction renverra le montant de px un élément est visible dans la (verticale) Viewport =:

function inViewport($el) {
    var elH = $el.outerHeight(),
        H   = $(window).height(),
        r   = $el[0].getBoundingClientRect(), t=r.top, b=r.bottom;
    return Math.max(0, t>0? Math.min(elH, H-t) : Math.min(b, H));
}

Utilisez comme:

$(window).on("scroll resize", function(){
  console.log( inViewport($('#elementID')) ); // n px in viewport
});

c'est tout.


jQuery .inViewport() plugin

démo jsFiddle

à partir de ce qui précède, vous pouvez extraire la logique et créer un plugin comme celui-ci:

/**
 * inViewport jQuery plugin by Roko C.B.
 * http://stackoverflow.com/a/26831113/383904
 * Returns a callback function with an argument holding
 * the current amount of px an element is visible in viewport
 * (The min returned value is 0 (element outside of viewport)
 */
;(function($, win) {
  $.fn.inViewport = function(cb) {
     return this.each(function(i,el) {
       function visPx(){
         var elH = $(el).outerHeight(),
             H = $(win).height(),
             r = el.getBoundingClientRect(), t=r.top, b=r.bottom;
         return cb.call(el, Math.max(0, t>0? Math.min(elH, H-t) : Math.min(b, H)));  
       }
       visPx();
       $(win).on("resize scroll", visPx);
     });
  };
}(jQuery, window));

Utilisez comme:

$("selector").inViewport(function(px) {
  console.log( px ); // `px` represents the amount of visible height
  if(px > 0) {
    // do this if element enters the viewport // px > 0
  }else{
    // do that if element exits  the viewport // px = 0
  }
}); // Here you can chain other jQuery methods to your selector

vos sélecteurs écouteront dynamiquement la fenêtre scroll et resize, mais renverront également la valeur initiale sur DOM ready par le premier argument de la fonction de rappel px.

47
Roko C. Buljan

Voici une version de l'approche de Rory ci-dessus, sauf qu'elle est écrite pour fonctionner comme un plugin jQuery. Il peut avoir une applicabilité plus générale dans ce format. Excellente réponse, Rory - merci!

$.fn.visibleHeight = function() {
    var elBottom, elTop, scrollBot, scrollTop, visibleBottom, visibleTop;
    scrollTop = $(window).scrollTop();
    scrollBot = scrollTop + $(window).height();
    elTop = this.offset().top;
    elBottom = elTop + this.outerHeight();
    visibleTop = elTop < scrollTop ? scrollTop : elTop;
    visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
    return visibleBottom - visibleTop
}

Peut être appelé avec ce qui suit:

$("#myDiv").visibleHeight();

jsFiddle

11
Michael.Lumley