¿Cómo puedo saber si un elemento DOM es visible en la ventana gráfica actual?

Resuelto benzaita asked hace 16 años • 31 respuestas

¿Existe una forma eficaz de saber si un elemento DOM (en un documento HTML) está actualmente visible (aparece en la ventana gráfica )?

(La pregunta se refiere a Firefox).

benzaita avatar Sep 24 '08 04:09 benzaita
Aceptado

Ahora la mayoría de los navegadores admiten el método getBoundingClientRect , que se ha convertido en la mejor práctica. Usar una respuesta antigua es muy lento, no preciso y tiene varios errores .

La solución seleccionada como correcta casi nunca es precisa .


Esta solución se probó en Internet Explorer 7 (y posteriores), iOS 5 (y posteriores), Safari, Android 2.0 (Eclair) y posteriores, BlackBerry, Opera Mobile e Internet Explorer Mobile 9 .


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

Cómo utilizar:

Puede estar seguro de que la función proporcionada anteriormente devuelve la respuesta correcta en el momento en que se llama, pero ¿qué pasa con la visibilidad del elemento de seguimiento como un evento?

Coloque el siguiente código en la parte inferior de su <body>etiqueta:

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); // Internet Explorer 9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

Si realiza alguna modificación DOM, por supuesto, puede cambiar la visibilidad de su elemento.

Directrices y errores comunes:

¿Quizás necesite realizar un seguimiento del zoom de la página o del pellizco del dispositivo móvil? jQuery debería manejar el navegador cruzado con zoom/pellizco ; de lo contrario, el primer o segundo enlace debería ayudarle.

Si modifica DOM , puede afectar la visibilidad del elemento. Deberías tomar control sobre eso y llamar handler()manualmente. Lamentablemente, no tenemos ningún onrepaintevento entre navegadores. Por otro lado, eso nos permite realizar optimizaciones y volver a verificar solo las modificaciones del DOM que pueden cambiar la visibilidad de un elemento.

Nunca lo uses solo dentro de jQuery $(document).ready() , porque no hay garantía de que se haya aplicado CSS en este momento. Su código puede funcionar localmente con su CSS en un disco duro, pero una vez colocado en un servidor remoto fallará.

Después DOMContentLoadedde disparar, se aplican estilos , pero las imágenes aún no se cargan . Entonces, deberíamos agregar window.onloadun detector de eventos.

Aún no podemos captar el evento de zoom/pellizco.

El último recurso podría ser el siguiente código:

/* TODO: this looks like a very bad code */
setInterval(handler, 600);

Puede utilizar la increíble visibilidad de la página de funciones de la API HTML5 si le importa si la pestaña con su página web está activa y visible.

TODO: este método no maneja dos situaciones:

  • Superposición usando z-index.
  • Usando overflow-scrollen el contenedor del elemento.
  • Pruebe algo nuevo: explicación de la API de Intersection Observer .
Dan avatar Sep 26 '2011 15:09 Dan

Actualización: el tiempo avanza y nuestros navegadores también. Esta técnica ya no se recomienda y debes usar la solución de Dan si no necesitas admitir la versión de Internet Explorer anterior a la 7.

Solución original (ahora desactualizada):

Esto comprobará si el elemento es completamente visible en la ventana gráfica actual:

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

Podrías modificar esto simplemente para determinar si alguna parte del elemento es visible en la ventana gráfica:

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
  );
}
Prestaul avatar Sep 24 '2008 02:09 Prestaul