Bagaimana cara menentukan apakah elemen DOM ditampilkan di viewport saat ini?

Apakah ada cara yang efektif untuk mengetahui apakah elemen DOM (dalam dokumen HTML) saat ini terlihat (ditampilkan di viewport )?

(Pertanyaannya menyangkut Firefox)

787
24 сент. diatur oleh benzaita 24 September . 2008-09-24 00:24 '08 pada 0:24 2008-09-24 00:24
@ 25 jawaban

Perbarui: Waktu terus berdetak, dan kami memiliki browser kami. Metode ini tidak lagi direkomendasikan , dan Anda harus menggunakan solusi @Dan di bawah ini ( encoreci.net.site/questions/10631 / ... ) jika Anda tidak perlu mendukung IE <7.

Solusi asli (sekarang kedaluwarsa):

Ini akan memeriksa apakah item akan sepenuhnya ditampilkan di viewport saat ini:

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

Anda dapat mengubah ini hanya untuk menentukan apakah ada bagian dari item yang terlihat di viewport:

 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 ); } 
301
24 сент. Jawabannya diberikan oleh Prestaul 24 Sep 2008-09-24 05:40 '08 pada 5:40 pagi 2008-09-24 05:40

Sekarang sebagian besar browser mendukung metode getBoundingClientRect , yang telah menjadi praktik terbaik. Menggunakan jawaban yang lama sangat lambat , tidak akurat dan memiliki beberapa kesalahan .

Solusi yang dipilih sebagai benar hampir tidak pernah ditentukan . Anda dapat membaca lebih lanjut tentang kesalahan Anda.


Solusi ini diuji pada IE7 +, iOS5 + Safari, Android2 +, Blackberry, Opera Mobile dan IE Mobile 10 .


 function isElementInViewport (el) { //special bonus for those using jQuery if (typeof jQuery === "function"  el instanceof jQuery) { el = el[0]; } var rect = el.getBoundingClientRect(); return ( rect.top >= 0  rect.left >= 0  rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)   rect.right <= (window.innerWidth || document.documentElement.clientWidth)  ); } 

Cara menggunakan:

Anda dapat yakin bahwa fungsi di atas mengembalikan jawaban yang benar pada titik waktu dipanggil, tetapi bagaimana dengan visibilitas elemen pelacakan sebagai suatu peristiwa?

Letakkan kode berikut di bagian bawah <body> :

 function onVisibilityChange(el, callback) { var old_visible; return function () { var visible = isElementInViewport(el); if (visible != old_visible) { old_visible = visible; if (typeof callback == 'function') { callback(); } } } } var handler = onVisibilityChange(el, function() {  }); //jQuery $(window).on('DOMContentLoaded load resize scroll', handler);  

Jika Anda membuat perubahan apa pun pada DOM, mereka dapat secara alami mengubah visibilitas elemen.

Rekomendasi dan kesalahan umum:

Mungkin Anda perlu melacak penskalaan halaman / pengikatan perangkat seluler? jQuery harus menangani scaling / clamping cross-browser, jika tidak tautan pertama atau kedua akan membantu Anda.

Jika Anda mengubah DOM , itu dapat mempengaruhi visibilitas elemen. Anda harus mengontrolnya dan memanggil handler() secara manual. Sayangnya, kami tidak memiliki peramban si>onrepaint . Di sisi lain, ini memungkinkan kami untuk mengoptimalkan dan memeriksa u>

Jangan pernah menggunakannya di dalam jQuery $ (dokumen) .ready () , karena saat ini tidak ada jaminan CSS. Kode Anda dapat bekerja secara lokal dengan CSS Anda di hard drive Anda, tetapi setelah menginstalnya pada server jauh itu tidak berfungsi.

Setelah meluncurkan DOMContentLoaded gaya diterapkan, tetapi gambar belum dimuat . Jadi, kita perlu menambahkan window.onload pendengar acara.

Kami masih tidak dapat menangkap acara zoom / pinch.

Pilihan terakhir mungkin adalah kode berikut:

  setInterval(handler, 600); 

Anda dapat menggunakan fungsi API HTML5 pageVisibiliy yang luar biasa jika Anda tertarik, tab aktif dan terlihat dengan halaman web Anda.

TODO: metode ini tidak menangani dua situasi:

1219
26 сент. balasan diberikan Dan 26 sept. 2011-09-26 18:27 '11 pada 18:27 2011-09-26 18:27

Perbarui

Di browser modern, Anda dapat memeriksa API persimpangan , yang memberikan manfaat berikut:

  • Performa yang lebih baik daripada mendengarkan acara gulir.
  • Berfungsi dalam iframe lintas domain
  • Dapat menentukan apakah suatu barang mengha>

Pengamat titik-temu sedang menuju standar penuh dan sudah didukung di Chrome 51+, Edge 15+ dan Firefox 55+ dan sedang dalam pengembangan untuk Safari. Tersedia juga polyfill .


Jawaban sebelumnya

Ada beberapa masalah dengan jawaban yang diberikan Dan , yang dapat membuatnya tidak cocok untuk beberapa situasi. Beberapa masalah ini ditunjukkan dalam jawabannya di bagian bawah, bahwa kodenya akan memberikan positif palsu untuk elemen yang:

  • Menyembunyikan elemen lain di depan tes.
  • Di luar area yang terlihat elemen induk atau leluhur
  • Elemen atau anak-anaknya disembunyikan oleh clip CSS

Batasan ini ditunjukkan dalam hasil tes sederhana berikut:

2019

138
04 марта '13 в 17:18 2013-03-04 17:18 jawabannya diberikan oleh Andy E 04 Maret 13 di 17:18 2013-03-04 17:18

Saya mencoba menjawab Dan, tetapi aljabar yang digunakan untuk mendefinisikan batasan berarti bahwa elemen tersebut harus ≤ ukuran viewport, atau sepenuhnya di dalam viewport untuk menjadi true , yang dengan mudah mengarah ke negatif palsu. Jika Anda ingin menentukan apakah suatu item ada di viewport sama sekali, jawaban untuk ryanve sudah dekat, tetapi item yang akan diperiksa harus tumpang tindih dengan viewport, jadi coba ini:

 function isElementInViewport(el) { var rect = el.getBoundingClientRect(); return rect.bottom > 0  rect.right > 0  rect.left < (window.innerWidth || document.documentElement.clientWidth)   rect.top < (window.innerHeight || document.documentElement.clientHeight) ; } 
48
29 апр. jawabannya diberikan pada 29 April. 2013-04-29 05:36 '13 pada 5:36 2013-04-29 05:36

Sebagai layanan komunitas:
Dan merespons dengan perhitungan yang benar (elemen dapat> sebuah jendela, terutama pada layar ponsel), dan memperbaiki pengujian jQuery dan menambahkan isElementPartiallyInViewport:

By the way, perbedaan antara window.innerWidth dan document.documentElement.clientWidth adalah clientWidth / clientHeight tidak termasuk bilah gulir, dan window.innerWidth / Height tidak.

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

Uji kasus

 <!DOCTYPE html> <html > 
28
25 сент. Jawabannya diberikan oleh Stefan Steiger 25 September. 2014-09-25 15:54 '14 pada 15:54 2014-09-25 15:54

Ada plugin jQuery yang disebut inview yang berfungsi

25
03 мая '10 в 20:00 2010-05-03 20:00 Jawaban diberikan oleh Yuri Salimovskiy pada 03 Mei '10 pukul 20:00 2010-05-03 20:00

Lihat sumber ambang untuk getBoundingClientRect . Itu seperti:

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

Mengembalikan nilai true jika ada bagian dari item di viewport.

24
14 сент. jawaban diberikan ryanve 14 sept . 2012-09-14 08:49 '12 pada jam 8:49 2012-09-14 08:49

versi saya lebih pendek dan lebih cepat.

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

tambahkan jsFiddle sesuai kebutuhan https://jsfiddle.net/on1g619L/1/

22
23 апр. Jawaban diberikan oleh Eric Chen pada 23 April 2014-04-23 06:04 '14 pada 6:04 2014-04-23 06:04

Tampak bagi saya bahwa masalahnya adalah tidak ada fungsionalitas jQuery tersedia. Ketika saya menemukan solusi Dan , saya mencoba memberikan sesuatu untuk orang-orang yang suka memprogram dalam gaya jQuery OO. Pastikan untuk menggulung halaman dan meninggalkan tulisan pada kode Dan. Bagus dan cepat dan berfungsi seperti pesona bagi saya.

bada bing bada booming

 $.fn.inView = function(){ if(!this.length) return false; var rect = this.get(0).getBoundingClientRect(); return ( rect.top >= 0  rect.left >= 0  rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)  rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }; //additional examples for other use cases //true false whether an array of elements are all in view $.fn.allInView = function(){ var all = []; this.forEach(function(){ all.push( $(this).inView() ); }); return all.indexOf(false) === -1; }; //only the class elements in view $('.some-class').filter(function(){ return $(this).inView(); }); //only the class elements not in view $('.some-class').filter(function(){ return !$(this).inView(); }); 

Penggunaan

 $(window).on('scroll',function(){ if( $('footer').inView() ) { // do cool stuff } }); 
19
02 авг. jawabannya diberikan r3wt 02 Agustus. 2015-08-02 16:40 '15 pada 16:40 2015-08-02 16:40

Saya percaya bahwa jawaban yang diterima di sini terlalu rumit untuk sebagian besar kegunaan. Kode ini berfungsi dengan baik (menggunakan jQuery) dan membedakan antara elemen yang sepenuhnya terlihat dan sebagian terlihat.

 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"); } }); 
8
30 янв. jawabannya diberikan oleh Adam Rehal pada bulan Januari. 2015-01-30 17:49 15 pada 17:49 2015-01-30 17:49

http://www.appelsiini.net/projects/viewport

Plugin hebat yang mudah digunakan, cukup gunakan: in-viewport

7
14 дек. Jawabannya diberikan oleh Maxim Siebert 14 Des. 2012-12-14 04:30 '12 pada pukul 4:30 pagi 2012-12-14 04:30

Saya pikir ini adalah cara yang lebih fungsional untuk melakukan ini. Jawaban "dan" tidak bekerja dalam konteks rekursif.

Fungsi ini memecahkan masalah ketika elemen Anda berada di dalam div lain yang dapat digulir, memeriksa setiap level secara rekursif di atas tag HTML, dan berhenti di false pertama.

  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
24 апр. jawabannya diberikan ton 24 Apr 2015-04-24 18:12 '15 pada 18:12 2015-04-24 18:12

Semua jawaban yang saya temui di sini hanya memeriksa apakah item tersebut ada di dalam viewport saat ini . Tetapi ini tidak berarti bahwa itu terlihat .
Bagaimana jika elemen ini ada di dalam div dengan konten yang meluap, dan itu menggelinding keluar dari pandangan?

Untuk mengatasi masalah ini, Anda perlu memeriksa apakah item tersebut berisi semua orang tua.
Solusi saya melakukan hal itu:

Ini juga memungkinkan Anda menentukan bagian elemen mana yang harus terlihat.

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

Keputusan ini mengabaikan fakta bahwa elemen mungkin tidak terlihat karena fakta lain, seperti opacity: 0 .

Saya menguji solusi ini di Chrome dan Internet Explorer 11.

3
23 июня '16 в 20:47 2016-06-23 20:47 jawabannya diberikan oleh Domysee pada 23 Juni '16 di 20:47 2016-06-23 20:47

Berdasarkan solusi @dan di atas ( encoreci.net.site/questions/10631 / ... ), saya memutuskan untuk membersihkannya, jadi menggunakannya beberapa kali pada satu halaman lebih mudah:

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

Cara saya menggunakannya adalah bahwa ketika suatu item bergulir ke tampilan, saya menambahkan kelas yang memulai animasi keyframe css. Ini cukup sederhana dan berfungsi dengan baik ketika Anda memiliki 10+ hal yang dapat dijelaskan secara kondisional pada halaman.

Semoga ini bisa membantu!

2
27 дек. Balas diberikan oleh Pirijan 27 Des 2014-12-27 00:00 '14 pukul 0:00 2014-12-27 00:00

Tergantung pada apa yang Anda maksud dengan yang terlihat. Jika Anda bermaksud bahwa ini saat ini ditampilkan pada halaman, mengingat posisi gulir, Anda dapat menghitungnya berdasarkan perpindahan elemen y dan posisi gulir saat ini.

2
24 сент. jawabannya diberikan roryf 24 september . 2008-09-24 00:29 '08 pada 0:29 2008-09-24 00:29

Solusi mudah dan kecil yang bekerja untuk saya.

Sebuah contoh Anda ingin melihat apakah elemen ditampilkan di elemen induk dengan overflow.

 $(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'); } }) 
1
19 июня '17 в 13:49 2017-06-19 13:49 Jawaban diberikan oleh Stevan Tosic pada 19 Juni '17 di 13:49 2017-06-19 13:49

Ini solusi saya, ini akan berfungsi jika item itu tersembunyi di dalam wadah bergulir.

Ini demo (coba ubah ukuran jendela)

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

Saya perlu memeriksa apakah terlihat pada sumbu Y (untuk menggulir beban ajax ada lebih banyak fungsi perekaman).

1
07 февр. Jawabannya diberikan Ally 07 Feb. 2014-02-07 14:39 '14 pada 14:39 2014-02-07 14:39

Berikut adalah fungsi yang melaporkan apakah suatu item terlihat di jendela tampilan induk saat ini:

 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
ответ дан ssten 28 сент. '18 в 13:01 2018-09-28 13:01