Como verificar se o elemento está visível após a rolagem?

1170

Estou carregando elementos via AJAX. Alguns deles são visíveis apenas se você rolar a página.
Existe alguma maneira de saber se um elemento está agora na parte visível da página?

yoavf
fonte
42
ele quer dizer que ele quer um método para saber se um determinado elemento é exibido na janela do navegador ou se o usuário precisa rolar para vê-lo.
Romain Linsolas
1
Para verificar se um elemento está totalmente visível em um contêiner, basta adicionar um parâmetro seletor extra e reutilizar o código elem. Library.IsElementVisibleInContainer = function (elementSelector, containerSelector) { var containerViewTop = $(containerSelector).offset().top; var containerViewBottom = containerViewTop + $(containerSelector).height();
Lifes
4
possível duplicata de Como saber se um elemento DOM está visível na viewport atual?
Audrius Meskauskas
1
Todas as respostas acionarão o reflow para que possa ser um gargalo, você grita usando IntersectionObserver, se suportado. Ela terá melhor desempenho em navegadores modernos,
jcubic

Respostas:

1258

Isso deve fazer o truque:

function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}

Função Utilitária Simples Isso permitirá que você chame uma função utilitária que aceite o elemento que está procurando e se desejar que o elemento esteja totalmente à vista ou parcialmente.

function Utils() {

}

Utils.prototype = {
    constructor: Utils,
    isElementInView: function (element, fullyInView) {
        var pageTop = $(window).scrollTop();
        var pageBottom = pageTop + $(window).height();
        var elementTop = $(element).offset().top;
        var elementBottom = elementTop + $(element).height();

        if (fullyInView === true) {
            return ((pageTop < elementTop) && (pageBottom > elementBottom));
        } else {
            return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
        }
    }
};

var Utils = new Utils();

Uso

var isElementInView = Utils.isElementInView($('#flyout-left-container'), false);

if (isElementInView) {
    console.log('in view');
} else {
    console.log('out of view');
}
Scott Dowding
fonte
52
Observe que isso só funciona se o documento for o elemento que está sendo rolado, ou seja, você não está verificando a visibilidade de algum elemento dentro de um painel interno de rolagem.
Andrew B.
8
como adicionar um pequeno deslocamento?
Jürgen Paul
5
Só trabalhou quando eu usei window.innerHeightvez
Christian Schnorr
2
Para elemTopeu usei $(elem).position().tope para elemBottomeu usei elemTop + $(elem).outerHeight(true).
Sarah Vessels
13
Para: "Qualquer parte do elemento em exibição", usei: ((((elemTop> = docViewTop)) && (elemTop <= docViewBottom)) || ((elemBottom> = docViewTop) && (elemBottom <= docViewBottom)))
Grizly
415

Esta resposta no Vanilla:

function isScrolledIntoView(el) {
    var rect = el.getBoundingClientRect();
    var elemTop = rect.top;
    var elemBottom = rect.bottom;

    // Only completely visible elements return true:
    var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
    // Partially visible elements return true:
    //isVisible = elemTop < window.innerHeight && elemBottom >= 0;
    return isVisible;
}
bravedick
fonte
27
não deveria ser assim isVisible = elementTop < window.innerHeight && elementBottom >= 0? Caso contrário, um elemento na metade da tela retornará falso.
gman
7
não. verifico se algum elemento está totalmente visível na página. se você quiser verificar a visibilidade de alguma parte - você pode personalizar esse trecho.
24515 bravedick
15
Acho que esta resposta tem um desempenho melhor que a resposta escolhida. Mais simples também.
Adam Venezia
12
Em comparação com a resposta aprovada, isso funciona muito melhor com centenas de elementos.
Ncla 15/02/16
5
ver um pequeno violino demonstrando aqui - jsfiddle.net/shaaraddalvi/4rp09jL0
upInCloud
122

Atualização: use IntersectionObserver


O melhor método que encontrei até agora é o plugin jQuery appear . Funciona como um encanto.

Imita um evento personalizado "aparecer", que é acionado quando um elemento rola para exibição ou se torna visível para o usuário.

$('#foo').appear(function() {
  $(this).text('Hello world');
});

Este plug-in pode ser usado para impedir solicitações desnecessárias de conteúdo oculto ou fora da área visível.

Joe Lencioni
fonte
30
Este é um plugin interessante, sem dúvida, mas não responde à pergunta.
John Adams
5
Embora o plugin jQuery-appear seja bom para o conteúdo da área da página principal, infelizmente ele tem problemas com divs de rolagem de tamanho fixo com estouro. O evento pode disparar prematuramente quando o elemento encadernado estiver dentro da área visível da página, mas fora da área visível da div e, em seguida, não será acionado conforme o esperado quando o elemento aparecer na div.
Peter
17
Existe um plugin para desaparecer?
Shamoon
3
@ Shamoon verifique a fonte appear plugine provavelmente você precisará adicionar um local !para obter um disappearplugin.
Sorte Soni
5
Como uma nota, este não funciona com jQuery 1.11.X github.com/morr/jquery.appear/issues/37
Jason Parham
86

Aqui está minha solução JavaScript pura que funciona se também estiver oculta em um contêiner rolável.

Demonstração aqui (tente redimensionar a janela também)

var visibleY = function(el){
  var rect = el.getBoundingClientRect(), top = rect.top, height = rect.height, 
    el = el.parentNode
  // Check if bottom of the element is off the page
  if (rect.bottom < 0) return false
  // Check its within the document viewport
  if (top > document.documentElement.clientHeight) return false
  do {
    rect = el.getBoundingClientRect()
    if (top <= rect.bottom === false) return false
    // Check if the element is out of view due to a container scrolling
    if ((top + height) <= rect.top) return false
    el = el.parentNode
  } while (el != document.body)
  return true
};

EDIT 26-03-2016: atualizei a solução para considerar a rolagem além do elemento, para que fique oculto acima da parte superior do contêiner capaz de rolagem. EDIT 2018-10-08: atualizado para lidar com quando rolado fora da vista acima da tela.

Aliado
fonte
Obrigado, talvez seja melhor return top <= document.documentElement.clientHeight && top >= 0;
Yousef Salimpour
16
+1 Esta foi a única resposta codificada (isto é, não de terceiros) que leva em consideração a natureza recursiva dos elementos. Eu expandido para lidar com rolagem horizontal, vertical e na página: jsfiddle.net/9nuqpgqa
Pebbl
3
Esta solução verifica apenas a parte superior do elemento. Se o primeiro pixel superior estiver visível, ele retornará verdadeiro, mesmo que o restante do item não esteja visível. Para verificar se todo o elemento está visível, você também precisa verificar a propriedade bottom.
Wojciech Jakubas
2
Sim, limpo! Usado para ajudar a escrever esta resposta (com crédito como comentário js).
Roamer-1888
Ausência de ; após o segundo "return false" no loop
Mikhail Ramendik
46

Usando a API IntersectionObserver (nativo em navegadores modernos)

É fácil e eficiente determinar se um elemento está visível no viewpor ou em qualquer contêiner rolável usando um observador .

A necessidade de anexar um scrollevento e a verificação manual do retorno de chamada do evento são eliminadas, assim a eficiência:

// this is the target which is observed
var target = document.querySelector('div');

// configure the intersection observer instance
var intersectionObserverOptions = {
  root: null,
  rootMargin: '150px',
  threshold: 1.0
}
    
var observer = new IntersectionObserver(onIntersection, intersectionObserverOptions);

// provide the observer with a target
observer.observe(target);

function onIntersection(entries){
  entries.forEach(entry => {
    console.clear();
    console.log(entry.intersectionRatio)
    target.classList.toggle('visible', entry.intersectionRatio > 0);
    
    // Are we in viewport?
    if (entry.intersectionRatio > 0) {
      // Stop watching 
      // observer.unobserve(entry.target);
    }
  });
}
.box{ width:100px; height:100px; background:red; margin:1000px; }
.box.visible{ background:green; }
Scroll both Vertically & Horizontally...
<div class='box'></div>


Exibir tabela de suporte de navegadores (não suportada no IE / Safari)

vsync
fonte
4
Obrigado! Isso funciona para mim e também tenho que trabalhar no IE11 com github.com/w3c/IntersectionObserver
Matt Wilson
De longe a melhor solução. Trabalhou no IE11 sem polyfill!
Fabian von Ellerts
Observe que, infelizmente, AINDA NÃO é suportado no iOS / macOS Safari. Certifique-se de verificar perf problemas se você optar por polyfill, isso é um grande grupo de usuários
Leland
@Leland - depende do projeto. para todos os meus projetos, este é um grupo absoluto de 0 usuários. Eu não construo sites, mas sistema web;)
vsync
Eu estou tentando executar isso em um loop em vários elementos, mas não está funcionando. Alguma ideia? Estou adicionando o elemento a ser alvo nesse loop.
Sascha Grindau 02/08/19
42

O plugin jQuery Waypoints é muito bom aqui.

$('.entry').waypoint(function() {
   alert('You have scrolled to an entry.');
});

Existem alguns exemplos no site do plugin .

Fedir RYKHTIK
fonte
3
Para mim, só trabalhou com um deslocamento $('#my-div').waypoint(function() { console.log('Hello there!'); }, { offset: '100%' });
leymannx
21

E se

function isInView(elem){
   return $(elem).offset().top - $(window).scrollTop() < $(elem).height() ;
}

Depois disso, você pode acionar o que quiser quando o elemento estiver em exibição como este

$(window).scroll(function(){
   if (isInView($('.classOfDivToCheck')))
      //fire whatever you what 
      dothis();
})

Isso funciona para mim muito bem

webicy
fonte
1
Isso funciona para mim, mas eu usei a função, aparentemente mais completa, isScrolledIntoView em stackoverflow.com/questions/487073/… :)
Meetai.com
3
Eu acho que deveria ser $ (window) .scrollTop () <$ (elem) .offset (). Top + $ (elem) .height ();
nova
Minha modificação seria assim: `return $ (window) .scrollTop () + $ (window) .height ()> $ (elem) .offset (). Top + $ (elem) .height (); `
bubencode 02/09
15

O WebResourcesDepot escreveu um script para carregar durante a rolagem que usa o jQuery há algum tempo. Você pode ver a demonstração ao vivo aqui . A carne de sua funcionalidade era esta:

$(window).scroll(function(){
  if  ($(window).scrollTop() == $(document).height() - $(window).height()){
    lastAddedLiveFunc();
  }
});

function lastAddedLiveFunc() { 
  $('div#lastPostsLoader').html('<img src="images/bigLoader.gif">');
  $.post("default.asp?action=getLastPosts&lastPostID="+$(".wrdLatest:last").attr("id"),
    function(data){
        if (data != "") {
          $(".wrdLatest:last").after(data);         
        }
      $('div#lastPostsLoader').empty();
    });
};
Sampson
fonte
15

A função interessante de Tweeked Scott Dowding para minha exigência - isso é usado para descobrir se o elemento acabou de rolar a tela, ou seja, é a borda superior.

function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();
    var elemTop = $(elem).offset().top;
    return ((elemTop <= docViewBottom) && (elemTop >= docViewTop));
}
Snigdha Batra
fonte
12

Baunilha comum para verificar se o elemento ( el) está visível em div ( holder) rolável

function isElementVisible (el, holder) {
  holder = holder || document.body
  const { top, bottom, height } = el.getBoundingClientRect()
  const holderRect = holder.getBoundingClientRect()

  return top <= holderRect.top
    ? holderRect.top - top <= height
    : bottom - holderRect.bottom <= height
},

Uso com jQuery:

var el = $('tr:last').get(0);
var holder = $('table').get(0);
isVisible =  isScrolledIntoView(el, holder);
Denis Matafonov
fonte
2
Nesta era de aplicativos de página única, tornou-se mais comum verificar se um elemento está visível em algum outro elemento além da janela . É por isso que este recebe meu voto positivo.
H Dog
8

isScrolledIntoView é uma função muito necessária, então eu tentei, funciona para elementos não maiores que a viewport, mas se o elemento for maior que a viewport, não funcionará. Para corrigir isso, mude facilmente a condição

return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));

para isso:

return (docViewBottom >= elemTop && docViewTop <= elemBottom);

Veja a demonstração aqui: http://jsfiddle.net/RRSmQ/

Robert
fonte
8

A maioria das respostas aqui não leva em consideração que um elemento também pode ser oculto porque é rolado para fora da visualização de uma div, não apenas da página inteira.

Para cobrir essa possibilidade, você basicamente precisa verificar se o elemento está posicionado dentro dos limites de cada um de seus pais.

Esta solução faz exatamente isso:

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

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

Também permite especificar em que porcentagem deve ser visível em cada direção.
Não cobre a possibilidade de ocultar devido a outros fatores, como display: hidden.

Isso deve funcionar em todos os principais navegadores, já que ele usa apenas getBoundingClientRect. Eu testei pessoalmente no Chrome e no Internet Explorer 11.

Domysee
fonte
Obrigado por este código. Gostaria de saber como você adicionaria o ouvinte de evento na rolagem, nesse caso, se você tiver vários elementos roláveis ​​aninhados? Parece que adicionar o ouvinte à janela sozinho não é suficiente; precisamos voltar ao pai principal para adicionar o ouvinte a cada contêiner rolável?
Mr1031011 13/0218
@ mr1031011 Deve ser possível adicionar o manipulador à janela e verificar o destino para identificar o contêiner que foi rolado.
Domysee 13/02/19
direita, ele não funciona com o exemplo dado por @vanowm,
mr1031011
7
function isScrolledIntoView(elem) {
    var docViewTop = $(window).scrollTop(),
        docViewBottom = docViewTop + $(window).height(),
        elemTop = $(elem).offset().top,
     elemBottom = elemTop + $(elem).height();
   //Is more than half of the element visible
   return ((elemTop + ((elemBottom - elemTop)/2)) >= docViewTop && ((elemTop + ((elemBottom - elemTop)/2)) <= docViewBottom));
}
Pascal Gagneur
fonte
7

Aqui está outra solução em http://web-profile.com.ua/

<script type="text/javascript">
$.fn.is_on_screen = function(){
    var win = $(window);
    var viewport = {
        top : win.scrollTop(),
        left : win.scrollLeft()
    };
    viewport.right = viewport.left + win.width();
    viewport.bottom = viewport.top + win.height();

    var bounds = this.offset();
    bounds.right = bounds.left + this.outerWidth();
    bounds.bottom = bounds.top + this.outerHeight();

    return (!(viewport.right < bounds.left || viewport.left > bounds.right ||    viewport.bottom < bounds.top || viewport.top > bounds.bottom));
 };

if( $('.target').length > 0 ) { // if target element exists in DOM
    if( $('.target').is_on_screen() ) { // if target element is visible on screen after DOM loaded
        $('.log').html('<div class="alert alert-success">target element is visible on screen</div>'); // log info       
    } else {
        $('.log').html('<div class="alert">target element is not visible on screen</div>'); // log info
    }
}
$(window).scroll(function(){ // bind window scroll event
if( $('.target').length > 0 ) { // if target element exists in DOM
    if( $('.target').is_on_screen() ) { // if target element is visible on screen after DOM loaded
        $('.log').html('<div class="alert alert-success">target element is visible on screen</div>'); // log info
    } else {
        $('.log').html('<div class="alert">target element is not visible on screen</div>'); // log info
    }
}
});
</script>

Veja no JSFiddle

Adrian P.
fonte
7

Isso considera qualquer preenchimento, borda ou margem que o elemento tenha, além de elementos maiores que a própria viewport.

function inViewport($ele) {
    var lBound = $(window).scrollTop(),
        uBound = lBound + $(window).height(),
        top = $ele.offset().top,
        bottom = top + $ele.outerHeight(true);

    return (top > lBound && top < uBound)
        || (bottom > lBound && bottom < uBound)
        || (lBound >= top && lBound <= bottom)
        || (uBound >= top && uBound <= bottom);
}

Para chamá-lo, use algo como isto:

var $myElement = $('#my-element'),
    canUserSeeIt = inViewport($myElement);

console.log(canUserSeeIt); // true, if element is visible; false otherwise
Brent Barbata
fonte
7

Existe um plugin para o jQuery chamado inview que adiciona um novo evento "inview".


Aqui está um código para um plugin jQuery que não usa eventos:

$.extend($.expr[':'],{
    inView: function(a) {
        var st = (document.documentElement.scrollTop || document.body.scrollTop),
            ot = $(a).offset().top,
            wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();
        return ot > st && ($(a).height() + ot) < (st + wh);
    }
});

(function( $ ) {
    $.fn.inView = function() {
        var st = (document.documentElement.scrollTop || document.body.scrollTop),
        ot = $(this).offset().top,
        wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();

        return ot > st && ($(this).height() + ot) < (st + wh);
    };
})( jQuery );

Encontrei isso em um comentário aqui ( http://remysharp.com/2009/01/26/element-in-view-event-plugin/ ) por um cara chamado James

ness-EE
fonte
Infelizmente, o jQuery inview não é mais mantido e não funciona com as versões atuais do jQuery.
Mikemaccana
1
O JQuery 1 é para suporte ao navegador herdado, novos recursos estão no jQuery 2. #
mikemaccana
O link não mostra o exemplo, pois a página foi atualizada.
Professor de programação
6

Eu precisava verificar a visibilidade nos elementos dentro do contêiner DIV rolável

    //p = DIV container scrollable
    //e = element
    function visible_in_container(p, e) {
        var z = p.getBoundingClientRect();
        var r = e.getBoundingClientRect();

        // Check style visiblilty and off-limits
        return e.style.opacity > 0 && e.style.display !== 'none' &&
               e.style.visibility !== 'hidden' &&
               !(r.top > z.bottom || r.bottom < z.top ||
                 r.left > z.right || r.right < z.left);
    }
Pigmalión
fonte
isso funciona para mim se eu mudar e.style.opacity > 0para, (!e.style.opacity || e.style.opacity > 0)porque, por padrão, é a string vazia para mim no FF.
Brett Zamir
6

Com base nessa ótima resposta , você pode simplificá-la um pouco mais usando o ES2015 +:

function isScrolledIntoView(el) {
  const { top, bottom } = el.getBoundingClientRect()
  return top >= 0 && bottom <= window.innerHeight
}

Se você não se importa com a parte superior saindo pela janela e apenas se preocupando com a visualização da parte inferior, isso pode ser simplificado para

function isSeen(el) {
  return el.getBoundingClientRect().bottom <= window.innerHeight
}

ou mesmo o one-liner

const isSeen = el => el.getBoundingClientRect().bottom <= window.innerHeight
rpearce
fonte
4

Você pode usar o plugin jquery "onScreen" para verificar se o elemento está na janela de exibição atual quando você rola. O plug-in define o ": onScreen" do seletor como true quando o seletor aparece na tela. Este é o link para o plugin que você pode incluir no seu projeto. " http://benpickles.github.io/onScreen/jquery.onscreen.min.js "

Você pode tentar o exemplo abaixo, que funciona para mim.

$(document).scroll(function() {
    if($("#div2").is(':onScreen')) {
        console.log("Element appeared on Screen");
        //do all your stuffs here when element is visible.
    }
    else {
        console.log("Element not on Screen");
        //do all your stuffs here when element is not visible.
    }
});

Código HTML:

<div id="div1" style="width: 400px; height: 1000px; padding-top: 20px; position: relative; top: 45px"></div> <br>
<hr /> <br>
<div id="div2" style="width: 400px; height: 200px"></div>

CSS:

#div1 {
    background-color: red;
}
#div2 {
    background-color: green;
}
Vasuki Dileep
fonte
3

Eu tenho esse método no meu aplicativo, mas ele não usa o jQuery:

/* Get the TOP position of a given element. */
function getPositionTop(element){
    var offset = 0;
    while(element) {
        offset += element["offsetTop"];
        element = element.offsetParent;
    }
    return offset;
}

/* Is a given element is visible or not? */
function isElementVisible(eltId) {
    var elt = document.getElementById(eltId);
    if (!elt) {
        // Element not found.
        return false;
    }
    // Get the top and bottom position of the given element.
    var posTop = getPositionTop(elt);
    var posBottom = posTop + elt.offsetHeight;
    // Get the top and bottom position of the *visible* part of the window.
    var visibleTop = document.body.scrollTop;
    var visibleBottom = visibleTop + document.documentElement.offsetHeight;
    return ((posBottom >= visibleTop) && (posTop <= visibleBottom));
}

Edit: Este método funciona bem para o IE (pelo menos versão 6). Leia os comentários para compatibilidade com o FF.

Romain Linsolas
fonte
2
Por alguma razão, document.body.scrollTop sempre retorna 0 (em ff3). Altere-o para var visibleTop = (document.documentElement.scrollTop? Document.documentElement.scrollTop: document.body.scrollTop);
yoavf 28/01
Desculpe por isso. Meu aplicativo deve ser executado apenas no IE 6 (sim, eu não tenho sorte :(), então eu nunca testei isso no FF ...
Romain Linsolas
Essa seria a melhor resposta aqui, se estivesse correta. Corrija uma de suas linhas para isso: var visibleBottom = visibleTop + window.innerHeight;não estou usando jQuery e você me ajudou a encontrar a resposta correta.
Bitterblue
3

Se você quiser ajustar isso para rolar o item dentro de outra div,

function isScrolledIntoView (elem, divID) 

{

    var docViewTop = $('#' + divID).scrollTop();


    var docViewBottom = docViewTop + $('#' + divID).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); 
}
Samiya Akhtar
fonte
3

Modificou a resposta aceita para que o elemento tenha sua propriedade de exibição definida como algo diferente de "nenhum" para a qualidade como visível.

function isScrolledIntoView(elem) {
   var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();

  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();
  var elemDisplayNotNone = $(elem).css("display") !== "none";

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop) && elemDisplayNotNone);
}
evanmcd
fonte
3

Aqui está uma maneira de conseguir a mesma coisa usando o Mootools, na horizontal, na vertical ou em ambas.

Element.implement({
inVerticalView: function (full) {
    if (typeOf(full) === "null") {
        full = true;
    }

    if (this.getStyle('display') === 'none') {
        return false;
    }

    // Window Size and Scroll
    var windowScroll = window.getScroll();
    var windowSize = window.getSize();
    // Element Size and Scroll
    var elementPosition = this.getPosition();
    var elementSize = this.getSize();

    // Calculation Variables
    var docViewTop = windowScroll.y;
    var docViewBottom = docViewTop + windowSize.y;
    var elemTop = elementPosition.y;
    var elemBottom = elemTop + elementSize.y;

    if (full) {
        return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
            && (elemBottom <= docViewBottom) && (elemTop >= docViewTop) );
    } else {
        return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    }
},
inHorizontalView: function(full) {
    if (typeOf(full) === "null") {
        full = true;
    }

    if (this.getStyle('display') === 'none') {
        return false;
    }

    // Window Size and Scroll
    var windowScroll = window.getScroll();
    var windowSize = window.getSize();
    // Element Size and Scroll
    var elementPosition = this.getPosition();
    var elementSize = this.getSize();

    // Calculation Variables
    var docViewLeft = windowScroll.x;
    var docViewRight = docViewLeft + windowSize.x;
    var elemLeft = elementPosition.x;
    var elemRight = elemLeft + elementSize.x;

    if (full) {
        return ((elemRight >= docViewLeft) && (elemLeft <= docViewRight)
            && (elemRight <= docViewRight) && (elemLeft >= docViewLeft) );
    } else {
        return ((elemRight <= docViewRight) && (elemLeft >= docViewLeft));
    }
},
inView: function(full) {
    return this.inHorizontalView(full) && this.inVerticalView(full);
}});
bmlkc
fonte
3

Um exemplo baseado nesta resposta para verificar se um elemento está 75% visível (ou seja, menos de 25% dele está fora da tela).

function isScrolledIntoView(el) {
  // check for 75% visible
  var percentVisible = 0.75;
  var elemTop = el.getBoundingClientRect().top;
  var elemBottom = el.getBoundingClientRect().bottom;
  var elemHeight = el.getBoundingClientRect().height;
  var overhang = elemHeight * (1 - percentVisible);

  var isVisible = (elemTop >= -overhang) && (elemBottom <= window.innerHeight + overhang);
  return isVisible;
}
Brendan Nee
fonte
3

Existem mais de 30 respostas para essa pergunta e nenhuma delas usa a solução JS pura e incrivelmente simples que eu tenho usado. Não é necessário carregar o jQuery apenas para resolver isso, pois muitos outros estão pressionando.

Para saber se o elemento está dentro da viewport, devemos primeiro determinar a posição dos elementos no corpo. Não precisamos fazer isso recursivamente, como eu pensava. Em vez disso, podemos usar element.getBoundingClientRect().

pos = elem.getBoundingClientRect().top - document.body.getBoundingClientRect().top;

Este valor é a diferença Y entre a parte superior do objeto e a parte superior do corpo.

Devemos então dizer se o elemento está à vista. A maioria das implementações pergunta se o elemento completo está dentro da janela de exibição, então é isso que abordaremos.

Primeiro de tudo, a posição superior da janela é: window.scrollY.

Podemos obter a posição inferior da janela adicionando a altura da janela à sua posição superior:

var window_bottom_position = window.scrollY + window.innerHeight;

Vamos criar uma função simples para obter a posição superior do elemento:

function getElementWindowTop(elem){
    return elem && typeof elem.getBoundingClientRect === 'function' ? elem.getBoundingClientRect().top - document.body.getBoundingClientRect().top : 0;
}

Esta função retornará a posição superior do elemento dentro da janela ou retornará 0se você passar algo diferente de um elemento com o .getBoundingClientRect()método Esse método existe há muito tempo, portanto você não precisa se preocupar com o fato de seu navegador não o suportar.

Agora, a posição superior do nosso elemento é:

var element_top_position = getElementWindowTop(element);

E ou a posição inferior do elemento é:

var element_bottom_position = element_top_position + element.clientHeight;

Agora, podemos determinar se o elemento está dentro da viewport, verificando se a posição inferior do elemento é menor que a posição superior da viewport e verificando se a posição superior do elemento é maior que a posição inferior da viewport:

if(element_bottom_position >= window.scrollY 
&& element_top_position <= window_bottom_position){
    //element is in view
else
    //element is not in view

A partir daí, você pode executar a lógica para adicionar ou remover uma in-viewclasse em seu elemento, com a qual poderá lidar posteriormente com efeitos de transição em seu CSS.

Estou absolutamente surpreso por não encontrar essa solução em nenhum outro lugar, mas acredito que essa é a solução mais limpa e eficaz e não exige que você carregue o jQuery!

WebWanderer
fonte
Explicação muito agradável! Mas já existem respostas que exatamente o que você faz, como resposta de Ally
Domysee
1
@Domysee Hmm, eu de alguma forma pulei isso. Justo. Obrigado por apontar isso embora. É bom ver isso feito de outra maneira.
WebWanderer
3

Uma versão mais eficiente desta resposta :

 /**
 * Is element within visible region of a scrollable container
 * @param {HTMLElement} el - element to test
 * @returns {boolean} true if within visible region, otherwise false
 */
 function isScrolledIntoView(el) {
      var rect = el.getBoundingClientRect();
      return (rect.top >= 0) && (rect.bottom <= window.innerHeight);
 }
John Doherty
fonte
2

Este método retornará true se qualquer parte do elemento estiver visível na página. Funcionou melhor no meu caso e pode ajudar outra pessoa.

function isOnScreen(element) {
  var elementOffsetTop = element.offset().top;
  var elementHeight = element.height();

  var screenScrollTop = $(window).scrollTop();
  var screenHeight = $(window).height();

  var scrollIsAboveElement = elementOffsetTop + elementHeight - screenScrollTop >= 0;
  var elementIsVisibleOnScreen = screenScrollTop + screenHeight - elementOffsetTop >= 0;

  return scrollIsAboveElement && elementIsVisibleOnScreen;
}
Rafael Garcia
fonte
2

Modificação simples para div rolável (contêiner)

var isScrolledIntoView = function(elem, container) {
    var containerHeight = $(container).height();
    var elemTop = $(elem).position().top;
    var elemBottom = elemTop + $(elem).height();
    return (elemBottom > 0 && elemTop < containerHeight);
}

NOTA: isso não funciona se o elemento for maior que a div rolável.

Derrick J Wippler
fonte
2

Adaptei esta curta extensão de função jQuery, que você pode usar (licença MIT).

/**
 * returns true if an element is visible, with decent performance
 * @param [scope] scope of the render-window instance; default: window
 * @returns {boolean}
 */
jQuery.fn.isOnScreen = function(scope){
    var element = this;
    if(!element){
        return;
    }
    var target = $(element);
    if(target.is(':visible') == false){
        return false;
    }
    scope = $(scope || window);
    var top = scope.scrollTop();
    var bot = top + scope.height();
    var elTop = target.offset().top;
    var elBot = elTop + target.height();

    return ((elBot <= bot) && (elTop >= top));
};
Lorenz Lo Sauer
fonte
2

Eu escrevi um componente para a tarefa, projetado para lidar com um grande número de elementos extremamente rápido (na faixa de <10ms para 1000 elementos em um celular lento ).

Ele funciona com qualquer tipo de recipiente de rolagem você tem acesso a - janela, elementos HTML, iframe incorporado, janela filho gerou - e é muito flexível no que detecta ( visibilidade total ou parcial , beira caixa ou caixa de conteúdo , costume zona de tolerância , etc )

Um conjunto de testes enorme, principalmente gerado automaticamente, garante que ele funcione como anunciado em vários navegadores .

Experimente se você gosta: jQuery.isInView . Caso contrário, você pode encontrar inspiração no código fonte, por exemplo, aqui .

hashchange
fonte