Apakah ada cara yang efisien untuk mengetahui apakah elemen DOM (dalam dokumen HTML) saat ini terlihat (muncul di viewport )?
(Pertanyaannya mengacu pada Firefox.)
Apakah ada cara yang efisien untuk mengetahui apakah elemen DOM (dalam dokumen HTML) saat ini terlihat (muncul di viewport )?
(Pertanyaannya mengacu pada Firefox.)
Jawaban:
Pembaruan: Waktu berjalan terus dan begitu juga browser kami. Teknik ini tidak lagi direkomendasikan dan Anda harus menggunakan solusi Dan jika Anda tidak perlu mendukung versi Internet Explorer sebelum 7.
Solusi asli (sekarang kedaluwarsa):
Ini akan memeriksa apakah elemen tersebut sepenuhnya terlihat 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 memodifikasi ini hanya untuk menentukan apakah ada bagian elemen 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
);
}
getBoundingClientRect
secara spesifik tujuan menemukan koordinat elemen ... Mengapa kita tidak menggunakannya?
Sekarang sebagian besar browser mendukung metode getBoundingClientRect , yang telah menjadi praktik terbaik. Menggunakan jawaban lama sangat lambat , tidak akurat dan memiliki beberapa bug .
Solusi yang dipilih sebagai benar hampir tidak pernah tepat . Anda dapat membaca lebih lanjut tentang bug-nya.
Solusi ini diuji pada Internet Explorer 7 (dan yang lebih baru), iOS 5 (dan yang lebih baru) Safari, Android 2.0 (Eclair) dan yang lebih baru, BlackBerry, Opera Mobile, dan Internet Explorer Mobile 9 .
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() */
);
}
Anda dapat yakin bahwa fungsi yang diberikan di atas mengembalikan jawaban yang benar pada saat dipanggil, tetapi bagaimana dengan visibilitas elemen pelacakan sebagai suatu peristiwa?
Tempatkan kode berikut di bagian bawah <body>
tag Anda :
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);
}
*/
Jika Anda melakukan modifikasi DOM, mereka dapat mengubah visibilitas elemen Anda tentu saja.
Panduan dan perangkap umum:
Mungkin Anda perlu melacak halaman zoom / cubit perangkat seluler? jQuery harus menangani zoom / pinch cross browser, jika tidak, pertama atau kedua tautan akan membantu Anda.
Jika Anda memodifikasi DOM , ini dapat memengaruhi visibilitas elemen. Anda harus mengambil kendali atas itu dan menelepon handler()
secara manual. Sayangnya, kami tidak memiliki browser lintasonrepaint
acara . Di sisi lain yang memungkinkan kami melakukan optimasi dan melakukan pengecekan ulang hanya pada modifikasi DOM yang dapat mengubah visibilitas elemen.
Never Ever menggunakannya di dalam jQuery $ (document) .ready () saja, karena tidak ada jaminan CSS telah diterapkan pada saat ini. Kode Anda dapat bekerja secara lokal dengan CSS Anda pada hard drive, tetapi sekali memakai server jauh itu akan gagal.
Setelah DOMContentLoaded
dipecat, gaya diterapkan , tetapi gambar belum dimuat . Jadi, kita harus menambahkan window.onload
pendengar acara.
Kami belum dapat menangkap acara zoom / pinch.
Pilihan terakhir bisa berupa kode berikut:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Anda dapat menggunakan pageVisibiliy fitur hebat dari API HTML5 jika Anda peduli jika tab dengan halaman web Anda aktif dan terlihat.
TODO: metode ini tidak menangani dua situasi:
z-index
overflow-scroll
dalam wadah elemenreturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))
) dan bukan objek jQuery (misalnya, isElementInViewport($("#elem))
). Setara jQuery adalah menambahkan[0]
seperti: isElementInViewport($("#elem)[0])
.
el is not defined
Di browser modern, Anda mungkin ingin memeriksa API Pengamat titik - temu yang memberikan manfaat berikut:
Intersection Observer sedang dalam perjalanan untuk menjadi standar penuh dan sudah didukung di Chrome 51+, Edge 15+ dan Firefox 55+ dan sedang dikembangkan untuk Safari. Tersedia juga polyfill .
Ada beberapa masalah dengan jawaban yang diberikan Dan yang mungkin membuatnya menjadi pendekatan yang tidak cocok untuk beberapa situasi. Beberapa masalah ini ditunjukkan dalam jawabannya di dekat bagian bawah, bahwa kodenya akan memberikan hasil positif palsu untuk elemen yang:
clip
properti CSSBatasan ini ditunjukkan dalam hasil tes sederhana berikut :
isElementVisible()
Inilah solusi untuk masalah-masalah tersebut, dengan hasil tes di bawah ini dan penjelasan dari beberapa bagian kode
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))
);
}
Lulus tes: http://jsfiddle.net/AndyE/cAY8c/
Dan hasilnya:
Namun metode ini bukan tanpa batasannya sendiri. Misalnya, elemen yang diuji dengan indeks-z lebih rendah daripada elemen lain di lokasi yang sama akan diidentifikasi sebagai disembunyikan bahkan jika elemen di depan tidak benar-benar menyembunyikan bagiannya. Namun, metode ini memiliki kegunaan dalam beberapa kasus yang tidak dicakup solusi Dan.
Keduanya element.getBoundingClientRect()
dan document.elementFromPoint()
merupakan bagian dari spesifikasi Draft Kerja CSSOM dan didukung di setidaknya IE 6 dan yang lebih baru dan sebagian besar browser desktop untuk waktu yang lama (meskipun, tidak sempurna). Lihat Quirksmode pada fungsi-fungsi ini untuk informasi lebih lanjut.
contains()
digunakan untuk melihat apakah elemen yang dikembalikan oleh document.elementFromPoint()
adalah simpul anak dari elemen yang kami uji untuk visibilitas. Ini juga mengembalikan true jika elemen yang dikembalikan adalah elemen yang sama. Ini hanya membuat pemeriksaan lebih kuat. Ini didukung di semua browser utama, Firefox 9.0 menjadi yang terakhir yang menambahkannya. Untuk dukungan Firefox yang lebih lama, periksa riwayat jawaban ini.
Jika Anda ingin menguji lebih banyak poin di sekitar elemen untuk visibilitas ― yaitu, untuk memastikan elemen tidak tercakup oleh lebih dari, katakanlah, 50% ― tidak perlu banyak untuk menyesuaikan bagian terakhir dari jawaban. Namun, perlu diketahui bahwa mungkin akan sangat lambat jika Anda memeriksa setiap piksel untuk memastikan itu 100% terlihat.
doc
sebagai alias untuk document
. Ya, saya suka menganggap ini sebagai solusi yang layak untuk kasus tepi.
element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Saya mencoba jawaban Dan . Namun , aljabar yang digunakan untuk menentukan batas berarti bahwa elemen tersebut harus berukuran ≤ ukuran viewport dan sepenuhnya berada di dalam viewport true
, sehingga mudah mengarah ke negatif palsu. Jika Anda ingin menentukan apakah suatu elemen ada di viewport sama sekali, jawaban ryanve dekat, tetapi elemen yang diuji 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) /* or $(window).width() */ &&
rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;
}
Sebagai layanan publik:
Jawaban Dan dengan perhitungan yang benar (elemen dapat> jendela, terutama pada layar ponsel), dan pengujian jQuery yang benar, serta menambahkan isElementPartiallyInViewport:
By the way, perbedaan antara window.innerWidth dan document.documentElement.clientWidth adalah bahwa clientWidth / clientHeight tidak termasuk scrollbar, sedangkan 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);
}
<!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>
isElementPartiallyInViewport
sangat berguna juga. Bagus
Lihat sumber ambang , yang menggunakan 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
);
}
Itu kembali true
jika ada bagian dari elemen di viewport.
Versi saya yang 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;
}
Dan jsFiddle sesuai kebutuhan: https://jsfiddle.net/on1g619L/1/
Saya merasa bermasalah bahwa tidak ada versi jQuery- sentris dari fungsi yang tersedia. Ketika saya menemukan solusi Dan, saya memata-matai kesempatan untuk menyediakan sesuatu untuk orang-orang yang suka memprogram dalam gaya jQuery OO. Sangat bagus dan tajam dan bekerja 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
// 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();
});
Pemakaian
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// Do cool stuff
}
});
API Pengamat Titik- Temu yang baru menjawab pertanyaan ini dengan sangat langsung.
Solusi ini akan memerlukan polyfill karena Safari, Opera dan Internet Explorer belum mendukung ini (polyfill termasuk dalam solusi).
Dalam solusi ini, ada kotak di luar pandangan yang menjadi target (diamati). Ketika muncul, tombol di bagian atas di header disembunyikan. Ini ditampilkan setelah kotak meninggalkan tampilan.
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>
<!DOCTYPE html>
ke HTML
IntersectionObserver
ini adalah fitur eksperimental (yang dapat berubah di masa mendatang).
IntersectionObserver
hanya mengaktifkan callback setelah perpindahan target relatif ke root.
observe
acara dipecat segera memberi tahu Anda saat ini persimpangan elemen dilacak. Jadi, dalam beberapa cara - itu alamat.
Semua jawaban yang saya temui di sini hanya memeriksa apakah elemen diposisikan di dalam viewport saat ini . Tetapi itu tidak berarti bahwa itu terlihat .
Bagaimana jika elemen yang diberikan ada di dalam div dengan konten yang meluap, dan itu tidak terlihat?
Untuk mengatasinya, Anda harus memeriksa apakah elemen tersebut terkandung oleh semua orang tua.
Solusi saya melakukan hal itu:
Ini juga memungkinkan Anda menentukan berapa banyak elemen 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;
};
Solusi ini mengabaikan fakta bahwa elemen mungkin tidak terlihat karena fakta lain, seperti opacity: 0
.
Saya telah menguji solusi ini di Chrome dan Internet Explorer 11.
Saya menemukan bahwa jawaban yang diterima di sini terlalu rumit untuk kebanyakan kasus penggunaan. Kode ini melakukan pekerjaan 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");
}
});
$window = $(window)
luar scroll handler.
Solusi paling sederhana sebagai dukungan Element.getBoundingClientRect () telah menjadi sempurna :
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
Saya pikir ini adalah cara yang lebih fungsional untuk melakukannya. Jawaban Dan tidak bekerja dalam konteks rekursif.
Fungsi ini memecahkan masalah ketika elemen Anda berada di dalam div yang lain dapat digulir dengan menguji setiap level secara rekursif hingga tag HTML, dan berhenti pada false pertama.
/**
* 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)
);
};
Jawaban yang paling diterima tidak berfungsi saat memperbesar Google Chrome di Android. Dalam kombinasi dengan jawaban Dan , untuk menjelaskan Chrome di Android, visualViewport harus digunakan. Contoh berikut hanya mengambil pemeriksaan vertikal ke akun dan menggunakan jQuery untuk ketinggian jendela:
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);
Inilah solusi saya. Ini akan berfungsi jika suatu elemen disembunyikan di dalam wadah yang dapat digulir.
Ini demo (coba ubah ukuran jendela menjadi)
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;
};
Saya hanya perlu memeriksa apakah itu terlihat di sumbu Y (untuk fitur Ajax scroll-more-records scrolling).
Berdasarkan solusi Dan , saya harus membersihkan implementasi sehingga menggunakannya berkali-kali pada halaman yang sama 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 ketika elemen bergulir ke tampilan, saya menambahkan kelas yang memicu animasi keyframe CSS. Ini sangat mudah dan bekerja dengan sangat baik ketika Anda memiliki lebih dari 10 hal untuk dikondisikan secara kondisional pada sebuah halaman.
$window = $(window)
luar scroll handler
Sebagian besar penggunaan dalam jawaban sebelumnya gagal pada titik-titik ini:
-Ketika setiap piksel elemen terlihat, tetapi bukan " sudut ",
-Ketika elemen lebih besar dari viewport dan terpusat ,
-Sebagian besar dari mereka hanya memeriksa elemen tunggal di dalam dokumen atau jendela .
Nah, untuk semua masalah ini saya punya solusi dan sisi positifnya adalah:
-Anda dapat kembali
visible
ketika hanya piksel dari sisi mana pun yang muncul dan bukan sudut,-Anda masih dapat kembali
visible
sementara elemen lebih besar dari viewport,-Anda dapat memilih
parent element
atau secara otomatis membiarkannya memilih,-Bekerja pada elemen yang ditambahkan secara dinamis juga.
Jika Anda memeriksa cuplikan di bawah ini, Anda akan melihat perbedaan dalam menggunakan overflow-scroll
wadah elemen tidak akan menimbulkan masalah dan melihat bahwa tidak seperti jawaban lain di sini bahkan jika piksel muncul dari sisi mana pun atau ketika suatu elemen lebih besar dari viewport dan kami melihat bagian dalamnya piksel elemen yang masih berfungsi.
Penggunaannya sederhana:
// 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
Demonstrasi tanpa crossSearchAlgorithm
yang berguna untuk elemen yang lebih besar dari viewport check element3 piksel dalam untuk melihat:
Anda lihat, ketika Anda berada di dalam elemen3 itu gagal untuk mengetahui apakah itu terlihat atau tidak, karena kami hanya memeriksa apakah elemen tersebut terlihat dari sisi atau sudut .
Dan ini termasuk crossSearchAlgorithm
yang memungkinkan Anda untuk tetap kembali visible
ketika elemen lebih besar dari viewport:
JSFiddle untuk dimainkan: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Kode ini dibuat untuk informasi yang lebih tepat jika ada bagian elemen yang ditampilkan dalam tampilan atau tidak. Untuk opsi kinerja atau hanya slide vertikal, jangan gunakan ini! Kode ini lebih efektif dalam menggambar kasus.
Solusi yang lebih baik:
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;
}
Berikut adalah fungsi yang memberi tahu jika suatu elemen terlihat di viewport saat ini dari elemen induk :
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
);
}
Ini memeriksa apakah suatu elemen setidaknya sebagian terlihat (dimensi vertikal):
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}
}
Saya memiliki pertanyaan yang sama dan menemukan jawabannya dengan menggunakan getBoundingClientRect ().
Kode ini sepenuhnya 'generik' dan hanya perlu ditulis satu kali agar bisa berfungsi (Anda tidak harus menuliskannya untuk setiap elemen yang ingin Anda ketahui ada di viewport).
Kode ini hanya memeriksa untuk melihat apakah itu secara vertikal di viewport, bukan horizontal . Dalam hal ini, 'elemen' variabel (array) menampung semua elemen yang Anda periksa secara vertikal di viewport, jadi ambil elemen apa pun yang Anda inginkan di mana saja dan simpan di sana.
'For loop', loop melalui setiap elemen dan memeriksa untuk melihat apakah itu secara vertikal di viewport. Kode ini dijalankan setiap kali pengguna menggulir! Jika getBoudingClientRect (). Top kurang dari 3/4 viewport (elemennya adalah seperempat di viewport), ia terdaftar sebagai 'di viewport'.
Karena kodenya generik, Anda ingin tahu elemen 'mana' yang ada di viewport. Untuk mengetahuinya, Anda dapat menentukannya dengan atribut khusus, nama simpul, id, nama kelas, dan banyak lagi.
Ini kode saya (beri tahu saya jika tidak berfungsi; sudah diuji di Internet Explorer 11, Firefox 40.0.3, Versi Chrome 45.0.2454.85 m, Opera 31.0.1889.174, dan Edge dengan Windows 10, [belum Safari belum ]) ...
// 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.');
}
};
Ini adalah solusi mudah dan kecil yang berhasil bagi saya.
Contoh : Anda ingin melihat apakah elemen terlihat di elemen induk yang memiliki gulir melimpah.
$(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');
}
})
Semua jawaban di sini menentukan apakah elemen tersebut sepenuhnya terkandung dalam viewport, tidak hanya terlihat dalam beberapa cara. Misalnya, jika hanya setengah dari gambar terlihat di bagian bawah tampilan, solusi di sini akan gagal, mengingat "di luar".
Saya memiliki use case di mana saya melakukan pemuatan malas melalui IntersectionObserver
, tetapi karena animasi yang terjadi selama pop-in, saya tidak ingin mengamati gambar yang sudah berpotongan pada pemuatan halaman. Untuk melakukan itu, saya menggunakan kode berikut:
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));
Ini pada dasarnya memeriksa untuk melihat apakah bagian atas atau bawah terikat secara independen di viewport. Ujung yang berlawanan mungkin berada di luar, tetapi selama salah satu ujung berada, itu "terlihat" setidaknya sebagian.
Saya menggunakan fungsi ini (ini hanya memeriksa apakah y adalah inscreen karena sebagian besar waktu x tidak diperlukan)
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;
}
}
Untuk tantangan serupa, saya sangat menikmati inti ini yang memperlihatkan polyfill untuk scrollIntoViewIfNeeded () .
Semua Kung Fu yang diperlukan untuk menjawab ada di dalam blok ini:
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
mengacu pada elemen yang Anda ingin tahu apakah itu, misalnya, overTop
atau overBottom
- Anda hanya perlu mengetahui ...