Obtenez la hauteur visible d'un div avec jQuery

86

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 dérange 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 du div.

Une fois que l'utilisateur fait défiler le haut du div, comme dans le graphique ci-dessus, il signale 296.

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

Évidemment, mes chiffres ne seront pas aussi cohérents et clairs que dans cette démo, ou je coderais simplement ces chiffres en dur.

J'ai un peu une théorie:

  • Obtenez la hauteur de la fenêtre
  • Obtenez la hauteur de la div
  • Obtenez le décalage initial du div à partir du haut de la fenêtre
  • Obtenez le décalage lorsque l'utilisateur fait défiler.
    • Si le décalage est positif, cela signifie que le haut du div est toujours visible.
    • s'il est négatif, le haut du div a été éclipsé par la fenêtre. À ce stade, le div pourrait occuper toute la hauteur de la fenêtre ou le bas du div pourrait afficher
    • Si le bas du div s'affiche, déterminez l'écart entre celui-ci 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; J'ai juste pensé que certains d'entre vous, les génies, pourraient peut-être aider.

Merci!

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

$(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);
        }
    }
});
JacobTheDev
la source
Je me pencherais sur les unités vh. 1vh = 1 / 100e de la hauteur de la fenêtre. Vous trouverez probablement une solution avec cela. Trouvez la hauteur de la fenêtre, la hauteur de l'élément, la position des éléments et la position de défilement et calculez en conséquence.
davidcondrey
En supposant qu'il y ait une marge sur le DIV intérieur, dites "10px" tout autour. Je détecterais la hauteur de défilement pour voir si elle est passée à "10", alors j'obtiendrais simplement la hauteur de l'élément parent, je la soustrais en fonction de sa hauteur de défilement.
Si tout le reste échoue, je viens de tomber sur ce script qui semble pouvoir servir le but dont vous avez besoin: larsjung.de/fracs
davidcondrey

Réponses:

57

Voici un concept rapide et sale. Il compare fondamentalement le offset().topde l'élément au haut de la fenêtre et le 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.

Rory McCrossan
la source
J'ai du mal à définir cela pour plusieurs divs sans simplement répéter le code. Y a-t-il un moyen de faire cela?
JacobTheDev
2
@Rev, voilà: jsfiddle.net/b5DGj/1 . J'ai séparé chaque élément en son propre appel de fonction. Vous pouvez même aller plus loin et définir un plugin pour le faire pour vous.
Rory McCrossan
J'ai légèrement réécrit l'approche de @ RoryMcCrossan pour fonctionner comme un plugin jQuery et l'ai posté comme réponse ci-dessous - il peut avoir une applicabilité plus générale dans ce format.
Michael.Lumley
Parfois, un élément peut être partiellement masqué à cause d'un débordement ou d'éléments parents, quelle que soit la "fenêtre"
Nadav B
55

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

Démo Fiddle

Cette petite fonction renverra la quantité d' pxun élément visible dans la fenêtre (verticale) :

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 ça.


.inViewport()Plugin jQuery

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 scrollet resizerenverront également la valeur initiale sur le DOM prêt via le premier argument de la fonction de rappel px.

Roko C. Buljan
la source
Pour travailler sur Android Chrome, vous devrez peut-être ajouter <meta name="viewport" content="width=your_size">dans la section html principale.
userlond
@userlond merci pour votre contribution, mais je ne suis pas sûr que content="width=1259"cela convienne à la majorité. Avez-vous essayé à la content="width=device-width, initial-scale=1"place?
Roko C. Buljan
Le site a un modèle hérité avec une largeur fixe et ne répond pas ... Je pense que votre suggestion sera acceptable pour la majorité. Donc, pour clarifier: si la solution de Roko C. Buljan ne fonctionne pas, essayez d'ajouter une <meta name="viewport" content="your_content">déclaration :)
userlond
C'est tellement génial et si proche de ce que je cherchais. Comment puis-je calculer la distance entre le bas des éléments et le bas de la fenêtre? Je peux donc charger plus de contenu avant de voir la fin du contenu. Par exemple, si elBottom <200 à partir de windowBot, lancez AJAX.
Thom
1
@Thom eh bien, la méthode ci-dessus ne renvoie aucune autre valeur spécifique (elle peut être étendue pour renvoyer plus de choses que juste visible height px, comme un objet. Voir ce violon pour une telle idée: jsfiddle.net/RokoCB/6xjq5gyy (ouvrir la console du développeur pour voir les journaux)
Roko C. Buljan
11

Voici une version de l'approche de Rory ci-dessus, sauf é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 les éléments suivants:

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

jsFiddle

Michael.Lumley
la source
2
Ne fonctionne pas au cas où la fenêtre deviendrait plus grande et que le bloc pourrait s'adapter. C'est pourquoi nous devons réinitialiser la hauteur du bloc: $ (this) .css ('height', ''); jsfiddle.net/b5DGj/144
Max Koshel