Como saber se um elemento DOM é visível na janela de visualização atual?
-
02-07-2019 - |
Pergunta
Existe uma maneira eficiente para dizer se um elemento DOM (em um documento HTML) é visível no momento (aparece no janela )?
(A questão refere-se a Firefox)
Solução
Update: O tempo marcha sobre e por isso têm os nossos navegadores. Esta técnica não é mais recomendado e você deve usar @ solução de Dan abaixo ( https://stackoverflow.com/ a / 7557433/5628 ) se você não precisa apoiar IE <7.
solução original (agora ultrapassado):
Este irá verificar se o elemento é completamente visível na janela de visualização atual:
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)
);
}
Você pode modificar este simplesmente para determinar se qualquer parte do elemento é visível na janela de exibição:
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
);
}
Outras dicas
Agora maioria dos navegadores apoio getBoundingClientRect método, que se tornou a melhor prática. Usando uma velha resposta é muito lento, não precisa e tem vários bugs .
A solução selecionada como correta é quase nunca precisa . Você pode leia mais sobre seus erros.
Esta solução foi testada em IE7 +, iOS5 + Safari, Android2 +, Blackberry, Opera Mobile, e 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() */
);
}
Como usar:
Você pode ter certeza que a função dada retornos acima da resposta correta no momento do tempo em que ele é chamado, mas o que sobre o acompanhamento de visibilidade do elemento como um evento?
Coloque o seguinte código na parte inferior de sua tag <body>
:
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);
}
*/
Se você fizer qualquer modificação DOM, eles podem mudar a visibilidade do seu elemento é claro.
Diretrizes e armadilhas comuns:
Talvez você precisa controlar zoom de página / pitada dispositivo móvel? jQuery deve lidar com zoom / beliscar cross browser, caso contrário primeira ou second link deve ajudá-lo.
Se você Modificar DOM , pode afetar a visibilidade do elemento. Você deve ter controle sobre isso e chamada handler()
manualmente. Infelizmente, não temos nenhum evento onrepaint
cross browser. Por outro lado, que nos permite fazer otimizações e realizar re-verificar apenas em modificações DOM que podem mudar a visibilidade do elemento.
Never Ever usá-lo dentro jQuery $ (document) .ready () única, porque não há CSS garantia foi aplicado neste momento . Seu código pode trabalhar localmente com o seu CSS no disco rígido, mas uma vez posto em servidor remoto irá falhar.
Depois de DOMContentLoaded
é disparado, são aplicados , mas the imagens não são carregados ainda . Então, devemos acrescentar window.onload
evento ouvinte.
Não podemos pegar zoom / beliscar evento.
O último recurso poderia ser o seguinte código:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Você pode usar o recurso incrível pageVisibiliy HTML5 API se você se importa se a guia com a sua página web é activa e visível.
TODO: este método não lidar com duas situações:
- sobreposição usando
z-index
-
usando
overflow-scroll
no recipiente do elemento -
Tente algo novo https://pawelgrzybek.com/the-intersection -observer-api-explicada /
Atualização
Em navegadores modernos, que você pode querer verificar o Intersection Observer API que proporciona os seguintes benefícios:
- Melhor desempenho do que escuta para eventos de rolagem
- Obras em domínio cruzado iframe
- Pode dizer-se um elemento está obstruindo / interseção outra
Intersection Observer está a caminho de ser um padrão de pleno direito e já é suportado no Chrome 51+, 15+ Borda e Firefox 55+ e está em desenvolvimento para o Safari. Há também um polyfill disponível.
resposta anterior
Existem alguns problemas com a resposta fornecido por Dan que pode torná-lo uma abordagem inadequada para algumas situações. Algumas destas questões são apontadas em sua resposta na parte inferior, que seu código vai dar falsos positivos para elementos que são:
- escondida por um outro elemento em frente do que está sendo testado
- Fora da área visível de um pai ou ancestral elemento
- Um elemento ou seus filhos escondidos usando a propriedade CSS
clip
Estas limitações são demonstrados nas seguintes resultados de uma simples teste:
A solução: isElementVisible()
Aqui está uma solução para esses problemas, com o resultado do teste abaixo e uma explicação de algumas partes do 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))
);
}
Passando teste: http://jsfiddle.net/AndyE/cAY8c/
E o resultado:
Notas adicionais
Este método não é sem suas próprias limitações, no entanto. Por exemplo, um elemento a ser testado com um menor índice z do que o outro elemento na mesma localização seriam identificados como oculto, mesmo se o elemento em frente na verdade não ocultar qualquer parte do mesmo. Ainda assim, este método tem seus usos em alguns casos que a solução da Dan não cobre.
Ambos element.getBoundingClientRect()
e document.elementFromPoint()
fazem parte do Projeto de especificação CSSOM de Trabalho e são apoiados em pelo menos IE 6 e posterior e mais navegadores de desktop por um longo tempo (embora não perfeitamente). Consulte Quirksmode sobre estas funções para mais informações.
contains()
é usado para ver se o elemento retornado por document.elementFromPoint()
é um nó filho do elemento Estamos testando para a visibilidade. Ele também retorna verdadeiro se o elemento retornado é o mesmo elemento. Isso só faz a verificação mais robusto. É suportado em todos os principais navegadores, Firefox 9.0 sendo o último deles para adicioná-lo. Para mais velhos apoio Firefox, verifique história desta resposta.
Se você quiser testar mais pontos em torno do elemento de visibilidade, ou seja, para garantir que o elemento não é coberto por mais de, digamos, 50% -não seria preciso muito para ajustar a última parte da resposta . No entanto, estar ciente de que provavelmente seria muito lento se você checar a cada pixel para se certificar de que foi de 100% visível.
Eu tentei resposta de Dan no entanto álgebra usado para determinar os limites significa que o elemento deve ser tanto = o tamanho da janela e completamente dentro da janela para obter true
, facilmente levando a falsos negativos. Se você quiser determinar se um elemento está na janela de visualização de todo, de ryanve resposta está perto, mas o elemento que está sendo testado devem sobrepor-se a porta de visualização, então tente o seguinte:
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 um serviço público:
A resposta de Dan com os cálculos corretos (elemento pode ser> janela, especialmente em telas de telefones celulares), e teste jQuery correta, bem como a adição isElementPartiallyInViewport:
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);
}
Test-case
<!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>
Há jQuery plugin chamado InView que faz o trabalho
Veja a fonte de beira , que usa getBoundingClientRect . É 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
);
}
Retorna true
se qualquer parte do elemento é no visor.
minha mais curto e mais rápido versão.
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
adicionar jsFiddle conforme necessário https://jsfiddle.net/on1g619L/1/
Eu achei preocupante que não havia uma versão centrada jQuery
das funcionalidades disponíveis. Quando me deparei com solução de Dan i viu a oportunidade de oferecer algo para pessoas que gostam de programa no estilo jQuery
OO. Certifique-se de rolar para cima e deixar uma upvote no código de Dan. Seu bom e mal-humorado e funciona como um encanto para mim.
bada bing bada crescimento
$.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();
});
uso
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// do cool stuff
}
});
Eu acho que a resposta aceito aqui é demasiado complicado para a maioria dos casos de uso. Este código faz o trabalho bem (usando JQuery) e diferencia entre elementos totalmente visíveis e parcialmente visíveis.
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 as respostas que eu encontrei aqui apenas verificar se o elemento é posicionada dentro do atual janela . Mas isso não significa que seja visível .
E se o elemento de dado é dentro de uma div com transbordando de conteúdo, e é deslocado para fora de vista?
Para resolver isso, você teria que verificar se o elemento é contido por todos os pais.
Minha solução faz exatamente isso:
Ele também permite que você especifique quanto do elemento tem de ser visível.
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 solução ignorou o fato de que os elementos podem não ser visíveis devido a outros fatos, como opacity: 0
.
Eu testei essa solução no Chrome e Internet Explorer 11.
Os novos Intersection Observer API aborda esta questão muito diretamente.
Esta solução vai precisar de um polyfill como Safari, Opera e IE não suportam isso ainda. (O polyfill está incluído na solução).
Nesta solução, existe uma caixa de vista de que é o alvo (observada). Quando ele entra em vista, o botão na parte superior no cabeçalho está escondido. É mostrado uma vez que as folhas de caixa de 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>
Eu acho que esta é uma forma mais funcional para fazê-lo. A resposta do Dan não funcionam no contexto recursiva.
Esta função resolver o problema quando o seu elemento é dentro de outros divs rolagem testando quaisquer níveis de forma recursiva superior para a tag HTML, e pára no primeiro 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)
);
};
Com base @ solução de dan acima ( https://stackoverflow.com/a/7557433/5628 ), I tinha um ir para a limpeza de implementação para que usá-lo várias vezes na mesma página é mais 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()
);
}
});
A maneira que eu estou usando é que quando os pergaminhos elemento na vista, eu estou adicionando uma classe que aciona uma animação css quadro-chave. É bastante simples e funciona especialmente bem quando você tem como 10+ coisas para condicionalmente animado em uma página.
Espero que ajude!
Aqui está a minha solução, ele vai funcionar se um elemento está escondido dentro de um recipiente de rolagem-capazes.
Aqui está uma demonstração (tentativa re-dimensionar a janela para)
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;
};
Eu só precisava verificar se é visível no eixo Y (para uma carga de ajax rolagem apresentam mais registros).
A solução fácil e pequeno que tem trabalhado para mim.
Exemplo Você quer ver se o elemento é visível no elemento pai que tem estouro de rolagem.
$(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');
}
})
Uma solução melhor:
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;
}
Verifica se o elemento é, pelo menos, parcialmente em vista (dimensão 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}
}
Eu tive a mesma pergunta e percebi isso usando getBoundingClientRect (). Este código é completamente 'genérico' e só tem de ser escritos uma vez para que ele funcione (você não tem que escrever para cada elemento que você quer saber é no visor). Este código só verifica para ver se é verticalmente na janela não horizontalmente . Neste caso, a variável (matriz) 'elementos' contém todos os elementos que você está visitando a ser verticalmente na janela, então pegue quaisquer elementos que você quer em qualquer lugar e armazená-los lá. O 'loop', percorre cada elemento e verifica para ver se é verticalmente na janela de visualização. Esse código é executado sempre o usuário rola! Se o getBoudingClientRect (). Top é inferior a 3/4 do visor (o elemento é um quarto no visor), ele registra como 'na janela'. Desde que o código é genérico, você vai querer saber 'que' elemento é no visor. Para descobrir isso, você pode determinar isso por atributo personalizado, nome do nó, id, nome da classe, e muito mais. Aqui está o meu código (Diga-me se ele não funciona, ele foi testado no IE 11, FireFox 40.0.3, Chrome versão 45.0.2454.85 m, Opera 31.0.1889.174, e Edge com o Windows 10, [ainda não 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.');
}
};
Espero que isso ajude alguém: -)
Aqui está uma função que diz se um elemento está em visível na janela atual de uma pai 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 simples como ele pode ficar IMO:
function isVisible(elem) {
var coords = elem.getBoundingClientRect();
return Math.abs(coords.top) <= coords.height;
}
Eu uso essa função (ele só verifica se o y é inscreen já que a maioria das vezes o x não é necessária)
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 um desafio semelhante eu realmente gostei este essência que expõe um polyfill para scrollIntoViewIfNeeded () .
Todo o necessário Kung Fu necessário para resposta é dentro deste bloco:
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
refere-se ao elemento que você quer saber se é ou seja overTop
ou overBottom
- apenas deve começar a deriva ...