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?


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



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

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

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.


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.


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)


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 .


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


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

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

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

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/


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.


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

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


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

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


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

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

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

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.


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

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

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

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

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!


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

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

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.


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

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 .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.