Pregunta

¿Hay una manera eficaz de saber si un elemento de DOM (en un documento HTML) es actualmente visible (aparece en la viewport)?

(La pregunta se refiere a Firefox)

¿Fue útil?

Solución

Actualización: El tiempo sigue su marcha y lo han hecho nuestros navegadores. Esta técnica no es recomendable y usted debe usar @Dan solución a continuación (https://stackoverflow.com/a/7557433/5628 si usted no necesita el apoyo de IE<7.

La solución Original (ahora obsoleto):

Se comprueba si el elemento es totalmente visible en la vista 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)
  );
}

Usted puede modificar este simplemente para determinar si cualquier parte de el elemento es visible en la vista:

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

Otros consejos

Ahora la mayoría de los navegadores apoyo getBoundingClientRect el método, que se ha convertido en la mejor práctica.La utilización de una vieja respuesta es muy lento, no se precisa y tiene varios errores.

La solución seleccionada como correcta es casi nunca se precisa.Usted puede leer más acerca de sus errores.


Esta solución fue probado en IE7+, iOS5+ Safari, Android2+, Blackberry, Opera Mobile, y IE Mobile 10.


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:

Usted puede estar seguro de que la función dada anteriormente devuelve la respuesta correcta en el momento de tiempo, cuando se llama, pero lo que sobre el seguimiento de visibilidad del elemento como un evento?

Coloque el código siguiente 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); // IE9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

Si haces cualquier DOM modificaciones, se puede cambiar de visibilidad del elemento de curso.

Las directrices y las dificultades comunes:

Tal vez usted necesita para realizar un seguimiento de la página de zoom / dispositivo móvil pizca? jQuery debe manejar zoom/sujetador cruz del navegador, de lo contrario primero o segundo enlace debe ayudarle.

Si modificar DOM, puede afectar a la visibilidad del elemento.Usted debe tomar el control sobre eso y llame a handler() de forma manual.Desafortunadamente, no contamos con todos los navegadores onrepaint evento.Por otra parte, que nos permite realizar optimizaciones y realizar re-verificación sólo en DOM modificaciones que puede cambiar la visibilidad del elemento.

Nunca Jamás lo utilizan en jQuery $(document).listo() sólo, porque no hay ninguna garantía de CSS se ha aplicado en este momento.El código puede trabajar localmente con tu CSS en el disco duro, pero una vez puesto en servidor remoto es un error.

Después de DOMContentLoaded se despide, se aplican los estilos, pero las imágenes que aún no se han cargado.Así, debemos agregar window.onload detector de eventos.

No podemos coger zoom/sujetador evento todavía.

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

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

Usted puede utilizar la característica impresionante pageVisibiliy La API de HTML5 si le importa si la pestaña de su página web es activo y visible.

TAREAS:este método no se ocupa de dos situaciones:

Actualización

En los navegadores modernos, es posible que desee comprobar hacia fuera el Intersección de Observador de la API que ofrece las siguientes ventajas:

  • Mejor rendimiento que la escucha de los eventos de desplazamiento
  • Obras en la cruz de dominio iframes
  • Se puede saber si un elemento está obstruyendo/intersección otro

Intersección Observador está en camino de ser un completo estándar y ya está soportado en Cromo 51+, Edge 15+ y Firefox 55+ y está en desarrollo para Safari.También hay un el polyfil disponible.


Respuesta anterior

Hay algunos problemas con el la respuesta proporcionada por Dan que podría hacer un inadecuado enfoque para algunas situaciones.Algunos de estos problemas se señaló en su respuesta, cerca de la parte inferior, que su código se dan falsos positivos para los elementos que son:

  • Oculto por otro elemento en frente de uno que está siendo probado
  • Fuera de la zona visible de un padre o de un elemento antecesor
  • Un elemento o de sus hijos oculta usando el CSS clip la propiedad

Estas limitaciones se muestra en los siguientes resultados de un prueba simple:

Failed test, using isElementInViewport

La solución: isElementVisible()

Aquí tienes la solución a esos problemas, con el resultado de la prueba a continuación y una explicación de algunas partes del código.

function isElementVisible(el) {
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || doc.documentElement.clientWidth,
        vHeight  = window.innerHeight || doc.documentElement.clientHeight,
        efp      = function (x, y) { return document.elementFromPoint(x, y) };     

    // Return false if it's not in the viewport
    if (rect.right < 0 || rect.bottom < 0 
            || rect.left > vWidth || rect.top > vHeight)
        return false;

    // Return true if any of its four corners are visible
    return (
          el.contains(efp(rect.left,  rect.top))
      ||  el.contains(efp(rect.right, rect.top))
      ||  el.contains(efp(rect.right, rect.bottom))
      ||  el.contains(efp(rect.left,  rect.bottom))
    );
}

La prueba que pasa: http://jsfiddle.net/AndyE/cAY8c/

Y el resultado:

Passed test, using isElementVisible

Notas adicionales

Este método no está exento de limitaciones, sin embargo.Por ejemplo, un elemento que está siendo probado con un menor índice z de otro elemento en el mismo lugar sería identificado como oculta, incluso si el elemento frontal no esconden ninguna parte de ella.Sin embargo, este método tiene sus usos, en algunos casos, que Dan la solución no cubre.

Ambos element.getBoundingClientRect() y document.elementFromPoint() son parte de la CSSOM Borrador de Trabajo de la especificación y se apoyó en al menos el IE 6 y posteriores, y la mayoría de los los navegadores de escritorio durante mucho tiempo (aunque no perfectamente).Ver Quirksmode en estas funciones para obtener más información.

contains() se utiliza para ver si el elemento devuelto por document.elementFromPoint() es un nodo hijo del elemento que estamos probando para la visibilidad.También devuelve true si el elemento devuelto es el mismo elemento.Esto sólo hace que el cheque sea más robusto.Es compatible en todos los principales navegadores, Firefox 9.0 siendo el último de ellos para agregarlo.Para mayor compatibilidad con Firefox, comprobar esta respuesta la historia.

Si quieres probar más puntos alrededor del elemento de visibilidad, es decir, asegúrese de que el elemento no está cubierto por más de, digamos, 50%―no tardaría mucho para ajustar la última parte de la respuesta.Sin embargo, ser conscientes de que probablemente sería muy lento si se revisan cada píxel para asegurarse de que era 100% visible.

He intentado Dan la respuesta sin embargo el álgebra se utiliza para determinar los límites significa que el elemento debe ser ≤ el tamaño de la ventana y completamente dentro de el área de visualización para obtener true, fácilmente conduce a falsos negativos.Si desea determinar si un elemento está en la vista en todos, ryanve la respuesta está cerca, pero el elemento que está siendo probado debe superponerse con el de la ventanilla, intente esto:

function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();

    return rect.bottom > 0 &&
        rect.right > 0 &&
        rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ &&
        rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;
}

Como un servicio público:
Dan respuesta con los cálculos correctos (elemento puede ser > de la ventana, especialmente en las pantallas de teléfono móvil), y corregir jQuery pruebas, así como la adición de isElementPartiallyInViewport:

Por el camino, la la diferencia entre la ventana.innerWidth y documento.documentElement.clientWidth es que clientWidth/clientHeight no incluye la barra de desplazamiento, mientras que la ventana.innerWidth/Altura hace.

function isElementPartiallyInViewport(el)
{
    //special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

    var rect = el.getBoundingClientRect();
    // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
    var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
    var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

    return (vertInView && horInView);
}


// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el) 
{
    //special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

    var rect = el.getBoundingClientRect();
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    return (
           (rect.left >= 0)
        && (rect.top >= 0)
        && ((rect.left + rect.width) <= windowWidth)
        && ((rect.top + rect.height) <= windowHeight)
    );

}


function fnIsVis(ele)
{
    var inVpFull = isElementInViewport(ele);
    var inVpPartial = isElementPartiallyInViewport(ele);
    console.clear();
    console.log("Fully in viewport: " + inVpFull);
    console.log("Partially in viewport: " + inVpPartial);
}

Caso de prueba

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Test</title>
    <!--
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>    
    <script src="scrollMonitor.js"></script>
    -->

    <script type="text/javascript">

        function isElementPartiallyInViewport(el)
        {
            //special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

            var rect = el.getBoundingClientRect();
            // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
            var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
            var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

            return (vertInView && horInView);
        }


        // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
        function isElementInViewport (el) 
        {
            //special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];


            var rect = el.getBoundingClientRect();
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            return (
                   (rect.left >= 0)
                && (rect.top >= 0)
                && ((rect.left + rect.width) <= windowWidth)
                && ((rect.top + rect.height) <= windowHeight)
            );

        }


        function fnIsVis(ele)
        {
            var inVpFull = isElementInViewport(ele);
            var inVpPartial = isElementPartiallyInViewport(ele);
            console.clear();
            console.log("Fully in viewport: " + inVpFull);
            console.log("Partially in viewport: " + inVpPartial);
        }


        // var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft,
        // var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

    </script>

</head>
<body>

    <div style="display: block; width: 2000px; height: 10000px; background-color: green;">

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <div style="background-color: crimson; display: inline-block; width: 800px; height: 500px;" ></div>
        <div id="myele" onclick="fnIsVis(this);" style="display: inline-block; width: 100px; height: 100px; background-color: hotpink;">
        t
        </div>

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

    </div>

    <!--
    <script type="text/javascript">

        var element = document.getElementById("myele");
        var watcher = scrollMonitor.create( element );

        watcher.lock();

        watcher.stateChange(function() {
            console.log("state changed");
            // $(element).toggleClass('fixed', this.isAboveViewport)
        });

    </script>
    -->
</body>
</html>

No hay plugin de jQuery llamado inview que hace el trabajo

Ver la fuente de punto, que utiliza getBoundingClientRect.Es como:

function inViewport (el) {

    var r, html;
    if ( !el || 1 !== el.nodeType ) { return false; }
    html = document.documentElement;
    r = el.getBoundingClientRect();

    return ( !!r 
      && r.bottom >= 0 
      && r.right >= 0 
      && r.top <= html.clientHeight 
      && r.left <= html.clientWidth 
    );

}

Devuelve true si cualquier la parte del elemento que está en la ventanilla.

mi más corta y más rápida versión.

function isElementOutViewport(el){
    var rect = el.getBoundingClientRect();
    return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}

agregar jsFiddle como se requiere https://jsfiddle.net/on1g619L/1/

Me pareció preocupante que no había un jQuery centrado en la versión de la funcionalidad disponible.Cuando me llegó a través de Dan la solución me fijé en la oportunidad de ofrecer algo para la gente que le gusta programar en el jQuery OO estilo.Asegúrese de desplazarse arriba y dejar un upvote en Dan el código.Su agradable y ágil y funciona como un encanto para mí.

bada bing, bada boom

$.fn.inView = function(){
    if(!this.length) return false;
    var rect = this.get(0).getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );

};

//additional examples for other use cases
//true false whether an array of elements are all in view
$.fn.allInView = function(){
    var all = [];
    this.forEach(function(){
        all.push( $(this).inView() );
    });
    return all.indexOf(false) === -1;
};

//only the class elements in view
$('.some-class').filter(function(){
    return $(this).inView();
});

//only the class elements not in view
$('.some-class').filter(function(){
    return !$(this).inView();
});

el uso de

$(window).on('scroll',function(){ 

    if( $('footer').inView() ) {
        // do cool stuff
    }

});

Me parece que el aceptado respuesta en este caso es demasiado complicado para la mayoría de los casos de uso.Este código hace bien el trabajo (con JQuery) y la diferencia entre completamente visible y parcialmente visibles de los elementos.

var element         = $("#element");
var topOfElement    = element.offset().top;
var bottomOfElement = element.offset().top + element.outerHeight(true);
var $window         = $(window);

$window.bind('scroll', function() {

    var scrollTopPosition   = $window.scrollTop()+$window.height();
    var windowScrollTop     = $window.scrollTop()

    if( windowScrollTop > topOfElement && windowScrollTop < bottomOfElement) {
       // Element is partially visible (above viewable area)
       console.log("Element is partially visible (above viewable area)");

    }else if( windowScrollTop > bottomOfElement && windowScrollTop > topOfElement ) {
        // Element is hidden (above viewable area)
       console.log("Element is hidden (above viewable area)");

    }else if( scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement ) {
        // Element is hidden (below viewable area)
        console.log("Element is hidden (below viewable area)");

    }else if( scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement ) {
        // Element is partially visible (below viewable area)
        console.log("Element is partially visible (below viewable area)");

    }else{
        // Element is completely visible
        console.log("Element is completely visible");
    }
});

Todas las respuestas que he encontrado aquí sólo comprobar si el elemento es se coloca dentro de la vista actual.Pero que no significa que sea visible.
Lo que si el elemento que está dentro de un div con el desborde de contenido, y se desplaza fuera de la vista?

Para solucionar esto, habría que revisar si el elemento está contenido por todos los padres.
Mi solución hace exactamente eso:

También le permite especificar el elemento tiene que ser visible.

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

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

Esta solución ignorado el hecho de que los elementos pueden no ser visibles debido a otros hechos, como opacity: 0.

He probado esta solución en Chrome e Internet Explorer 11.

El nuevo Intersección de Observador de la API aborda esta cuestión de forma muy directa.

Esta solución tendrá un polyfill como Safari, Opera e IE no soporta esto todavía.(el relleno está incluido en la solución).

En esta solución, hay una casilla fuera de la vista, que es el destino (observado).Cuando se llega a la vista, el botón en la parte superior de la cabecera está oculto.Se muestra una vez que el cuadro de las hojas de la vista.

const buttonToHide = document.querySelector('button');

const hideWhenBoxInView = new IntersectionObserver((entries) => {
  if (entries[0].intersectionRatio <= 0) { // If not in view
    buttonToHide.style.display = "inherit";
  } else {
    buttonToHide.style.display = "none";
  }
});

hideWhenBoxInView.observe(document.getElementById('box'));
header {
  position: fixed;
  top: 0;
  width: 100vw;
  height: 30px;
  background-color: lightgreen;
}

.wrapper {
  position: relative;
  margin-top: 600px;
}

#box {
  position: relative;
  left: 175px;
  width: 150px;
  height: 135px;
  background-color: lightblue;
  border: 2px solid;
}
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<header>
  <button>NAVIGATION BUTTON TO HIDE</button>
</header>
  <div class="wrapper">
    <div id="box">
    </div>
  </div>

Creo que esta es una más funcional manera de hacerlo.El Dan la respuesta no funcionan en recursiva contexto.

Esta función de resolver el problema cuando el elemento que está dentro de otros desplazable divs por pruebas de niveles de forma recursiva superior a la etiqueta HTML, y se detiene en la primera falsa.

/**
 * fullVisible=true only returns true if the all object rect is visible
 */
function isReallyVisible(el, fullVisible) {
    if ( el.tagName == "HTML" )
            return true;
    var parentRect=el.parentNode.getBoundingClientRect();
    var rect = arguments[2] || el.getBoundingClientRect();
    return (
            ( fullVisible ? rect.top    >= parentRect.top    : rect.bottom > parentRect.top ) &&
            ( fullVisible ? rect.left   >= parentRect.left   : rect.right  > parentRect.left ) &&
            ( fullVisible ? rect.bottom <= parentRect.bottom : rect.top    < parentRect.bottom ) &&
            ( fullVisible ? rect.right  <= parentRect.right  : rect.left   < parentRect.right ) &&
            isReallyVisible(el.parentNode, fullVisible, rect)
    );
};

Basado en @dan la solución anterior (https://stackoverflow.com/a/7557433/5628), Tenía un ir en la limpieza de la aplicación para que el uso de varias veces en la misma página es más fácil:

$(function() {

  $(window).on('load resize scroll', function() {
    addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon');
    addClassToElementInViewport($('.another-thing'), 'animate-thing');
    // 👏 repeat as needed ...
  });

  function addClassToElementInViewport(element, newClass) {
    if (inViewport(element)) {
      element.addClass(newClass);
    }
  }

  function inViewport(element) {
    if (typeof jQuery === "function" && element instanceof jQuery) {
      element = element[0];
    }
    var elementBounds = element.getBoundingClientRect();
    return (
      elementBounds.top >= 0 &&
      elementBounds.left >= 0 &&
      elementBounds.bottom <= $(window).height() &&
      elementBounds.right <= $(window).width()
    );
  }

});

La manera en que yo estoy usando es que cuando el elemento se desplaza a la vista, voy a agregar una clase que desencadena un css fotograma de la animación.Es bastante sencillo y funciona especialmente bien cuando tienes como 10+ cosas de forma condicional animar en una página.

Espero que ayude!

Aquí está mi solución, va a trabajar si un elemento está oculto en el interior de un desplazamiento capaz de contenedor.

Aquí un demo (intente cambiar el tamaño de la ventana)

var visibleY = function(el){
    var top = el.getBoundingClientRect().top, rect, el = el.parentNode;
    do {
        rect = el.getBoundingClientRect();
        if (top <= rect.bottom === false)
            return false;
        el = el.parentNode;
    } while (el != document.body);
    // Check its within the document viewport
    return top <= document.documentElement.clientHeight;
};

Sólo necesitaba comprobar si es visible en el eje Y (para un desplazamiento ajax carga más característica de registros).

La fácil y pequeña solución que ha funcionado para mí.

Ejemplo Quieres ver si el elemento es visible en el elemento principal que ha de desbordamiento de desplazamiento.

$(window).on('scroll', function () {  

     var container = $('#sidebar');
     var containerHeight = container.height();
     var scrollPosition = $('#row1').offset().top - container.offset().top;

     if (containerHeight < scrollPosition) {
         console.log('not visible');
     } else {
         console.log('visible');
     }
})

Una solución mejor:

function getViewportSize(w) {
    var w = w || window;
    if(w.innerWidth != null) return {w:w.innerWidth, h:w.innerHeight};
    var d = w.document;
    if (document.compatMode == "CSS1Compat") {
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    }
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}
function isViewportVisible(e) {
    var box = e.getBoundingClientRect();
    var height = box.height || (box.bottom - box.top);
    var width = box.width || (box.right - box.left);
    var viewport = getViewportSize();
    if(!height || !width) return false;
    if(box.top > viewport.h || box.bottom < 0) return false;
    if(box.right < 0 || box.left > viewport.w) return false;
    return true;    
}

Comprueba si el elemento es, al menos parcialmente, en la vista (dimensión vertical):

function inView(element) {
                var box = element.getBoundingClientRect();
                return inViewBox(box);
}

function inViewBox(box) {
                return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true;
}


function getWindowSize() { 
        return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight} 
}

Yo tenía la misma pregunta y pensé que mediante el uso de getBoundingClientRect().Este código es completamente 'genérico' y sólo tiene que ser escrita de una vez para que funcione (usted no tiene que escribir para cada elemento que usted quiere saber es en el área de visualización).Este código sólo se comprueba a ver si es vertical en el área de visualización no horizontal.En este caso, la variable (matriz) 'elementos' tiene todos los elementos que son la comprobación de forma vertical en la ventanilla, así que coge todos los elementos que desee en cualquier lugar y almacenarlos allí.El 'bucle', bucles a través de cada elemento y se comprueba si está verticalmente en el área de visualización.Este código se ejecuta cada vez el usuario se desplaza!Si el getBoudingClientRect().la parte superior está a menos de 3/4 de la vista (el elemento es un cuarto en el viewport), que se registra como 'en la ventana'.Puesto que el código es genérico, usted va a querer saber 'que' es el elemento en el área de visualización.Para averiguarlo, se puede determinar mediante el atributo personalizado, nombre de nodo, id, nombre de clase, y más.Aquí está mi código (dime si no funciona, que ha sido probado en IE 11, FireFox 40.0.3, Chrome Versión 45.0.2454.85 m, Ópera 31.0.1889.174, y el Borde con Windows 10, [no Safari sin embargo,])...

//scrolling handlers...
window.onscroll = function(){
  var elements = document.getElementById('whatever').getElementsByClassName('whatever');
  for(var i = 0; i != elements.length; i++)
  {
   if(elements[i].getBoundingClientRect().top <= window.innerHeight*0.75 && elements[i].getBoundingClientRect().top > 0)
   {
      console.log(elements[i].nodeName + ' ' + elements[i].className + ' ' + elements[i].id + ' is in the viewport; proceed with whatever code you want to do here.');
   }
};

Espero que esto ayude a alguien :-)

Aquí es una función que indica si un elemento está visible en la vista actual de un los padres elemento:

function inParentViewport(el, pa) {
    if (typeof jQuery === "function"){
        if (el instanceof jQuery)
            el = el[0];
        if (pa instanceof jQuery)
            pa = pa[0];
    }

    var e = el.getBoundingClientRect();
    var p = pa.getBoundingClientRect();

    return (
        e.bottom >= p.top &&
        e.right >= p.left &&
        e.top <= p.bottom &&
        e.left <= p.right
    );
}

Como sencillo, ya que puede llegar IMO:

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  return Math.abs(coords.top) <= coords.height;
}

Puedo usar esta función (sólo comprueba si el y es inscreen ya que la mayoría de las veces el x no es necesario)

function elementInViewport(el) {
    var elinfo = {
        "top":el.offsetTop,
        "height":el.offsetHeight,
    };

    if (elinfo.top + elinfo.height < window.pageYOffset || elinfo.top > window.pageYOffset + window.innerHeight) {
        return false;
    } else {
        return true;
    }

}

Para un desafío similar realmente disfruté esta esencia lo que expone a un polyfill para scrollIntoViewIfNeeded().

Todo lo necesario Kung Fu necesaria para responder es dentro de este bloque:

var parent = this.parentNode,
    parentComputedStyle = window.getComputedStyle(parent, null),
    parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
    parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
    overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
    overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
    overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
    overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
    alignWithTop = overTop && !overBottom;

this se refiere al elemento que desea saber si es decir, overTop o overBottom - sólo debe obtener la deriva...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top