¿Hay una manera eficiente 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).
¿Hay una manera eficiente 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).
Respuestas:
Actualización: el tiempo avanza y también nuestros navegadores. Esta técnica ya no se recomienda y debe usar la solución de Dan si no necesita admitir la versión de Internet Explorer anterior a la 7.
Solución original (ahora obsoleta):
Esto verificará 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)
);
}
Puede modificar esto simplemente para determinar si alguna parte del elemento está 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
);
}
getBoundingClientRect
específicamente con el propósito de encontrar coordenadas de elementos ... ¿Por qué no lo usaríamos?
Ahora la mayoría de los navegadores admiten el método getBoundingClientRect , que se ha convertido en la mejor práctica. Usar una respuesta anterior es muy lento , no es preciso y tiene varios errores .
La solución seleccionada como correcta casi nunca es precisa . Puedes leer más sobre sus errores.
Esta solución se probó en Internet Explorer 7 (y posterior), iOS 5 (y posterior) Safari, Android 2.0 (Eclair) y posterior, 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() */
);
}
Puede estar seguro de que la función dada anteriormente devuelve la respuesta correcta en el momento en que se llama, pero ¿qué pasa con el seguimiento de la visibilidad del elemento como 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 modificaciones DOM, pueden cambiar la visibilidad de su elemento, por supuesto.
Pautas y dificultades comunes:
¿Quizás necesite rastrear el zoom de la página / pellizco del dispositivo móvil? jQuery debería manejar el navegador de zoom / pellizco cruzado, de lo contrario, el primer o segundo enlace debería ayudarlo.
Si modifica DOM , puede afectar la visibilidad del elemento. Debe tomar el control sobre eso y llamar handler()
manualmente. Desafortunadamente, no tenemos ningún onrepaint
evento de navegador cruzado . Por otro lado, eso nos permite hacer optimizaciones y volver a verificar solo las modificaciones DOM que pueden cambiar la visibilidad de un elemento.
Nunca lo use 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 que se coloca en un servidor remoto fallará.
Después de DOMContentLoaded
disparar, se aplican estilos , pero las imágenes aún no se cargan . Por lo tanto, deberíamos agregar window.onload
escucha de eventos.
Todavía no podemos ver el evento zoom / pellizco.
El último recurso podría ser el siguiente código:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Puede usar la increíble página de características Visibilidad de la API HTML5 si le importa si la pestaña de su página web está activa y visible.
TODO: este método no maneja dos situaciones:
z-index
overflow-scroll
en el contenedor del elementoreturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))
) y no el objeto jQuery (por ejemplo, isElementInViewport($("#elem))
). El equivalente jQuery es agregar [0]
este modo: isElementInViewport($("#elem)[0])
.
el is not defined
En los navegadores modernos, es posible que desee consultar la API Intersection Observer que ofrece los siguientes beneficios:
Intersection Observer está en camino de convertirse en un estándar completo y ya es compatible con Chrome 51+, Edge 15+ y Firefox 55+ y está en desarrollo para Safari. También hay un polyfill disponible.
Hay algunos problemas con la respuesta proporcionada por Dan que podrían hacer que sea un enfoque inadecuado para algunas situaciones. Algunos de estos problemas se señalan en su respuesta cerca del final, que su código dará falsos positivos para elementos que son:
clip
propiedad CSSEstas limitaciones se demuestran en los siguientes resultados de una prueba simple :
isElementVisible()
Aquí hay una 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 || document.documentElement.clientWidth,
vHeight = window.innerHeight || document.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))
);
}
Prueba de aprobación: http://jsfiddle.net/AndyE/cAY8c/
Y el resultado:
Sin embargo, este método no está exento de limitaciones. Por ejemplo, un elemento que se está probando con un índice z más bajo que otro elemento en la misma ubicación se identificaría como oculto incluso si el elemento en el frente en realidad no oculta ninguna parte de él. Aún así, este método tiene sus usos en algunos casos que la solución de Dan no cubre.
Ambos element.getBoundingClientRect()
y document.elementFromPoint()
son parte de la especificación del Borrador de trabajo CSSOM y son compatibles con al menos IE 6 y posteriores y la mayoría de los navegadores de escritorio durante mucho tiempo (aunque no de manera perfecta). Consulte Quirksmode sobre estas funciones para obtener más información.
contains()
se usa para ver si el elemento devuelto por document.elementFromPoint()
es un nodo hijo del elemento que estamos probando para la visibilidad. También devuelve verdadero si el elemento devuelto es el mismo elemento. Esto solo hace que el cheque sea más robusto. Es compatible con todos los principales navegadores, siendo Firefox 9.0 el último de ellos en agregarlo. Para el soporte anterior de Firefox, revise el historial de esta respuesta.
Si desea probar la visibilidad de más puntos alrededor del elemento, es decir, para asegurarse de que el elemento no esté cubierto por más de, digamos, 50%, no se necesitaría mucho para ajustar la última parte de la respuesta. Sin embargo, tenga en cuenta que probablemente sería muy lento si verificara cada píxel para asegurarse de que fuera 100% visible.
doc
como alias document
. Sí, me gusta pensar en esto como una solución decente para los casos extremos.
element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Intenté la respuesta de Dan . Sin embargo , el álgebra utilizada para determinar los límites significa que el elemento debe ser tanto ≤ el tamaño de la ventana gráfica como completamente dentro de la ventana gráfica para obtener true
, lo que lleva fácilmente a falsos negativos. Si desea determinar si un elemento está en la ventana gráfica , la respuesta de ryanve es cercana, pero el elemento que se está probando debe superponerse a la ventana gráfica, así que 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 servicio público:
la respuesta de Dan con los cálculos correctos (el elemento puede ser> ventana, especialmente en las pantallas de teléfonos móviles) y las pruebas jQuery correctas, además de agregar isElementPartiallyInViewport:
Por cierto, la diferencia entre window.innerWidth y document.documentElement.clientWidth es que clientWidth / clientHeight no incluye la barra de desplazamiento, mientras que window.innerWidth / Height sí.
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);
}
<!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>
isElementPartiallyInViewport
También es muy útil. Buena esa.
Vea la fuente de verge , que usa 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
);
}
Se devuelve true
si alguna parte del elemento está en la ventana gráfica.
Mi versión más corta y más rápida:
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
Y un jsFiddle según sea necesario: https://jsfiddle.net/on1g619L/1/
Me pareció preocupante que no hubiera una versión jQuery de la funcionalidad disponible. Cuando me encontré con la solución de Dan, descubrí la oportunidad de proporcionar algo para las personas que les gusta programar en el estilo jQuery OO. Es 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
// Is 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();
});
Uso
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// Do cool stuff
}
});
La nueva API de Intersection Observer aborda esta pregunta muy directamente.
Esta solución necesitará un polyfill ya que Safari, Opera e Internet Explorer aún no lo admiten (el polyfill está incluido en la solución).
En esta solución, hay un cuadro fuera de la vista que es el objetivo (observado). Cuando aparece, el botón en la parte superior del encabezado está oculto. Se muestra una vez que el cuadro abandona 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>
<!DOCTYPE html>
al HTML
IntersectionObserver
es una característica experimental (que puede cambiar en el futuro).
IntersectionObserver
solo activa la devolución de llamada después del movimiento del objetivo en relación con la raíz.
observe
evento de llamada , se le informa de inmediato el estado actual de intersección del elemento rastreado. Entonces, de alguna manera, se dirige.
Todas las respuestas que he encontrado aquí solo verifican si el elemento está posicionado dentro de la ventana gráfica actual . Pero eso no significa que sea visible .
¿Qué pasa si el elemento dado está dentro de un div con contenido desbordado y se desplaza fuera de la vista?
Para resolver eso, tendría que verificar si el elemento está contenido por todos los padres.
Mi solución hace exactamente eso:
También le permite especificar cuánto del 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 ignoró 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.
Encuentro que la respuesta aceptada aquí es demasiado complicada para la mayoría de los casos de uso. Este código funciona bien (usando jQuery) y diferencia entre elementos totalmente visibles y parcialmente visibles:
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");
}
});
$window = $(window)
en caché fuera del controlador de desplazamiento.
La solución más simple como el soporte de Element.getBoundingClientRect () se ha vuelto perfecta :
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
Creo que esta es una forma más funcional de hacerlo. La respuesta de Dan no funciona en un contexto recursivo.
Esta función resuelve el problema cuando su elemento está dentro de otros divs desplazables probando cualquier nivel recursivamente hasta la etiqueta HTML, y se detiene en el primer falso.
/**
* 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)
);
};
Las respuestas más aceptadas no funcionan al hacer zoom en Google Chrome en Android. En combinación con la respuesta de Dan , para tener en cuenta Chrome en Android, se debe usar visualViewport . El siguiente ejemplo solo tiene en cuenta la verificación vertical y usa jQuery para la altura de la ventana:
var Rect = YOUR_ELEMENT.getBoundingClientRect();
var ElTop = Rect.top, ElBottom = Rect.bottom;
var WindowHeight = $(window).height();
if(window.visualViewport) {
ElTop -= window.visualViewport.offsetTop;
ElBottom -= window.visualViewport.offsetTop;
WindowHeight = window.visualViewport.height;
}
var WithinScreen = (ElTop >= 0 && ElBottom <= WindowHeight);
Aquí está mi solución. Funcionará si un elemento está oculto dentro de un contenedor desplazable.
Aquí hay una demostración (intenta 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 it's within the document viewport
return top <= document.documentElement.clientHeight;
};
Solo necesitaba verificar si está visible en el eje Y (para una función de desplazamiento de carga más registros Ajax).
Basado en la solución de dan , tuve la oportunidad de limpiar la implementación para que sea más fácil usarla varias veces en la misma página:
$(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 forma en que lo uso es que cuando el elemento se desplaza a la vista, agrego una clase que desencadena una animación de fotograma clave CSS. Es bastante sencillo y funciona especialmente bien cuando tienes más de 10 cosas para animar condicionalmente en una página.
$window = $(window)
en caché fuera del controlador de desplazamiento
La mayoría de los usos en las respuestas anteriores fallan en estos puntos:
-Cuando cualquier píxel de un elemento es visible, pero no " una esquina ",
-Cuando un elemento es más grande que la vista y centrado ,
-La mayoría de ellos están buscando solo un elemento singular dentro de un documento o ventana .
Bueno, para todos estos problemas tengo una solución y los aspectos positivos son:
-Puedes regresar
visible
cuando solo aparece un píxel de cualquier lado y no es una esquina,-Puedes regresar
visible
mientras el elemento sea más grande que la ventana gráfica-Puedes elegir tu
parent element
o puedes dejar que elija automáticamente,-Trabaja en elementos añadidos dinámicamente también.
Si marca los fragmentos a continuación, verá que la diferencia en el uso overflow-scroll
del contenedor del elemento no causará ningún problema y verá que, a diferencia de otras respuestas aquí, incluso si aparece un píxel desde cualquier lado o cuando un elemento es más grande que la ventana gráfica y estamos viendo el interior píxeles del elemento que aún funciona.
El uso es simple:
// For checking element visibility from any sides
isVisible(element)
// For checking elements visibility in a parent you would like to check
var parent = document; // Assuming you check if 'element' inside 'document'
isVisible(element, parent)
// For checking elements visibility even if it's bigger than viewport
isVisible(element, null, true) // Without parent choice
isVisible(element, parent, true) // With parent choice
Una demostración sin la crossSearchAlgorithm
cual es útil para elementos más grandes que el elemento de verificación de la ventana de visualización3 píxeles internos para ver:
Verá, cuando está dentro del elemento3 , no puede decir si es visible o no, porque solo estamos verificando si el elemento es visible desde los lados o las esquinas .
Y este incluye el crossSearchAlgorithm
que le permite regresar visible
cuando el elemento es más grande que la ventana gráfica:
JSFiddle para jugar: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Este código está hecho para obtener información más precisa si alguna parte del elemento se muestra en la vista o no. Para opciones de rendimiento o solo diapositivas verticales, ¡no use esto! Este código es más efectivo en casos de dibujo.
Una mejor solución:
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;
}
Aquí hay una función que indica si un elemento está visible en la ventana gráfica actual de un elemento padre :
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
);
}
Esto verifica si un elemento está al menos parcialmente a 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}
}
Tuve la misma pregunta y la resolví usando getBoundingClientRect ().
Este código es completamente 'genérico' y solo tiene que escribirse una vez para que funcione (no tiene que escribirlo para cada elemento que desea saber que está en la ventana gráfica).
Este código solo verifica si está verticalmente en la ventana gráfica, no horizontalmente . En este caso, la variable (matriz) 'elementos' contiene todos los elementos que está verificando que estén verticalmente en la ventana gráfica, así que tome los elementos que desee en cualquier lugar y guárdelos allí.
El 'bucle for', recorre cada elemento y comprueba si está verticalmente en la ventana gráfica. ¡Este código se ejecuta cada vez que el usuario se desplaza! Si getBoudingClientRect (). Top es inferior a 3/4 de la ventana gráfica (el elemento es un cuarto en la ventana gráfica), se registra como 'en la ventana gráfica'.
Como el código es genérico, querrá saber "qué" elemento está en la ventana gráfica. Para descubrirlo, puede determinarlo por atributo personalizado, nombre de nodo, id, nombre de clase y más.
Aquí está mi código (dime si no funciona; se ha probado en Internet Explorer 11, Firefox 40.0.3, Chrome Versión 45.0.2454.85 m, Opera 31.0.1889.174 y Edge con Windows 10, [aún no Safari ]) ...
// 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.');
}
};
Esta es la solución fácil y pequeña que me ha funcionado.
Ejemplo : desea ver si el elemento está visible en el elemento principal que tiene un desplazamiento de desbordamiento.
$(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');
}
})
Todas las respuestas aquí determinan si el elemento está completamente contenido dentro de la ventana gráfica, no solo visible de alguna manera. Por ejemplo, si solo se ve la mitad de una imagen en la parte inferior de la vista, las soluciones aquí fallarán, considerando que "afuera".
Tuve un caso de uso en el que realizo una carga diferida IntersectionObserver
, pero debido a las animaciones que ocurren durante el pop-in, no quería observar ninguna imagen que ya estuviera intersectada en la carga de la página. Para hacer eso, utilicé el siguiente código:
const bounding = el.getBoundingClientRect();
const isVisible = (0 < bounding.top && bounding.top < (window.innerHeight || document.documentElement.clientHeight)) ||
(0 < bounding.bottom && bounding.bottom < (window.innerHeight || document.documentElement.clientHeight));
Básicamente, esto es verificar si el límite superior o inferior está independientemente en la ventana gráfica. El extremo opuesto puede estar afuera, pero mientras un extremo esté adentro, es "visible" al menos parcialmente.
Uso esta función (solo comprueba si la y está en pantalla, ya que la mayoría de las veces la x no es necesaria)
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 que expone un polyfill para scrollIntoViewIfNeeded () .
Todo el Kung Fu necesario para responder está 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, por ejemplo, overTop
o overBottom
simplemente debe obtener la deriva ...