Geçerli görünüm penceresinde bir DOM öğesinin görünür olup olmadığını nasıl anlayabilirim?


949

Bir DOM öğesinin (HTML belgesinde) şu anda görünür olup olmadığını ( görünüm penceresinde görünür) anlamanın etkili bir yolu var mı ?

(Soru Firefox ile ilgilidir.)


Ne demek istediğinize göre değişir. Kaydırma konumu göz önüne alındığında, o anda sayfada gösteriliyorsa, y ofseti ve geçerli kaydırma konumuna göre hesaplayabilirsiniz.
roryf

18
Açıklama: Görüntülenmekte olan dikdörtgenin içindeki gibi görünür. Görünmez, gizli veya ekranda görünmediği gibi: yok mu? Görünür, Z düzenindeki başka bir şeyin arkasında olmadığı gibi?
Adam Wright

6
o anda görüntülenen dikdörtgenin içindeki gibi. Teşekkürler. Rephrased.
benzaita

2
Buradaki tüm yanıtların, üst öğelerinin görünür alanının dışında (taşma gizlenmiş haldedir) ancak görünüm alanı içinde bulunan öğeler için size yanlış bir pozitif vereceğini unutmayın.
Andy E

1
Bir milyon cevap var ve çoğu gülünç derecede uzun. İki astar için buraya bakın
leonheess

Yanıtlar:


347

Güncelleme: Zaman ilerliyor ve böylece tarayıcılarımız var. Bu teknik artık önerilmemektedir ve 7'den önce Internet Explorer sürümünü desteklemeniz gerekmiyorsa Dan'ın çözümünü kullanmalısınız .

Orijinal çözüm (şimdi modası geçmiş):

Bu, öğenin geçerli görünüm penceresinde tamamen görünür olup olmadığını kontrol eder:

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

Bunu, öğenin herhangi bir bölümünün görünüm alanında görünür olup olmadığını belirlemek için değiştirebilirsiniz:

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

1
Gönderilen orijinal işlevde bir hata oluştu.
Elemanı

15
Öğe kaydırılabilir bir div içinde yaşıyor ve bir görünümden dışarı kaydırılırsa ne olur?
amartynov

2
Lütfen aşağıdaki betiğin daha yeni bir sürümünü gözden
Dan

1
Ayrıca @ amartynov'un sorusu hakkında merak ediyorum. Herkes, bir ata elemanının taşması nedeniyle bir elemanın gizli olup olmadığını nasıl anlayabilir? Bu, çocuğun ne kadar derin yuvalanmış olduğuna bakılmaksızın tespit edilebilirse bonus.
Eric Nguyen

1
@deadManN, DOM üzerinden yineleniyor. Bu yeterli bir sebep, ancak tarayıcı satıcıları da getBoundingClientRectözellikle eleman koordinatları bulmak amacıyla oluşturdular ... Neden kullanmıyoruz?
Prestaul

1402

Artık çoğu tarayıcı , en iyi uygulama haline gelen getBoundingClientRect yöntemini destekliyor . Eski bir cevap kullanmak çok yavaş , doğru değil ve birkaç hata var .

Doğru olarak seçilen çözelti neredeyse hiçbir zaman kesin değildir . Sen edebilirsiniz devamı olan hatalar hakkında.


Bu çözüm Internet Explorer 7 (ve üstü), iOS 5 (ve üstü) Safari, Android 2.0 (Eclair) ve üstü, BlackBerry, Opera Mobile ve Internet Explorer Mobile 9'da test edildi .


function isElementInViewport (el) {

    // Special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */
    );
}

Nasıl kullanılır:

Yukarıda verilen işlevin çağrıldığı anda doğru yanıtı döndürdüğünden emin olabilirsiniz, ancak bir öğenin olay olarak görünürlüğünü izlemeye ne dersiniz?

Aşağıdaki kodu <body>etiketinizin altına yerleştirin :

function onVisibilityChange(el, callback) {
    var old_visible;
    return function () {
        var visible = isElementInViewport(el);
        if (visible != old_visible) {
            old_visible = visible;
            if (typeof callback == 'function') {
                callback();
            }
        }
    }
}

var handler = onVisibilityChange(el, function() {
    /* Your code go here */
});


// jQuery
$(window).on('DOMContentLoaded load resize scroll', handler);

/* // Non-jQuery
if (window.addEventListener) {
    addEventListener('DOMContentLoaded', handler, false);
    addEventListener('load', handler, false);
    addEventListener('scroll', handler, false);
    addEventListener('resize', handler, false);
} else if (window.attachEvent)  {
    attachEvent('onDOMContentLoaded', handler); // Internet Explorer 9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

Herhangi bir DOM değişikliği yaparsanız, öğenizin elbette görünürlüğünü değiştirebilirler.

Yönergeler ve yaygın tuzaklar:

Belki sayfa yakınlaştırma / mobil cihaz tutam izlemeniz gerekir? jQuery yakınlaştırma / sıkıştırma çapraz tarayıcıyı işlemelidir , aksi takdirde birinci veya ikinci bağlantı size yardımcı olacaktır.

Eğer varsa DOM'da değişiklik , bu öğenin görünürlüğünü etkileyebilir. Bunu kontrol etmeli ve handler()manuel olarak aramalısınız . Ne yazık ki, çapraz tarayıcı onrepaintetkinliğimiz yok. Öte yandan optimizasyon yapmamıza ve yalnızca bir öğenin görünürlüğünü değiştirebilecek DOM değişikliklerinde yeniden kontrol yapmamıza olanak tanır.

Asla Hiç jQuery $ (document) .ready () içinde kullanmayın , çünkü şu anda hiçbir CSS uygulanmış garanti yoktur . Kodunuz sabit diskteki CSS'nizle yerel olarak çalışabilir, ancak uzak sunucuya yerleştirildikten sonra başarısız olur.

DOMContentLoadedİşten çıkarıldıktan sonra stiller uygulanır , ancak görüntüler henüz yüklenmez . Bu yüzden window.onloadolay dinleyicisi eklemeliyiz .

Henüz zoom / pinch olayını yakalayamadık.

Son çare aşağıdaki kod olabilir:

/* TODO: this looks like a very bad code */
setInterval(handler, 600);

Web sayfanızdaki sekmenin etkin ve görünür olup olmadığını düşünüyorsanız, HTML5 API'sının harika özellik sayfasını kullanabilirsiniz .

YAPILACAKLAR: Bu yöntem iki durumu ele almaz:


6
Bu çözümü kullanıyorum ("botom" yazım hatası olmasına rağmen). Ayrıca, dikkate aldığımız öğenin içinde ne zaman görüntüler olacağını bilmeniz gereken bir şey var. Chrome'un (en azından), resmin loadingRectangle için tam değere sahip olmasını beklemesi gerekir. Firefox'un bu "sorunu" yok gibi görünüyor
Claudio

4
Gövde içindeki bir kapta kaydırma etkin olduğunda çalışır. Örneğin burada çalışmaz - agaase.github.io/webpages/demo/isonscreen2.html isElementInViewport (document.getElementById ("innerele")). internalele, kaydırma özelliğine sahip bir kabın içinde bulunur.
agaase

70
Hesaplamalar, öğenin ekrandan daha küçük olduğunu varsayar. Yüksek veya geniş elemanlarınız varsa, kullanmak daha doğru olabilirreturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
Roonaan

11
İpucu: Bunu jQuery ile uygulamaya çalışanlar için isElementInViewport(document.getElementById('elem')), jQuery nesnesini (ör isElementInViewport($("#elem)).) Değil , HTML DOM nesnesini (ör. ) Geçirmesi kolay bir hatırlatmadır . JQuery eşdeğer eklemektir [0]şöyle: isElementInViewport($("#elem)[0]).
jiminy

11
el is not defined
M_Willett

176

Güncelleme

Modern tarayıcılarda, aşağıdaki avantajları sağlayan Kesişim Gözlemcisi API'sına göz atmak isteyebilirsiniz :

  • Kaydırma olaylarını dinlemekten daha iyi performans
  • Web alanları arası iframe'lerde çalışır
  • Bir öğenin başka bir öğeyi engelleyip engellemediğini söyleyebilir

Kavşak Gözlemcisi tam teşekküllü bir standart olma yolunda ilerliyor ve Chrome 51+, Edge 15+ ve Firefox 55+ sürümlerinde zaten destekleniyor ve Safari için geliştirilme aşamasında. Ayrıca bir çoklu dolgu da mevcuttur.


Önceki cevap

Dan'ın verdiği cevapta bazı durumlar için uygun olmayan bir yaklaşım olabilecek bazı sorunlar var . Bu sorunlardan bazıları, dibine yakın olan cevabında, kodunun aşağıdaki öğeler için yanlış pozitifler vereceğine dikkat çekiyor:

  • Test edilenin önünde başka bir eleman tarafından gizlendi
  • Bir üst öğenin veya üst öğe öğesinin görünür alanının dışında
  • CSS clipözelliği kullanılarak gizlenen bir öğe veya alt öğeleri

Bu sınırlamalar basit bir testin aşağıdaki sonuçlarında gösterilmiştir :

İsElementInViewport kullanılarak test başarısız oldu

Çözüm: isElementVisible()

Aşağıda, aşağıdaki test sonucu ve kodun bazı bölümlerinin açıklaması ile bu sorunlara bir çözüm.

function isElementVisible(el) {
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || document.documentElement.clientWidth,
        vHeight  = window.innerHeight || document.documentElement.clientHeight,
        efp      = function (x, y) { return document.elementFromPoint(x, y) };     

    // Return false if it's not in the viewport
    if (rect.right < 0 || rect.bottom < 0 
            || rect.left > vWidth || rect.top > vHeight)
        return false;

    // Return true if any of its four corners are visible
    return (
          el.contains(efp(rect.left,  rect.top))
      ||  el.contains(efp(rect.right, rect.top))
      ||  el.contains(efp(rect.right, rect.bottom))
      ||  el.contains(efp(rect.left,  rect.bottom))
    );
}

Başarılı sınav: http://jsfiddle.net/AndyE/cAY8c/

Ve sonuç:

İsElementVisible kullanarak testi geçti

Ek Notlar

Ancak bu yöntemin kendi sınırlamaları yoktur. Örneğin, aynı konumdaki başka bir öğeden daha düşük bir z-endeksiyle test edilen bir öğe, öndeki öğe aslında herhangi bir bölümünü gizlemese bile gizli olarak tanımlanır. Yine de, bu yöntemin Dan'ın çözümünün kapsamadığı bazı durumlarda kullanımları vardır.

Her ikisi de element.getBoundingClientRect()ve document.elementFromPoint()CSSOM Çalışma Taslağı belirtiminin bir parçasıdır ve en az IE 6 ve sonraki sürümlerde ve çoğu masaüstü tarayıcısında uzun süre (mükemmel olmasa da) desteklenmektedir. Daha fazla bilgi için bkz . Bu işlevler için Quirksmode .

contains()tarafından döndürülen document.elementFromPoint()öğenin görünürlük için test ettiğimiz öğenin alt düğümü olup olmadığını görmek için kullanılır . Ayrıca, döndürülen öğe aynı öğeyse true değerini döndürür. Bu sadece çeki daha sağlam yapar. Tüm büyük tarayıcılarda desteklenir, Firefox 9.0 en son eklenenleridir. Daha eski Firefox desteği için bu yanıtın geçmişini kontrol edin.

Görünürlük için öğenin etrafında daha fazla nokta denemek istiyorsanız, yani, öğenin, örneğin% 50'den daha fazla kapsama alanı içermediğinden emin olmak için cevabın son kısmını ayarlamak çok fazla zaman almaz. Ancak,% 100 görünür olduğundan emin olmak için her pikseli kontrol etmenizin çok yavaş olacağını unutmayın.


2
Bunu mu demek istediniz: doc.documentElement.clientWidth? Bunun yerine 'document.documentElement' olmalı mı? Farklı bir notta, bu, CSS 'klip' ​​özelliğini kullanarak erişilebilirlik için bir öğenin içeriğini gizleme gibi kullanım durumları için de çalışan tek yöntemdir: snook.ca/archives/html_and_css/hiding-content-for-accessibility
Kevin

@klamping: iyi yakalayın, teşekkürler. docTakma ad olarak kullandığım kodumdan kopyaladım document. Evet, bunu uç durumlar için iyi bir çözüm olarak düşünmeyi seviyorum.
Andy E

2
Benim için çalışmıyor. Ancak önceki yanıttaki inViewport (), FF'de çalışıyor.
Satya Prakash

9
Ayrıca, köşeleri yuvarlatılmış veya bir dönüşüm uyguladıysanız, elemanın merkezinin görünür olup olmadığını kontrol etmek de yararlı olabilir, çünkü sınırlayıcı köşeler beklenen elemanı geri element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Jared

2
Benim için girdiler üzerinde çalışmadı (krom kanarya 50). Neden olduğundan emin değilim, belki de yuvarlatılmış köşeler? El.contains (efp (rect.left + 1, rect.top + 1)) çalışmak için coords biraz azaltmak zorunda kaldı || 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

Dan'ın cevabını denedim . Bununla birlikte , sınırları belirlemek için kullanılan cebir, elemanın hem görüntü alanı boyutu hem de görüntü alanının tamamen içinde olması ve truekolayca yanlış negatiflere yol açması gerektiği anlamına gelir . Bir öğenin görünüm alanında olup olmadığını belirlemek istiyorsanız, ryanve'nin yanıtı yakındır, ancak test edilen öğe görünümün üzerine gelmelidir, bu yüzden şunu deneyin:

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

33

Bir kamu hizmeti olarak:
Dan'ın doğru hesaplamalarla yanıtı (öğe, özellikle cep telefonu ekranlarında pencere olabilir) ve doğru jQuery testinin yanı sıra isElementPartiallyInViewport ekleyerek:

Bu arada, window.innerWidth ve document.documentElement.clientWidth arasındaki fark , window.innerWidth / Height öğesinin clientWidth / clientHeight öğesinin kaydırma çubuğunu içermemesidir.

function isElementPartiallyInViewport(el)
{
    // Special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) 
        el = el[0];

    var rect = el.getBoundingClientRect();
    // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
    var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
    var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

    return (vertInView && horInView);
}


// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el)
{
    // Special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) 
        el = el[0];

    var rect = el.getBoundingClientRect();
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    return (
           (rect.left >= 0)
        && (rect.top >= 0)
        && ((rect.left + rect.width) <= windowWidth)
        && ((rect.top + rect.height) <= windowHeight)
    );
}


function fnIsVis(ele)
{
    var inVpFull = isElementInViewport(ele);
    var inVpPartial = isElementPartiallyInViewport(ele);
    console.clear();
    console.log("Fully in viewport: " + inVpFull);
    console.log("Partially in viewport: " + inVpPartial);
}

Test durumu

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Test</title>
    <!--
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script src="scrollMonitor.js"></script>
    -->

    <script type="text/javascript">

        function isElementPartiallyInViewport(el)
        {
            // Special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) 
                el = el[0];

            var rect = el.getBoundingClientRect();
            // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
            var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
            var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

            return (vertInView && horInView);
        }


        // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
        function isElementInViewport (el)
        {
            // Special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) 
                el = el[0];

            var rect = el.getBoundingClientRect();
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            return (
                   (rect.left >= 0)
                && (rect.top >= 0)
                && ((rect.left + rect.width) <= windowWidth)
                && ((rect.top + rect.height) <= windowHeight)
            );
        }


        function fnIsVis(ele)
        {
            var inVpFull = isElementInViewport(ele);
            var inVpPartial = isElementPartiallyInViewport(ele);
            console.clear();
            console.log("Fully in viewport: " + inVpFull);
            console.log("Partially in viewport: " + inVpPartial);
        }


        // var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft,
        // var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
    </script>
</head>

<body>
    <div style="display: block; width: 2000px; height: 10000px; background-color: green;">

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <div style="background-color: crimson; display: inline-block; width: 800px; height: 500px;" ></div>
        <div id="myele" onclick="fnIsVis(this);" style="display: inline-block; width: 100px; height: 100px; background-color: hotpink;">
        t
        </div>

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />
    </div>

    <!--
    <script type="text/javascript">

        var element = document.getElementById("myele");
        var watcher = scrollMonitor.create(element);

        watcher.lock();

        watcher.stateChange(function() {
            console.log("state changed");
            // $(element).toggleClass('fixed', this.isAboveViewport)
        });
    </script>
    -->
</body>
</html>

2
isElementPartiallyInViewportde çok faydalıdır. Güzel bir.
RJ Cuthbertson

İsElementInViewport (e) için: Kodunuz görüntüleri yüklemiyor bile, Bu doğru: fonksiyon isElementInViewport (e) {var t = e.getBoundingClientRect (); 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: Kodumun hiçbiri resim yüklemiyor, bu yüzden neden olmalı ve formül doğru.
Stefan Steiger

1
@targumon: Bunun nedeni eski tarayıcıları desteklemektir.
Stefan Steiger

1
@StefanSteiger, MDN'ye göre IE9'dan beri desteklenmektedir, bu nedenle doğrudan window.innerHeight'ı kullanmak neredeyse güvenlidir (en azından benim durumumda). Teşekkürler!
targumon

28

GetBoundingClientRect kullanan verge kaynağına bakın . Gibi:

function inViewport (el) {

    var r, html;
    if ( !el || 1 !== el.nodeType ) { return false; }
    html = document.documentElement;
    r = el.getBoundingClientRect();

    return ( !!r
      && r.bottom >= 0
      && r.right >= 0
      && r.top <= html.clientHeight
      && r.left <= html.clientWidth
    );

}

Bu döndürür trueeğer herhangi bir elemanın parçası viewport içindedir.


27

Daha kısa ve daha hızlı versiyonum:

function isElementOutViewport(el){
    var rect = el.getBoundingClientRect();
    return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}

Ve gerektiği gibi bir jsFiddle: https://jsfiddle.net/on1g619L/1/


4
Çözümüm daha açgözlü ve daha hızlı, öğe görünüm penceresinde herhangi bir piksel olduğunda, yanlış dönecektir.
Eric Chen

1
Bunu sevdim. Özlü. İlk satırda işlev adı ile parantez arasındaki ve parantez ile küme ayracı arasındaki boşlukları kaldırabilirsiniz. O alanları hiç sevmedim. Belki de sadece metin düzenleyicim renk kodlaması onu okumayı kolaylaştırır. function aaa (arg) {statements} Daha hızlı çalışmasını sağlamadığını biliyorum, yerine küçültme altına düşüyor.
YK

21

Mevcut işlevsellik jQuery merkezli bir sürümü olmadığını rahatsız edici buldum . Ben karşıdan karşıya geldiğinde Dan çözümü jQuery OO tarzında programa gibi millet için bir şeyler sağlama fırsatı casusluk. Güzel ve çabuk ve benim için bir cazibe gibi çalışıyor.

Bada bing bada patlaması

$.fn.inView = function(){
    if(!this.length) 
        return false;
    var rect = this.get(0).getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );

};

// Additional examples for other use cases
// Is true false whether an array of elements are all in view
$.fn.allInView = function(){
    var all = [];
    this.forEach(function(){
        all.push( $(this).inView() );
    });
    return all.indexOf(false) === -1;
};

// Only the class elements in view
$('.some-class').filter(function(){
    return $(this).inView();
});

// Only the class elements not in view
$('.some-class').filter(function(){
    return !$(this).inView();
});

kullanım

$(window).on('scroll',function(){

    if( $('footer').inView() ) {
        // Do cool stuff
    }
});

1
Kıvırcık ayraç if ifadesini kapatmak için yeterli olur mu?
Calasyr

1
Özdeş bir sınıfın birden çok öğesiyle çalışmasını sağlayamadım.
Oz'un

1
@TheWhizofOz cevapladığım diğer olası kullanım örneklerini vermek için cevabımı güncelledim. iyi şanslar.
r3wt

10

Yeni Kavşak Gözlemcisi API'sı bu soruyu çok doğrudan ele alıyor.

Safari, Opera ve Internet Explorer henüz bunu desteklemediğinden bu çözümün bir çoklu dolguya ihtiyacı olacaktır (çoklu dolgu çözüme dahil edilmiştir).

Bu çözümde, hedef (gözlenen) olan bir kutu vardır. Görünmeye geldiğinde, başlığın üstündeki düğme gizlenir. Kutu görünümden ayrıldığında gösterilir.

const buttonToHide = document.querySelector('button');

const hideWhenBoxInView = new IntersectionObserver((entries) => {
  if (entries[0].intersectionRatio <= 0) { // If not in view
    buttonToHide.style.display = "inherit";
  } else {
    buttonToHide.style.display = "none";
  }
});

hideWhenBoxInView.observe(document.getElementById('box'));
header {
  position: fixed;
  top: 0;
  width: 100vw;
  height: 30px;
  background-color: lightgreen;
}

.wrapper {
  position: relative;
  margin-top: 600px;
}

#box {
  position: relative;
  left: 175px;
  width: 150px;
  height: 135px;
  background-color: lightblue;
  border: 2px solid;
}
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<header>
  <button>NAVIGATION BUTTON TO HIDE</button>
</header>
  <div class="wrapper">
    <div id="box">
    </div>
  </div>


3
İyi uygulama ve bağlantıyı göre bu cevap o olmalı ekleyerek safari çalışmak <!DOCTYPE html>HTML
Alon Eitan

Bunun IntersectionObserverdeneysel bir özellik olduğunu unutmayın (gelecekte değişebilir).
Karthik Chintala

2
@KarthikChintala - IE hariç her tarayıcıda desteklenir - ve ayrıca bir çoklu dolgu da mevcuttur.
Randy Casburn

OP'nin sorusunu sadece değişiklikleri tespit ettiği için ele almaz : IntersectionObserversadece hedefin köke göre hareketinden sonra geri arama yapar.
Brandon Hill

Çağrı observeolayı hemen izlendiğinde, izlenen öğenin geçerli kavşak durumunu gösterir. Yani, bir şekilde - hitap ediyor.
Mesqalito

8

Burada karşılaştığım tüm yanıtlar yalnızca öğenin geçerli görünümün içine yerleştirilip yerleştirilmediğini kontrol eder . Ancak bu görünür olduğu anlamına gelmez .
Belirli bir öğe taşan içeriğe sahip bir div içindeyse ve görünümden dışarı kaydırılırsa ne olur?

Bunu çözmek için, öğenin tüm ebeveynler tarafından bulunup bulunmadığını kontrol etmeniz gerekir.
Benim çözüm tam olarak bunu yapar:

Ayrıca, öğenin ne kadarının görünür olması gerektiğini belirtmenize de olanak tanır.

Element.prototype.isVisible = function(percentX, percentY){
    var tolerance = 0.01;   //needed because the rects returned by getBoundingClientRect provide the position up to 10 decimals
    if(percentX == null){
        percentX = 100;
    }
    if(percentY == null){
        percentY = 100;
    }

    var elementRect = this.getBoundingClientRect();
    var parentRects = [];
    var element = this;

    while(element.parentElement != null){
        parentRects.push(element.parentElement.getBoundingClientRect());
        element = element.parentElement;
    }

    var visibleInAllParents = parentRects.every(function(parentRect){
        var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
        var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
        var visiblePercentageX = visiblePixelX / elementRect.width * 100;
        var visiblePercentageY = visiblePixelY / elementRect.height * 100;
        return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY;
    });
    return visibleInAllParents;
};

Bu çözüm, diğer gerçekler nedeniyle elementlerin görünmeyebileceği gerçeğini göz ardı etmiştir opacity: 0.

Bu çözümü Chrome ve Internet Explorer 11'de test ettim.


8

Burada kabul edilen cevabın çoğu kullanım durumunda aşırı derecede karmaşık olduğunu görüyorum. Bu kod işi iyi yapar (jQuery kullanarak) ve tamamen görünür ve kısmen görünür öğeler arasında ayrım yapar:

var element         = $("#element");
var topOfElement    = element.offset().top;
var bottomOfElement = element.offset().top + element.outerHeight(true);
var $window         = $(window);

$window.bind('scroll', function() {

    var scrollTopPosition   = $window.scrollTop()+$window.height();
    var windowScrollTop     = $window.scrollTop()

    if (windowScrollTop > topOfElement && windowScrollTop < bottomOfElement) {
        // Element is partially visible (above viewable area)
        console.log("Element is partially visible (above viewable area)");

    } else if (windowScrollTop > bottomOfElement && windowScrollTop > topOfElement) {
        // Element is hidden (above viewable area)
        console.log("Element is hidden (above viewable area)");

    } else if (scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement) {
        // Element is hidden (below viewable area)
        console.log("Element is hidden (below viewable area)");

    } else if (scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement) {
        // Element is partially visible (below viewable area)
        console.log("Element is partially visible (below viewable area)");

    } else {
        // Element is completely visible
        console.log("Element is completely visible");
    }
});

Kesinlikle $window = $(window)kaydırma işleyicisinin dışında önbellek kullanmalısınız .
sam

4

En basit çözüm desteği olarak Element.getBoundingClientRect () olan mükemmel hale :

function isInView(el) {
    let box = el.getBoundingClientRect();
    return box.top < window.innerHeight && box.bottom >= 0;
}

Bu, mobil tarayıcılarda nasıl çalışır? Çoğu, başlık kaydırma aşağı yukarı gidiyor veya birlikte viewport ilgili arabası ve farklı davranış zaman klavye gösterileri yukarı, bu vb android veya ios eğer bağlı
Kev

@Kev Bu yöntemi ne zaman çağırdığınıza bağlı olarak iyi çalışmalıdır. Pencereyi çağırır ve sonra pencereyi yeniden boyutlandırırsanız, sonuç artık doğru olmayabilir. İstediğiniz işlev türüne bağlı olarak her yeniden boyutlandırma etkinliğinde çağırabilirsiniz. Özel kullanım durumunuz hakkında ayrı bir soru sormaktan ve bana burada ping atmaktan çekinmeyin.
leonheess

3

Bunun daha işlevsel bir yolu olduğunu düşünüyorum. Dan'ın yanıtı özyinelemeli bir bağlamda çalışmaz.

Bu işlev, HTML etiketine kadar herhangi bir düzeyi yinelemeli olarak test ederek öğeniz diğer kaydırılabilir div'ların içinde olduğunda sorunu çözer ve ilk yanlışta durur.

/**
 * fullVisible=true only returns true if the all object rect is visible
 */
function isReallyVisible(el, fullVisible) {
    if ( el.tagName == "HTML" )
            return true;
    var parentRect=el.parentNode.getBoundingClientRect();
    var rect = arguments[2] || el.getBoundingClientRect();
    return (
            ( fullVisible ? rect.top    >= parentRect.top    : rect.bottom > parentRect.top ) &&
            ( fullVisible ? rect.left   >= parentRect.left   : rect.right  > parentRect.left ) &&
            ( fullVisible ? rect.bottom <= parentRect.bottom : rect.top    < parentRect.bottom ) &&
            ( fullVisible ? rect.right  <= parentRect.right  : rect.left   < parentRect.right ) &&
            isReallyVisible(el.parentNode, fullVisible, rect)
    );
};

3

En çok kabul edilen yanıtlar Android'de Google Chrome'u yakınlaştırırken çalışmaz. Dan'ın cevabı ile birlikte , Android'de Chrome'u hesaba katmak için visualViewport kullanılmalıdır. Aşağıdaki örnek yalnızca dikey denetimi dikkate alır ve pencere yüksekliği için jQuery kullanır:

var Rect = YOUR_ELEMENT.getBoundingClientRect();
var ElTop = Rect.top, ElBottom = Rect.bottom;
var WindowHeight = $(window).height();
if(window.visualViewport) {
    ElTop -= window.visualViewport.offsetTop;
    ElBottom -= window.visualViewport.offsetTop;
    WindowHeight = window.visualViewport.height;
}
var WithinScreen = (ElTop >= 0 && ElBottom <= WindowHeight);

2

İşte benim çözümüm. Kaydırılabilir bir kabın içinde bir öğe gizlenmişse çalışır.

İşte bir demo (pencereyi yeniden boyutlandırmayı deneyin)

var visibleY = function(el){
    var top = el.getBoundingClientRect().top, rect, el = el.parentNode;
    do {
        rect = el.getBoundingClientRect();
        if (top <= rect.bottom === false)
            return false;
        el = el.parentNode;
    } while (el != document.body);
    // Check it's within the document viewport
    return top <= document.documentElement.clientHeight;
};

Sadece Y ekseninde görünüp görünmediğini kontrol etmem gerekiyordu (kaydırma Ajax daha fazla yük kaydı özelliği için).


2

Dan'ın çözümüne dayanarak , uygulamayı aynı sayfada birden çok kez kullanmak daha kolay olacak şekilde temizlemeye gitmiştim:

$(function() {

  $(window).on('load resize scroll', function() {
    addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon');
    addClassToElementInViewport($('.another-thing'), 'animate-thing');
    // 👏 repeat as needed ...
  });

  function addClassToElementInViewport(element, newClass) {
    if (inViewport(element)) {
      element.addClass(newClass);
    }
  }

  function inViewport(element) {
    if (typeof jQuery === "function" && element instanceof jQuery) {
      element = element[0];
    }
    var elementBounds = element.getBoundingClientRect();
    return (
      elementBounds.top >= 0 &&
      elementBounds.left >= 0 &&
      elementBounds.bottom <= $(window).height() &&
      elementBounds.right <= $(window).width()
    );
  }

});

Bunu kullanıyorum, öğe görünüme kaydırıldığında, bir CSS ana kare animasyonunu tetikleyen bir sınıf ekliyorum. Oldukça basittir ve bir sayfada koşullu olarak animasyon uygulamak için 10'dan fazla şey olduğunda özellikle iyi çalışır.


Kesinlikle $window = $(window)kaydırma işleyicisinin önbelleğini almalısınız
Adam Rehal

2

Önceki yanıtlardaki kullanımların çoğu şu noktalarda başarısız oluyor:

-Bir öğenin herhangi bir pikseli görünürken, " köşe " " olmadığında,

-Bir öğe görünüm penceresinden daha büyük ve ortalandığında ,

-Çoğu sadece bir belge veya pencere içinde tekil bir öğe olup olmadığını kontrol ediyor .

Tüm bu problemler için bir çözümüm var ve artı taraflar:

-Geri dönebilirsiniz visible bir taraftan bir piksel göründüğünde ve bir köşe olmadığında ,

- Hala dönebilirsin visible öğe görüntü alanından daha büyükken ,

-Seçebilirsiniz parent element veya otomatik olarak seçmesine izin verebilirsiniz,

-Dinamik olarak eklenen öğeler üzerinde de çalışır.

Kullandığınız farkı göreceksiniz Aşağıdaki parçacıkları işaretlerseniz overflow-scrollherhangi bir sorun neden olmaz öğenin kapta ve görüyoruz burada aksine diğer cevaplar bile piksel gösterileri yukarıya doğru eğer herhangi tarafında veya bir eleman görüntü alanından daha büyüktür ve biz görüyoruz hala çalıştığı öğenin pikselleri .

Kullanımı basit:

// For checking element visibility from any sides
isVisible(element)

// For checking elements visibility in a parent you would like to check
var parent = document; // Assuming you check if 'element' inside 'document'
isVisible(element, parent)

// For checking elements visibility even if it's bigger than viewport
isVisible(element, null, true) // Without parent choice
isVisible(element, parent, true) // With parent choice

crossSearchAlgorithmGörüntü alanı kontrol elemanından3 büyük pikselden daha büyük elemanlar için kullanışsız bir tanıtım :

Görüyorsunuz , element3'ün içindeyken görünür olup olmadığını söyleyemiyor, çünkü sadece öğenin kenarlardan veya köşelerden görünür olup olmadığını kontrol ediyoruz .

Ve bu , öğe görünüm penceresinden daha büyük olduğunda crossSearchAlgorithmhala geri dönmenizi sağlayan şunları içerir visible:

JSFiddle ile oynamak için: http://jsfiddle.net/BerkerYuceer/grk5az2c/

Bu kod, öğenin herhangi bir kısmı görünümde gösteriliyorsa veya gösterilmiyorsa daha kesin bilgi için yapılır. Performans seçenekleri veya yalnızca dikey slaytlar için bunu kullanmayın! Bu kod çizim durumlarında daha etkilidir.


1

Daha iyi bir çözüm:

function getViewportSize(w) {
    var w = w || window;
    if(w.innerWidth != null)
        return {w:w.innerWidth, h:w.innerHeight};
    var d = w.document;
    if (document.compatMode == "CSS1Compat") {
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    }
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}


function isViewportVisible(e) {
    var box = e.getBoundingClientRect();
    var height = box.height || (box.bottom - box.top);
    var width = box.width || (box.right - box.left);
    var viewport = getViewportSize();
    if(!height || !width)
        return false;
    if(box.top > viewport.h || box.bottom < 0)
        return false;
    if(box.right < 0 || box.left > viewport.w)
        return false;
    return true;
}

12
Sürümünüzün neden daha iyi olduğunu açıklamaya çalışmalısınız. Haliyle, diğer çözümlerle neredeyse aynı görünüyor.
Andy E

1
Harika bir çözüm bir BOX / ScrollContainer vardır ve WINDOW kullanmıyor (sadece belirtilmemişse). Kodu daha evrensel bir çözüm olduğunu oranı bir göz atın (çok arıyordum)
teter

1

Olabildiğince basit, IMO:

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  return Math.abs(coords.top) <= coords.height;
}

0

Bir öğenin, üst öğenin geçerli görünüm penceresinde görünür olup olmadığını bildiren bir işlev şunlardır :

function inParentViewport(el, pa) {
    if (typeof jQuery === "function"){
        if (el instanceof jQuery)
            el = el[0];
        if (pa instanceof jQuery)
            pa = pa[0];
    }

    var e = el.getBoundingClientRect();
    var p = pa.getBoundingClientRect();

    return (
        e.bottom >= p.top &&
        e.right >= p.left &&
        e.top <= p.bottom &&
        e.left <= p.right
    );
}

0

Bu, bir öğenin en azından kısmen görünümde olup olmadığını kontrol eder (dikey boyut):

function inView(element) {
    var box = element.getBoundingClientRect();
    return inViewBox(box);
}

function inViewBox(box) {
    return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true;
}


function getWindowSize() {
    return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight}
}

0

Aynı soru vardı ve getBoundingClientRect () kullanarak anladım.

Bu kod tamamen 'jenerik' ve çalışması için sadece bir kez yazılması gerekiyor (bilmek istediğiniz her öğe için görünümde yazmak zorunda değilsiniz).

Bu kod yalnızca dikey olarak görünüm alanında olup olmadığını kontrol eder, yatay değil . Bu durumda, değişken (dizi) 'öğeleri' görünüm alanında dikey olarak olmasını kontrol ettiğiniz tüm öğeleri tutar, bu nedenle istediğiniz herhangi bir öğeyi istediğiniz yere alın ve orada saklayın.

'For döngüsü', her öğede dolaşır ve görünüm alanında dikey olup olmadığını kontrol eder. Bu kod , kullanıcı her kaydırdığında yürütülür! GetBoudingClientRect (). Top, görünüm penceresinin 3 / 4'ünden küçükse (öğe görünüm penceresinde dörtte birdir), 'görünüm penceresinde' olarak kaydedilir.

Kod genel olduğundan, görünüm penceresinde 'hangi' öğenin olduğunu bilmek isteyeceksiniz. Bunu bulmak için, özel özellik, düğüm adı, kimlik, sınıf adı ve daha fazlasına göre belirleyebilirsiniz.

İşte kodum (çalışmadığını söyle; Internet Explorer 11, Firefox 40.0.3, Chrome Sürüm 45.0.2454.85 m, Opera 31.0.1889.174 ve Edge ile Windows 10'da test edildi, henüz Safari'de değil ]) ...

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

0

Bu benim için çalışan kolay ve küçük bir çözüm.

Örnek : Öğenin taşma kaydırması olan üst öğede görünür olup olmadığını görmek istiyorsunuz.

$(window).on('scroll', function () {

     var container = $('#sidebar');
     var containerHeight = container.height();
     var scrollPosition = $('#row1').offset().top - container.offset().top;

     if (containerHeight < scrollPosition) {
         console.log('not visible');
     } else {
         console.log('visible');
     }
})

0

Buradaki tüm cevaplar, öğenin yalnızca bir şekilde görünür değil, görünüm alanında tamamen bulunup bulunmadığını belirler. Örneğin, bir görüntünün yalnızca yarısı görünümün altında görünürse, buradaki çözümler "dışarıda" dikkate alındığında başarısız olur.

Ben aracılığıyla tembel yükleme yapıyorum bir kullanım durumunda vardı IntersectionObserver, fakat ben olan tüm görüntüleri gözlemlemek istemiyordu-pop sırasında meydana animasyonlara zaten sayfa yüklemesinde kesişen. Bunu yapmak için aşağıdaki kodu kullandım:

const bounding = el.getBoundingClientRect();
const isVisible = (0 < bounding.top && bounding.top < (window.innerHeight || document.documentElement.clientHeight)) ||
        (0 < bounding.bottom && bounding.bottom < (window.innerHeight || document.documentElement.clientHeight));

Bu temel olarak üst veya alt sınırın bağımsız olarak görünüm alanında olup olmadığını kontrol eder. Karşı uç dışarıda olabilir, ancak bir uç olduğu sürece, en azından kısmen "görülebilir".


-1

Bu işlevi kullanıyorum (çoğu zaman x gerekli olmadığından yalnızca y'nin ekran olup olmadığını kontrol eder)

function elementInViewport(el) {
    var elinfo = {
        "top":el.offsetTop,
        "height":el.offsetHeight,
    };

    if (elinfo.top + elinfo.height < window.pageYOffset || elinfo.top > window.pageYOffset + window.innerHeight) {
        return false;
    } else {
        return true;
    }

}

-3

Benzer bir meydan okuma için, scrollIntoViewIfNeeded () için bir çoklu dolgu gösteren bu özü gerçekten keyif aldım .

Cevaplamak için gereken tüm Kung Fu bu blokta:

var parent = this.parentNode,
    parentComputedStyle = window.getComputedStyle(parent, null),
    parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
    parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
    overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
    overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
    overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
    overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
    alignWithTop = overTop && !overBottom;

thisörneğin, olup olmadığını bilmek istediğiniz öğeye atıfta bulunur, overTopya da overBottom- sadece sapmayı almalısınız ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.