Obtenga la altura visible de un div con jQuery

86

Necesito recuperar la altura visible de un div dentro de un área desplazable. Me considero bastante decente con jQuery, pero esto me está confundiendo por completo.

Digamos que tengo un div rojo dentro de una envoltura negra:

En el gráfico anterior, la función jQuery devolvería 248, la parte visible del div.

Una vez que el usuario se desplaza más allá de la parte superior del div, como en el gráfico anterior, informará 296.

Ahora, una vez que el usuario se haya desplazado más allá del div, volvería a informar 248.

Obviamente, mis números no serán tan consistentes y claros como lo son en esta demostración, o simplemente codificaría esos números.

Tengo un poco de teoría:

  • Obtenga la altura de la ventana
  • Obtenga la altura del div
  • Obtenga el desplazamiento inicial del div desde la parte superior de la ventana
  • Obtenga el desplazamiento a medida que el usuario se desplaza.
    • Si el desplazamiento es positivo, significa que la parte superior del div aún está visible.
    • si es negativo, la parte superior del div ha sido eclipsada por la ventana. En este punto, el div podría estar ocupando toda la altura de la ventana, o la parte inferior del div podría estar mostrando
    • Si se muestra la parte inferior del div, calcule el espacio entre él y la parte inferior de la ventana.

Parece bastante simple, pero no puedo entenderlo. Tomaré otro crack mañana por la mañana; Supuse que algunos de ustedes, genios, podrían ayudar.

¡Gracias!

ACTUALIZACIÓN: descubrí esto por mi cuenta, pero parece que una de las respuestas a continuación es más elegante, así que la usaré en su lugar. Para los curiosos, esto es lo que se me ocurrió:

$(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
fuente
Buscaría unidades vh. 1vh = 1/100 de la altura de la ventana gráfica. Probablemente encontrará una solución con eso. Encuentre la altura de la ventana gráfica, la altura del elemento, la posición de los elementos y la posición de desplazamiento y calcule en consecuencia.
Davidcondrey
Suponiendo que hay un margen en el DIV interno, diga "10px" alrededor. Detectaría la altura del desplazamiento para ver si pasó "10", luego obtendría la altura del elemento principal y lo restaría en función de la altura del desplazamiento.
Si todo lo demás falla, acabo de encontrar este script que parece que puede servir para el propósito que necesita: larsjung.de/fracs
davidcondrey

Respuestas:

57

Aquí hay un concepto rápido y sucio. Básicamente, compara el offset().topdel elemento con la parte superior de la ventana y offset().top + height()con la parte inferior de la ventana:

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

Violín de ejemplo

editar : pequeña actualización para realizar también la lógica cuando se cambia el tamaño de la ventana.

Rory McCrossan
fuente
Tengo problemas para configurar esto para varios divmensajes de correo electrónico sin solo repetir el código. ¿Hay alguna forma de hacer esto?
JacobTheDev
2
@Rev aquí tienes: jsfiddle.net/b5DGj/1 . Separé cada elemento en su propia llamada de función. Incluso podría ir más allá y definir un complemento para hacer esto por usted.
Rory McCrossan
He reescrito ligeramente el enfoque de @ RoryMcCrossan para funcionar como un complemento de jQuery y lo publiqué como una respuesta a continuación; puede tener una aplicabilidad más general en ese formato.
Michael.Lumley
A veces, un elemento puede estar parcialmente oculto debido a un desbordamiento o elementos principales, independientemente de la "ventana"
Nadav B
55

Calcular la cantidad de px que tiene un elemento (altura) en la ventana gráfica

Demostración de violín

Esta pequeña función devolverá la cantidad de pxun elemento que es visible en la ventana gráfica (vertical) :

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

Usar como:

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

Eso es.


jQuery .inViewport()Plugin

jsFiddle demo

de lo anterior puede extraer la lógica y crear un complemento como este:

/**
 * 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));

Usar como:

$("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

sus selectores escucharán dinámicamente la ventana scrolly resizetambién devolverán el valor inicial en DOM listo a través del primer argumento de la función de devolución de llamada px.

Roko C. Buljan
fuente
Para trabajar en Android Chrome, es posible que deba agregarlo <meta name="viewport" content="width=your_size">a la sección html principal.
userlond
@userlond gracias por su contribución, pero no estoy seguro de content="width=1259"que sea adecuado para la mayoría. ¿Lo has intentado en su content="width=device-width, initial-scale=1"lugar?
Roko C. Buljan
El sitio tiene una plantilla heredada con un ancho fijo y no responde ... Creo que su sugerencia estará bien para la mayoría. Entonces, para aclarar: si la solución de Roko C. Buljan no funciona, intente agregar una <meta name="viewport" content="your_content">declaración :)
userlond
Esto es tan asombroso y tan cercano a lo que he estado buscando. ¿Cómo puedo calcular la distancia desde la parte inferior de los elementos hasta la parte inferior de la ventana? Entonces puedo cargar más contenido antes de que veas el final del contenido. Por ejemplo, si elBottom <200 de windowBot, dispara AJAX.
Thom
1
@Thom bueno, el método anterior no devuelve ningún otro valor específico (se puede expandir para devolver más cosas que solo visible height px, como un objeto. Vea este violín para una idea así: jsfiddle.net/RokoCB/6xjq5gyy (consola de desarrollador abierta para ver los registros)
Roko C. Buljan
11

Aquí hay una versión del enfoque de Rory anterior, excepto que está escrito para funcionar como un complemento de jQuery. Puede tener una aplicabilidad más general en ese formato. Gran respuesta, Rory, ¡gracias!

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

Se puede llamar con lo siguiente:

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

jsFiddle

Michael.Lumley
fuente
2
No trabaje en caso de que la ventana se haga más grande y el bloque pueda caber. Es por eso que necesitamos restablecer la altura del bloque: $ (this) .css ('height', ''); jsfiddle.net/b5DGj/144
Max Koshel