Depende do que você quer dizer com visível. Se você quer dizer que ele está atualmente exibido na página, dada a posição de rolagem, é possível calculá-lo com base nos elementos y offset e na posição atual de rolagem.
roryf 23/09/08
18
Esclarecimento: visível, como no retângulo atualmente exibido. Visível, como em não oculto ou exibido: nenhum? Visível, como não está por trás de outra coisa na ordem Z?
Adam Wright
6
como dentro do retângulo atualmente exibido. Obrigado. Reformulado.
benzaita 23/09/08
2
Observe que todas as respostas aqui fornecerão um falso positivo para itens que estão fora da área visível do elemento pai (eh com o estouro oculto), mas dentro da área da janela de visualização.
Atualização: o tempo continua e nossos navegadores também. Essa técnica não é mais recomendada e você deve usar a solução de Dan se não precisar oferecer suporte à versão do Internet Explorer anterior ao 7.
Solução original (agora desatualizada):
Isso verificará se o elemento está totalmente visível na viewport 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 isso simplesmente para determinar se alguma parte do elemento está 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
);}
A função original postada teve um erro. Necessário salvar a largura / altura antes de reatribuir ...
Prestaul
15
E se o elemento estiver em uma div rolável e sair de uma visualização ??
amartynov
2
Consulte uma versão mais recente do script abaixo
Dan
1
Também curioso sobre a pergunta de @ amartynov. Alguém sabe como simplesmente dizer se um elemento está oculto devido ao estouro de um elemento ancestral? Bônus se isso puder ser detectado, independentemente da profundidade da criança.
11138 Eric Nguyen #
1
@deadManN recorrente no DOM é notoriamente lento. Isso é motivo suficiente, mas os fornecedores de navegadores também criaram getBoundingClientRectespecificamente para o propósito de encontrar coordenadas de elemento ... Por que não o usamos?
Esta solução foi testada no Internet Explorer 7 (e posterior), iOS 5 (e posterior) Safari, Android 2.0 (Eclair) e posterior, BlackBerry, Opera Mobile e Internet Explorer Mobile 9 .
function isElementInViewport (el){// Special bonus for those using jQueryif(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 de que a função fornecida acima retorna a resposta correta no momento em que é chamada, mas e quanto à visibilidade do elemento de rastreamento como um evento?
Coloque o seguinte código na parte inferior da sua <body>tag:
function onVisibilityChange(el, callback){var old_visible;returnfunction(){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);
}
*/
Se você fizer modificações no DOM, elas poderão alterar a visibilidade do seu elemento, é claro.
Diretrizes e armadilhas comuns:
Talvez você precise acompanhar o zoom da página / pitada de dispositivo móvel? O jQuery deve lidar com o zoom / pitada de navegador, caso contrário, o primeiro ou o segundo link deve ajudá-lo.
Se você modificar o DOM , isso poderá afetar a visibilidade do elemento. Você deve assumir o controle sobre isso e ligar handler()manualmente. Infelizmente, não temos nenhum onrepaintevento entre navegadores . Por outro lado, isso nos permite fazer otimizações e executar a verificação novamente apenas nas modificações do DOM que podem alterar a visibilidade de um elemento.
Estou usando esta solução (cuidado com o erro de digitação "botom"). Também há algo para estar ciente, quando o elemento que estamos considerando tiver imagens nele. O Chrome (pelo menos) deve esperar que a imagem seja carregada para ter o valor exato para o boundingRectangle. Parece que o Firefox não tem esse "problema"
Claudio
4
Funciona quando você tem a rolagem ativada em um contêiner dentro do corpo. Por exemplo, ele não funciona aqui - agaase.github.io/webpages/demo/isonscreen2.html isElementInViewport (document.getElementById ("innerele")). innerele está presente dentro de um contêiner com a rolagem ativada.
agaase
70
Os cálculos assumem que o elemento é menor que a tela. Se tiver elementos de alta ou de largura, pode ser mais preciso usarreturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
Roonaan
11
Dica: Para aqueles que tentam implementar isso com o jQuery, apenas um lembrete amigável para passar o objeto HTML DOM (por exemplo, isElementInViewport(document.getElementById('elem'))) e não o objeto jQuery (por exemplo, isElementInViewport($("#elem))). O jQuery equivalente é adicionar [0]assim: isElementInViewport($("#elem)[0]).
Jiminy # jiminy #
11
el is not defined
M_Willett
176
Atualizar
Nos navegadores modernos, você pode conferir a API do Intersection Observer, que oferece os seguintes benefícios:
Melhor desempenho do que ouvir eventos de rolagem
Funciona em iframes entre domínios
Pode dizer se um elemento está obstruindo / cruzando outro
O Intersection Observer está a caminho de ser um padrão completo e já é suportado no Chrome 51+, Edge 15+ 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 fornecida por Dan que podem torná-la uma abordagem inadequada para algumas situações. Algumas dessas questões são apontadas em sua resposta, na parte inferior, que seu código fornecerá falsos positivos para elementos que são:
Escondido por outro elemento na frente do que está sendo testado
Fora da área visível de um elemento pai ou ancestral
Um elemento ou seus filhos ocultos usando a clippropriedade CSS
Essas limitações são demonstradas nos seguintes resultados de um teste simples :
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 || 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 viewportif(rect.right <0|| rect.bottom <0|| rect.left > vWidth || rect.top > vHeight)returnfalse;// Return true if any of its four corners are visiblereturn(
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)));}
Este método não é sem suas próprias limitações, no entanto. Por exemplo, um elemento sendo testado com um índice z menor do que outro elemento no mesmo local seria identificado como oculto, mesmo que o elemento na frente não oculte realmente nenhuma parte dele. Ainda assim, esse método tem seus usos em alguns casos que a solução de 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 a maioria dos navegadores de desktop por um longo tempo (embora não perfeitamente). Consulte Quirksmode nestas funções para obter mais informações.
contains()é usado para ver se o elemento retornado por document.elementFromPoint()é um nó filho do elemento que estamos testando para obter visibilidade. Também retorna true se o elemento retornado for o mesmo elemento. Isso apenas torna a verificação mais robusta. É suportado em todos os principais navegadores, sendo o Firefox 9.0 o último a adicioná-lo. Para suporte mais antigo ao Firefox, verifique o histórico desta resposta.
Se você quiser testar mais pontos em torno do elemento para obter visibilidade - ou seja, para garantir que o elemento não seja coberto por mais de, digamos, 50% -, não seria preciso muito para ajustar a última parte da resposta. No entanto, esteja ciente de que provavelmente seria muito lento se você verificasse cada pixel para garantir que estivesse 100% visível.
Você queria usar doc.documentElement.clientWidth? Em vez disso, deveria ser 'document.documentElement'? Em uma observação diferente, este é o único método que também funciona para casos de uso, como ocultar o conteúdo de um elemento para acessibilidade usando a propriedade 'clip' do CSS: snook.ca/archives/html_and_css/hiding-content-for-accessibility
Kevin Atualizando
@ klamping: boa captura, obrigado. Eu o copiei diretamente do meu código para o qual estava usando docum alias document. Sim, eu gosto de pensar nisso como uma solução decente para os casos extremos.
Andy E
2
Para mim, não está funcionando. Mas inViewport () na resposta anterior está funcionando no FF.
Satya Prakash
9
Também pode ser benéfico verificar se o centro do elemento está visível se você tiver cantos arredondados ou uma transformação aplicada, pois os cantos delimitadores podem não retornar o elemento esperado:element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Jared
2
Não funcionou em entradas para mim (chrome canary 50). Não sabe por que, talvez cantos arredondados nativos? Eu tive que reduzir um pouco as cordas para que ele funcionasse el.contains (efp (rect.left + 1, rect.top + 1)) || el.contains (efp (rect.right-1, rect.top + 1)) || el.contains (efp (rect.right-1, rect.bottom-1)) || el.contains (EFP (rect.left + 1, rect.bottom-1))
Rayjax
65
Eu tentei a resposta de Dan . No entanto , a álgebra usada para determinar os limites significa que o elemento deve ser ≤ o tamanho da viewport e estar completamente dentro da viewport para obter true, levando facilmente a falsos negativos. Se você deseja determinar se um elemento está na janela de exibição, a resposta do ryanve está próxima, mas o elemento que está sendo testado deve se sobrepor à janela de exibiçã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 (o elemento pode ser> janela, especialmente nas telas do telefone celular) e o teste jQuery correto, além de adicionar isElementPartiallyInViewport:
A propósito, a diferença entre window.innerWidth e document.documentElement.clientWidth é que clientWidth / clientHeight não inclui a barra de rolagem, enquanto window.innerWidth / Height inclui.
function isElementPartiallyInViewport(el){// Special bonus for those using jQueryif(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-overlapvar 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-viewportfunction isElementInViewport (el){// Special bonus for those using jQueryif(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);}
isElementPartiallyInViewporté muito útil também. Agradável.
RJ Cuthbertson
Para isElementInViewport (e): Seu código nem está carregando imagens, este está correto: function isElementInViewport (e) {var t = e.getBoundingClientRect (); return t.top> = 0 && t.left> = 0 && t.bottom <= (window.innerHeight || document.documentElement.clientHeight) && t.right <= (window.innerWidth || document.documentElement.clientWidth) }
Arun chauhan
1
@Arun chauhan: Nenhum dos meus códigos está carregando imagens, por que deveria e a fórmula está correta.
Stefan Steiger
1
@targumon: O motivo é o suporte a navegadores antigos.
Stefan Steiger
1
@StefanSteiger, de acordo com o MDN, é suportado desde o IE9, por isso é praticamente seguro (pelo menos no meu caso) usar apenas o window.innerHeight diretamente. Obrigado!
Minha solução é mais gananciosa e rápida, quando um elemento tem algum pixel na janela de exibição, ele retornará falso.
Eric Chen
1
Eu gosto disso. Conciso. Você pode remover os espaços entre o nome da função e parênteses, e entre parênteses e chaves, na primeira linha. Nunca gostei desses espaços. Talvez seja apenas o meu editor de texto que codifica com cores tudo que facilita a leitura. function aaa (arg) {declarações} Eu sei que isso não faz com que seja executado mais rapidamente, mas é minimizado.
YK
21
Achei preocupante o fato de não haver uma versão centralizada do jQuery da funcionalidade disponível. Quando me deparei a solução de Dan, vi a oportunidade de oferecer algo para as pessoas que gostam de programar no estilo jQuery OO. É agradável e ágil e funciona como um encanto para mim.
Bada bing bada boom
$.fn.inView =function(){if(!this.length)returnfalse;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}});
Esta solução precisará de um polyfill, pois o Safari, Opera e Internet Explorer ainda não suportam isso (o polyfill está incluído na solução).
Nesta solução, há uma caixa fora da vista que é o alvo (observado). Quando aparece, o botão na parte superior do cabeçalho fica oculto. É mostrado quando a caixa sai da vista.
const buttonToHide = document.querySelector('button');const hideWhenBoxInView =newIntersectionObserver((entries)=>{if(entries[0].intersectionRatio <=0){// If not in view
buttonToHide.style.display ="inherit";}else{
buttonToHide.style.display ="none";}});
hideWhenBoxInView.observe(document.getElementById('box'));
<scriptsrc="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script><header><button>NAVIGATION BUTTON TO HIDE</button></header><divclass="wrapper"><divid="box"></div></div>
Boa implementação, e de acordo com o link em esta resposta que deve trabalhar em safari, adicionando <!DOCTYPE html>ao HTML
Alon Eitan
Observe que IntersectionObserver é um recurso experimental (que pode mudar no futuro).
Karthik Chintala
2
@KarthikChintala - é suportado em todos os navegadores, exceto o IE - e também há um polyfill disponível.
Randy Casburn
Não aborda a questão do OP, pois apenas detecta alterações : IntersectionObserversomente aciona o retorno de chamada após o movimento do destino em relação à raiz.
Brandon Hill
Quando o observeevento de chamada é acionado imediatamente, informando o estado atual da interseção do elemento rastreado. Então, de alguma forma - ele aborda.
Mesqalito 24/01
8
Todas as respostas que encontrei aqui apenas verificam se o elemento está posicionado dentro da viewport atual . Mas isso não significa que seja visível .
E se o elemento fornecido estiver dentro de uma div com conteúdo excedente e for deslocado para fora da vista?
Para resolver isso, você teria que verificar se o elemento está contido por todos os pais.
Minha solução faz exatamente isso:
Também permite especificar quanto do elemento deve estar 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 decimalsif(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 elementos podem não estar visíveis devido a outros fatos, como opacity: 0.
Eu testei esta solução no Chrome e no Internet Explorer 11.
Acho que a resposta aceita aqui é excessivamente complicada para a maioria dos casos de uso. Esse código funciona 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)");}elseif(windowScrollTop > bottomOfElement && windowScrollTop > topOfElement){// Element is hidden (above viewable area)
console.log("Element is hidden (above viewable area)");}elseif(scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement){// Element is hidden (below viewable area)
console.log("Element is hidden (below viewable area)");}elseif(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");}});
Como isso se comporta nos navegadores móveis? A maioria deles tem problemas com a viewport, com o cabeçalho subindo ou descendo na rolagem e comportamento diferente quando o teclado é exibido, dependendo do Android ou do iOS, etc.
Kev
@Kev Deverá funcionar perfeitamente, dependendo de quando você chamar esse método. Se você chamá-lo e redimensionar a janela, o resultado pode obviamente não estar mais correto. Você pode chamá-lo em todos os eventos de redimensionamento, dependendo do tipo de funcionalidade desejada. Sinta-se à vontade para fazer uma pergunta separada sobre seu caso de uso específico e faça ping aqui.
leonheess 26/01
3
Eu acho que essa é uma maneira mais funcional de fazer isso.
A resposta de Dan não funciona em um contexto recursivo.
Essa função resolve o problema quando seu elemento está dentro de outras divs roláveis, testando qualquer nível recursivamente até a tag HTML e para no primeiro falso.
/**
* fullVisible=true only returns true if the all object rect is visible
*/function isReallyVisible(el, fullVisible){if( el.tagName =="HTML")returntrue;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));};
As respostas mais aceitas não funcionam ao ampliar o Google Chrome no Android. Em combinação com a resposta de Dan , para dar conta do Chrome no Android, o visualViewport deve ser usado. O exemplo a seguir leva em consideração apenas a verificação vertical e usa jQuery para a altura da janela:
var visibleY =function(el){var top = el.getBoundingClientRect().top, rect, el = el.parentNode;do{
rect = el.getBoundingClientRect();if(top <= rect.bottom ===false)returnfalse;
el = el.parentNode;}while(el != document.body);// Check it's within the document viewportreturn top <= document.documentElement.clientHeight;};
Eu só precisava verificar se ele está visível no eixo Y (para um recurso de rolagem Ajax load-more-records).
O jeito que eu estou usando é que, quando o elemento aparece na tela, estou adicionando uma classe que aciona uma animação de quadro-chave CSS. É bem direto e funciona especialmente bem quando você tem mais de 10 itens para animar condicionalmente em uma página.
Você deve definitivamente cache de $window = $(window)fora do manipulador de rolagem
Adam Rehal
2
A maioria dos usos nas respostas anteriores está falhando nesses pontos:
-Quando qualquer pixel de um elemento estiver visível, mas não " um canto ",
-Quando um elemento é maior que a porta de visualização e centralizado ,
-A maioria deles está verificando apenas um elemento singular dentro de um documento ou janela .
Bem, para todos esses problemas, eu tenho uma solução e os lados positivos são:
-Você pode retornar visiblequando apenas um pixel de qualquer lado aparecer e não for um canto,
-Você ainda pode retornar visibleenquanto o elemento for maior que a viewport,
-Você pode escolher o seu parent element ou pode deixá-lo escolher automaticamente,
-Funciona também em elementos adicionados dinamicamente .
Se você verificar os trechos abaixo, verá que a diferença de uso overflow-scrollno contêiner de elemento não causará nenhum problema e verá que, ao contrário de outras respostas aqui, mesmo que um pixel apareça de qualquer lado ou quando um elemento seja maior que a viewport e estamos vendo o interior pixels do elemento ainda funciona.
O uso é simples:
// For checking element visibility from any sides
isVisible(element)// For checking elements visibility in a parent you would like to checkvar 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
Uma demonstração sem a crossSearchAlgorithmqual é útil para elementos maiores que a viewport, verifique os pixels internos do elemento3 para ver:
function isVisible(element, parent, crossSearchAlgorithm){var rect = element.getBoundingClientRect(),
prect =(parent !=undefined)? parent.getBoundingClientRect(): element.parentNode.getBoundingClientRect(),
csa =(crossSearchAlgorithm !=undefined)? crossSearchAlgorithm :false,
efp =function(x, y){return document.elementFromPoint(x, y)};// Return false if it's not in the viewportif(rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom){returnfalse;}var flag =false;// Return true if left to right any border pixel reachedfor(var x = rect.left; x < rect.right; x++){if(element.contains(efp(rect.top, x))|| element.contains(efp(rect.bottom, x))){
flag =true;break;}}// Return true if top to bottom any border pixel reachedif(flag ==false){for(var y = rect.top; y < rect.bottom; y++){if(element.contains(efp(rect.left, y))|| element.contains(efp(rect.right, y))){
flag =true;break;}}}if(csa){// Another algorithm to check if element is centered and bigger than viewportif(flag ==false){var x = rect.left;var y = rect.top;// From top left to bottom rightwhile(x < rect.right || y < rect.bottom){if(element.contains(efp(x,y))){
flag =true;break;}if(x < rect.right){ x++;}if(y < rect.bottom){ y++;}}if(flag ==false){
x = rect.right;
y = rect.top;// From top right to bottom leftwhile(x > rect.left || y < rect.bottom){if(element.contains(efp(x,y))){
flag =true;break;}if(x > rect.left){ x--;}if(y < rect.bottom){ y++;}}}}}return flag;}// Check multiple elements visibility
document.getElementById('container').addEventListener("scroll",function(){var elementList = document.getElementsByClassName("element");var console = document.getElementById('console');for(var i=0; i < elementList.length; i++){// I did not define parent, so it will be element's parentif(isVisible(elementList[i])){
console.innerHTML ="Element with id["+ elementList[i].id +"] is visible!";break;}else{
console.innerHTML ="Element with id["+ elementList[i].id +"] is hidden!";}}});// Dynamically added elementsfor(var i=4; i <=6; i++){var newElement = document.createElement("div");
newElement.id ="element"+ i;
newElement.classList.add("element");
document.getElementById('container').appendChild(newElement);}
Veja bem, quando você está dentro do elemento3, ele não sabe se é visível ou não, porque estamos verificando apenas se o elemento é visível dos lados ou dos cantos .
E este inclui o crossSearchAlgorithmque permite que você ainda retorne visiblequando o elemento for maior que a viewport:
function isVisible(element, parent, crossSearchAlgorithm){var rect = element.getBoundingClientRect(),
prect =(parent !=undefined)? parent.getBoundingClientRect(): element.parentNode.getBoundingClientRect(),
csa =(crossSearchAlgorithm !=undefined)? crossSearchAlgorithm :false,
efp =function(x, y){return document.elementFromPoint(x, y)};// Return false if it's not in the viewportif(rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom){returnfalse;}var flag =false;// Return true if left to right any border pixel reachedfor(var x = rect.left; x < rect.right; x++){if(element.contains(efp(rect.top, x))|| element.contains(efp(rect.bottom, x))){
flag =true;break;}}// Return true if top to bottom any border pixel reachedif(flag ==false){for(var y = rect.top; y < rect.bottom; y++){if(element.contains(efp(rect.left, y))|| element.contains(efp(rect.right, y))){
flag =true;break;}}}if(csa){// Another algorithm to check if element is centered and bigger than viewportif(flag ==false){var x = rect.left;var y = rect.top;// From top left to bottom rightwhile(x < rect.right || y < rect.bottom){if(element.contains(efp(x,y))){
flag =true;break;}if(x < rect.right){ x++;}if(y < rect.bottom){ y++;}}if(flag ==false){
x = rect.right;
y = rect.top;// From top right to bottom leftwhile(x > rect.left || y < rect.bottom){if(element.contains(efp(x,y))){
flag =true;break;}if(x > rect.left){ x--;}if(y < rect.bottom){ y++;}}}}}return flag;}// Check multiple elements visibility
document.getElementById('container').addEventListener("scroll",function(){var elementList = document.getElementsByClassName("element");var console = document.getElementById('console');for(var i=0; i < elementList.length; i++){// I did not define parent so it will be element's parent// and it will do crossSearchAlgorithmif(isVisible(elementList[i],null,true)){
console.innerHTML ="Element with id["+ elementList[i].id +"] is visible!";break;}else{
console.innerHTML ="Element with id["+ elementList[i].id +"] is hidden!";}}});// Dynamically added elementsfor(var i=4; i <=6; i++){var newElement = document.createElement("div");
newElement.id ="element"+ i;
newElement.classList.add("element");
document.getElementById('container').appendChild(newElement);}
Esse código é criado para obter informações mais precisas se alguma parte do elemento for mostrada na visualização ou não. Para opções de desempenho ou apenas slides verticais, não use isso! Esse código é mais eficaz em casos de desenho.
Você deve tentar explicar por que sua versão é melhor. Tal como está, parece mais ou menos o mesmo que as outras soluções.
Andy E
1
Ótima solução, ele possui um BOX / ScrollContainer e não está usando a JANELA (apenas se não for especificado). Dê uma olhada no código do que a taxa é uma solução mais universal (Foi procurá-lo muito)
teter
1
Tão simples quanto possível, IMO:
function isVisible(elem){var coords = elem.getBoundingClientRect();returnMath.abs(coords.top)<= coords.height;}
Eu tive a mesma pergunta e descobri usando getBoundingClientRect ().
Esse código é completamente 'genérico' e só precisa ser escrito uma vez para que funcione (você não precisa escrevê-lo para cada elemento que deseja saber que está na janela de exibição).
Esse código verifica apenas se está verticalmente na janela de exibição, não horizontalmente . Nesse caso, a variável (matriz) 'elementos' mantém todos os elementos que você está verificando na vertical na janela de exibição, então pegue qualquer elemento que desejar em qualquer lugar e armazene-o lá.
O 'for loop', percorre cada elemento e verifica se está verticalmente na viewport. Esse código é executado toda vez que o usuário rola! Se o getBoudingClientRect (). Top for inferior a 3/4 da janela de visualização (o elemento está a um quarto da janela de visualização), ele será registrado como 'na janela de visualização'.
Como o código é genérico, você desejará saber 'qual' elemento está na janela de exibição. Para descobrir isso, você pode determiná-lo por atributo personalizado, nome do nó, ID, nome da classe e muito mais.
Aqui está o meu código (diga-me se não funcionar; foi testado no Internet Explorer 11, Firefox 40.0.3, Chrome versão 45.0.2454.85 m, Opera 31.0.1889.174 e Edge with Windows 10, [ainda não 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.');}};
Todas as respostas aqui estão determinando se o elemento está totalmente contido na janela de exibição, e não apenas visível de alguma forma. Por exemplo, se apenas metade da imagem estiver visível na parte inferior da visualização, as soluções aqui falharão, considerando que "fora".
Eu tive um caso de uso em que estou carregando preguiçosamente via IntersectionObserver, mas devido às animações que ocorrem durante o pop-in, não queria observar nenhuma imagem que já estivesse interceptada no carregamento da página. Para fazer isso, usei o seguinte código:
Basicamente, isso é verificar se o limite superior ou inferior está independentemente na janela de exibição. O lado oposto pode estar do lado de fora, mas enquanto um lado estiver dentro, ele será "visível" pelo menos parcialmente.
Respostas:
Atualização: o tempo continua e nossos navegadores também. Essa técnica não é mais recomendada e você deve usar a solução de Dan se não precisar oferecer suporte à versão do Internet Explorer anterior ao 7.
Solução original (agora desatualizada):
Isso verificará se o elemento está totalmente visível na viewport atual:
Você pode modificar isso simplesmente para determinar se alguma parte do elemento está visível na janela de exibição:
fonte
getBoundingClientRect
especificamente para o propósito de encontrar coordenadas de elemento ... Por que não o usamos?Agora a maioria dos navegadores oferece suporte ao método getBoundingClientRect , que se tornou a melhor prática. O uso de uma resposta antiga é muito lento , não é preciso e possui vários erros .
A solução selecionada como correta quase nunca é precisa . Você pode ler mais sobre seus erros.
Esta solução foi testada no Internet Explorer 7 (e posterior), iOS 5 (e posterior) Safari, Android 2.0 (Eclair) e posterior, BlackBerry, Opera Mobile e Internet Explorer Mobile 9 .
Como usar:
Você pode ter certeza de que a função fornecida acima retorna a resposta correta no momento em que é chamada, mas e quanto à visibilidade do elemento de rastreamento como um evento?
Coloque o seguinte código na parte inferior da sua
<body>
tag:Se você fizer modificações no DOM, elas poderão alterar a visibilidade do seu elemento, é claro.
Diretrizes e armadilhas comuns:
Talvez você precise acompanhar o zoom da página / pitada de dispositivo móvel? O jQuery deve lidar com o zoom / pitada de navegador, caso contrário, o primeiro ou o segundo link deve ajudá-lo.
Se você modificar o DOM , isso poderá afetar a visibilidade do elemento. Você deve assumir o controle sobre isso e ligar
handler()
manualmente. Infelizmente, não temos nenhumonrepaint
evento entre navegadores . Por outro lado, isso nos permite fazer otimizações e executar a verificação novamente apenas nas modificações do DOM que podem alterar a visibilidade de um elemento.Nunca Nunca use-o somente no jQuery $ (document) .ready () , porque não há garantia de que o CSS tenha sido aplicado neste momento. Seu código pode funcionar localmente com seu CSS em um disco rígido, mas uma vez colocado em um servidor remoto, ele falhará.
Depois de
DOMContentLoaded
disparado, os estilos são aplicados , mas as imagens ainda não foram carregadas . Portanto, devemos adicionar owindow.onload
ouvinte de eventos.Ainda não podemos capturar o evento zoom / pitada.
O último recurso pode ser o seguinte código:
Você pode usar o incrível recurso pageVisibiliy da API HTML5 se quiser saber se a guia da sua página da web está ativa e visível.
TODO: esse método não lida com duas situações:
z-index
overflow-scroll
no contêiner do elementofonte
return (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))
) e não o objeto jQuery (por exemplo,isElementInViewport($("#elem))
). O jQuery equivalente é adicionar[0]
assim:isElementInViewport($("#elem)[0])
.el is not defined
Atualizar
Nos navegadores modernos, você pode conferir a API do Intersection Observer, que oferece os seguintes benefícios:
O Intersection Observer está a caminho de ser um padrão completo e já é suportado no Chrome 51+, Edge 15+ 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 fornecida por Dan que podem torná-la uma abordagem inadequada para algumas situações. Algumas dessas questões são apontadas em sua resposta, na parte inferior, que seu código fornecerá falsos positivos para elementos que são:
clip
propriedade CSSEssas limitações são demonstradas nos seguintes resultados de um teste simples :
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.
E o resultado:
Notas Adicionais
Este método não é sem suas próprias limitações, no entanto. Por exemplo, um elemento sendo testado com um índice z menor do que outro elemento no mesmo local seria identificado como oculto, mesmo que o elemento na frente não oculte realmente nenhuma parte dele. Ainda assim, esse método tem seus usos em alguns casos que a solução de Dan não cobre.
Ambos
element.getBoundingClientRect()
edocument.elementFromPoint()
fazem parte do Projeto de especificação CSSOM de Trabalho e são apoiados em pelo menos IE 6 e posterior e a maioria dos navegadores de desktop por um longo tempo (embora não perfeitamente). Consulte Quirksmode nestas funções para obter mais informações.contains()
é usado para ver se o elemento retornado pordocument.elementFromPoint()
é um nó filho do elemento que estamos testando para obter visibilidade. Também retorna true se o elemento retornado for o mesmo elemento. Isso apenas torna a verificação mais robusta. É suportado em todos os principais navegadores, sendo o Firefox 9.0 o último a adicioná-lo. Para suporte mais antigo ao Firefox, verifique o histórico desta resposta.Se você quiser testar mais pontos em torno do elemento para obter visibilidade - ou seja, para garantir que o elemento não seja coberto por mais de, digamos, 50% -, não seria preciso muito para ajustar a última parte da resposta. No entanto, esteja ciente de que provavelmente seria muito lento se você verificasse cada pixel para garantir que estivesse 100% visível.
fonte
doc
um aliasdocument
. Sim, eu gosto de pensar nisso como uma solução decente para os casos extremos.element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Eu tentei a resposta de Dan . No entanto , a álgebra usada para determinar os limites significa que o elemento deve ser ≤ o tamanho da viewport e estar completamente dentro da viewport para obter
true
, levando facilmente a falsos negativos. Se você deseja determinar se um elemento está na janela de exibição, a resposta do ryanve está próxima, mas o elemento que está sendo testado deve se sobrepor à janela de exibição, então tente o seguinte:fonte
Como um serviço público:
a resposta de Dan com os cálculos corretos (o elemento pode ser> janela, especialmente nas telas do telefone celular) e o teste jQuery correto, além de adicionar isElementPartiallyInViewport:
A propósito, a diferença entre window.innerWidth e document.documentElement.clientWidth é que clientWidth / clientHeight não inclui a barra de rolagem, enquanto window.innerWidth / Height inclui.
Caso de teste
fonte
isElementPartiallyInViewport
é muito útil também. Agradável.Veja a fonte da margem , que usa getBoundingClientRect . É como:
Retorna
true
se alguma parte do elemento estiver na janela de exibição.fonte
Minha versão mais curta e mais rápida:
E um jsFiddle conforme necessário: https://jsfiddle.net/on1g619L/1/
fonte
Achei preocupante o fato de não haver uma versão centralizada do jQuery da funcionalidade disponível. Quando me deparei a solução de Dan, vi a oportunidade de oferecer algo para as pessoas que gostam de programar no estilo jQuery OO. É agradável e ágil e funciona como um encanto para mim.
Bada bing bada boom
Uso
fonte
A nova API do Intersection Observer aborda essa questão muito diretamente.
Esta solução precisará de um polyfill, pois o Safari, Opera e Internet Explorer ainda não suportam isso (o polyfill está incluído na solução).
Nesta solução, há uma caixa fora da vista que é o alvo (observado). Quando aparece, o botão na parte superior do cabeçalho fica oculto. É mostrado quando a caixa sai da vista.
fonte
<!DOCTYPE html>
ao HTMLIntersectionObserver
é um recurso experimental (que pode mudar no futuro).IntersectionObserver
somente aciona o retorno de chamada após o movimento do destino em relação à raiz.observe
evento de chamada é acionado imediatamente, informando o estado atual da interseção do elemento rastreado. Então, de alguma forma - ele aborda.Todas as respostas que encontrei aqui apenas verificam se o elemento está posicionado dentro da viewport atual . Mas isso não significa que seja visível .
E se o elemento fornecido estiver dentro de uma div com conteúdo excedente e for deslocado para fora da vista?
Para resolver isso, você teria que verificar se o elemento está contido por todos os pais.
Minha solução faz exatamente isso:
Também permite especificar quanto do elemento deve estar visível.
Esta solução ignorou o fato de que elementos podem não estar visíveis devido a outros fatos, como
opacity: 0
.Eu testei esta solução no Chrome e no Internet Explorer 11.
fonte
Acho que a resposta aceita aqui é excessivamente complicada para a maioria dos casos de uso. Esse código funciona bem (usando jQuery) e diferencia entre elementos totalmente visíveis e parcialmente visíveis:
fonte
$window = $(window)
fora do manipulador de rolagem.A solução mais simples como o suporte a Element.getBoundingClientRect () tornou- se perfeita :
fonte
Eu acho que essa é uma maneira mais funcional de fazer isso. A resposta de Dan não funciona em um contexto recursivo.
Essa função resolve o problema quando seu elemento está dentro de outras divs roláveis, testando qualquer nível recursivamente até a tag HTML e para no primeiro falso.
fonte
As respostas mais aceitas não funcionam ao ampliar o Google Chrome no Android. Em combinação com a resposta de Dan , para dar conta do Chrome no Android, o visualViewport deve ser usado. O exemplo a seguir leva em consideração apenas a verificação vertical e usa jQuery para a altura da janela:
fonte
Aqui está a minha solução. Funcionará se um elemento estiver oculto dentro de um contêiner rolável.
Aqui está uma demonstração (tente redimensionar a janela para)
Eu só precisava verificar se ele está visível no eixo Y (para um recurso de rolagem Ajax load-more-records).
fonte
Com base na solução de dan , eu tentei limpar a implementação para facilitar o uso dela várias vezes na mesma página:
O jeito que eu estou usando é que, quando o elemento aparece na tela, estou adicionando uma classe que aciona uma animação de quadro-chave CSS. É bem direto e funciona especialmente bem quando você tem mais de 10 itens para animar condicionalmente em uma página.
fonte
$window = $(window)
fora do manipulador de rolagemA maioria dos usos nas respostas anteriores está falhando nesses pontos:
Bem, para todos esses problemas, eu tenho uma solução e os lados positivos são:
Se você verificar os trechos abaixo, verá que a diferença de uso
overflow-scroll
no contêiner de elemento não causará nenhum problema e verá que, ao contrário de outras respostas aqui, mesmo que um pixel apareça de qualquer lado ou quando um elemento seja maior que a viewport e estamos vendo o interior pixels do elemento ainda funciona.O uso é simples:
Uma demonstração sem a
crossSearchAlgorithm
qual é útil para elementos maiores que a viewport, verifique os pixels internos do elemento3 para ver:Mostrar snippet de código
Veja bem, quando você está dentro do elemento3, ele não sabe se é visível ou não, porque estamos verificando apenas se o elemento é visível dos lados ou dos cantos .
E este inclui o
crossSearchAlgorithm
que permite que você ainda retornevisible
quando o elemento for maior que a viewport:Mostrar snippet de código
JSFiddle para jogar: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Esse código é criado para obter informações mais precisas se alguma parte do elemento for mostrada na visualização ou não. Para opções de desempenho ou apenas slides verticais, não use isso! Esse código é mais eficaz em casos de desenho.
fonte
Uma solução melhor:
fonte
Tão simples quanto possível, IMO:
fonte
Aqui está uma função que informa se um elemento está visível na viewport atual de um elemento pai :
fonte
Isso verifica se um elemento está pelo menos parcialmente visível (dimensão vertical):
fonte
Eu tive a mesma pergunta e descobri usando getBoundingClientRect ().
Esse código é completamente 'genérico' e só precisa ser escrito uma vez para que funcione (você não precisa escrevê-lo para cada elemento que deseja saber que está na janela de exibição).
Esse código verifica apenas se está verticalmente na janela de exibição, não horizontalmente . Nesse caso, a variável (matriz) 'elementos' mantém todos os elementos que você está verificando na vertical na janela de exibição, então pegue qualquer elemento que desejar em qualquer lugar e armazene-o lá.
O 'for loop', percorre cada elemento e verifica se está verticalmente na viewport. Esse código é executado toda vez que o usuário rola! Se o getBoudingClientRect (). Top for inferior a 3/4 da janela de visualização (o elemento está a um quarto da janela de visualização), ele será registrado como 'na janela de visualização'.
Como o código é genérico, você desejará saber 'qual' elemento está na janela de exibição. Para descobrir isso, você pode determiná-lo por atributo personalizado, nome do nó, ID, nome da classe e muito mais.
Aqui está o meu código (diga-me se não funcionar; foi testado no Internet Explorer 11, Firefox 40.0.3, Chrome versão 45.0.2454.85 m, Opera 31.0.1889.174 e Edge with Windows 10, [ainda não no Safari ]) ...
fonte
Esta é a solução fácil e pequena que funcionou para mim.
Exemplo : você deseja ver se o elemento está visível no elemento pai que possui rolagem excedente.
fonte
Todas as respostas aqui estão determinando se o elemento está totalmente contido na janela de exibição, e não apenas visível de alguma forma. Por exemplo, se apenas metade da imagem estiver visível na parte inferior da visualização, as soluções aqui falharão, considerando que "fora".
Eu tive um caso de uso em que estou carregando preguiçosamente via
IntersectionObserver
, mas devido às animações que ocorrem durante o pop-in, não queria observar nenhuma imagem que já estivesse interceptada no carregamento da página. Para fazer isso, usei o seguinte código:Basicamente, isso é verificar se o limite superior ou inferior está independentemente na janela de exibição. O lado oposto pode estar do lado de fora, mas enquanto um lado estiver dentro, ele será "visível" pelo menos parcialmente.
fonte
Eu uso essa função (ela apenas verifica se y está na tela, pois na maioria das vezes o x não é necessário)
fonte
Para um desafio semelhante, eu realmente gostei dessa essência, que expõe um polyfill para scrollIntoViewIfNeeded () .
Todo o Kung Fu necessário para responder está dentro deste bloco:
this
refere-se ao elemento que você deseja saber se é, por exemplo,overTop
ouoverBottom
- você só deve entender o que está acontecendo ...fonte