Чи є ефективний спосіб визначити, чи в даний момент елемент DOM (у документі HTML) видно (відображається у вікні перегляду )?
(Питання стосується Firefox.)
Чи є ефективний спосіб визначити, чи в даний момент елемент DOM (у документі HTML) видно (відображається у вікні перегляду )?
(Питання стосується Firefox.)
Відповіді:
Оновлення: Час марширується і так є у наших браузерів. Ця методика більше не рекомендується, і ви повинні використовувати рішення Дана, якщо вам не потрібно підтримувати версію Internet Explorer до 7.
Оригінальне рішення (зараз застаріле):
Це дозволить перевірити, чи елемент повністю видимий у поточному вікні перегляду:
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)
);
}
Ви можете змінити це, щоб визначити, чи якась частина елемента видно у вікні перегляду:
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
для того, щоб знайти координати елементів ... Чому б ми не використали його?
Зараз більшість браузерів підтримують метод getBoundingClientRect , який став найкращою практикою. Використання старої відповіді дуже повільне , неточне та має кілька помилок .
Рішення, вибране як правильне, майже ніколи не є точним . Ви можете прочитати більше про його помилки.
Це рішення було протестовано на Internet Explorer 7 (і пізніших версіях), iOS 5 (і новіших версіях), Safari, Android 2.0 (Eclair) та новіших версіях, BlackBerry, Opera Mobile та 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() */
);
}
Ви можете бути впевнені, що наведена вище функція повертає правильну відповідь у той момент, коли вона викликається, але як щодо видимості елемента відстеження як події?
Розташуйте такий код внизу <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() {
/* 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);
}
*/
Якщо ви робите будь-які модифікації DOM, вони, звичайно, можуть змінювати видимість вашого елемента.
Вказівки та загальні підводні камені:
Можливо, вам потрібно відстежувати масштабування сторінки / щіпку мобільного пристрою? jQuery повинен обробляти перехресний перемикач масштабування / стискання , інакше перше чи друге посилання повинно вам допомогти.
Якщо ви модифікуєте DOM , це може вплинути на видимість елемента. Ви повинні взяти на себе контроль над цим і зателефонувати handler()
вручну. На жаль, у нас немає жодної крос-браузерної onrepaint
події. З іншого боку, що дозволяє нам робити оптимізацію та проводити повторну перевірку лише на модифікаціях DOM, які можуть змінити видимість елемента.
Ніколи не використовуйте його всередині jQuery $ (document) .ready () , тому що в цей момент жодної гарантії CSS не застосовується . Ваш код може працювати локально з вашим CSS на жорсткому диску, але після того, як поставити його на віддалений сервер, він вийде з ладу.
Після DOMContentLoaded
запуску стилі застосовуються , але зображення ще не завантажені . Отже, ми повинні додати window.onload
слухача подій.
Поки ми не можемо зловити масштаб / пінчінг.
Останнім засобом може бути наступний код:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Ви можете використовувати дивовижну сторінку функційVisibiliy API HTML5, якщо вам важливо, чи вкладка з вашою веб-сторінкою активна та видима.
TODO: цей метод не вирішує дві ситуації:
z-index
overflow-scroll
в контейнері елементаreturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))
), а не об’єкт jQuery (наприклад, isElementInViewport($("#elem))
). JQuery еквівалент додати [0]
наступним чином: isElementInViewport($("#elem)[0])
.
el is not defined
У сучасних браузерах ви можете перевірити API Intersection Observer, який забезпечує такі переваги:
Intersection Observer готується до повноцінного стандарту, він уже підтримується в Chrome 51+, Edge 15+ та Firefox 55+ і розробляється для Safari. Також є поліфіл .
Є деякі проблеми з відповіддю, наданими Деном, які можуть зробити його непридатним для деяких ситуацій. Деякі з цих питань вказуються у його відповіді внизу, що його код дасть помилкові позитиви щодо таких елементів:
clip
властивості CSSЦі обмеження демонструються в наступних результатах простого тесту :
isElementVisible()
Ось вирішення цих проблем з результатом тестування нижче та поясненням деяких частин коду.
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))
);
}
Проходження тесту: http://jsfiddle.net/AndyE/cAY8c/
І результат:
Однак цей метод не позбавлений власних обмежень. Наприклад, тестований елемент із нижчим z-індексом, ніж інший елемент у тому самому місці, буде ідентифікований як прихований, навіть якщо елемент попереду насправді не приховує жодної його частини. Тим не менш, цей метод має застосування в деяких випадках, що рішення Дана не охоплює.
Обидва element.getBoundingClientRect()
і document.elementFromPoint()
є частиною специфікації робочого проекту CSSOM та підтримуються щонайменше в IE 6 та пізніших версіях і в більшості настільних браузерів протягом тривалого часу (хоча і не ідеально). Додаткову інформацію див. У Quirksmode щодо цих функцій .
contains()
використовується для того, щоб побачити, чи повертається елемент document.elementFromPoint()
- це дочірній вузол елемента, який ми перевіряємо на предмет видимості. Він також повертає true, якщо повернутий елемент - це той самий елемент. Це просто робить чек більш надійним. Він підтримується у всіх основних браузерах, останній із них додав Firefox 9.0. Щоб отримати старішу підтримку Firefox, перегляньте історію цієї відповіді.
Якщо ви хочете перевірити більше точок навколо елемента на видимість, тобто, щоб переконатися, що елемент не охоплюється більш ніж, скажімо, 50% ― не знадобиться багато для коригування останньої частини відповіді. Однак майте на увазі, що це, ймовірно, буде дуже повільним, якби ви перевіряли кожен піксель, щоб переконатися, що він видимий на 100%.
doc
псевдонім document
. Так, мені подобається думати про це як про гідне рішення крайових справ.
element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Я спробував відповідь Дена . Однак алгебра, яка використовується для визначення меж, означає, що елемент повинен мати як ≤ розмір вікна перегляду, так і повністю всередині області перегляду, щоб true
легко отримати , що призводить до помилкових негативів. Якщо ви хочете визначити, чи є елемент у вікні перегляду взагалі, відповідь ryanve близька, але тестований елемент повинен перекривати область перегляду, тому спробуйте це:
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() */;
}
Як державна служба: відповідь
Дена з правильними розрахунками (елементом може бути> вікно, особливо на екранах мобільних телефонів), правильним тестуванням jQuery, а також додаванням isElementParicallyInViewport:
До речі, різниця між window.innerWidth і document.documentElement.clientWidth полягає в тому, що clientWidth / clientHeight не включає смугу прокрутки, тоді як window.innerWidth / Height.
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
дуже корисно також. Хороший.
Дивіться джерело межі , яка використовує getBoundingClientRect . Це як:
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
);
}
Він повертається, true
якщо будь-яка частина елемента знаходиться у вікні перегляду.
Моя коротша та швидша версія:
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
І jsFiddle, як потрібно: https://jsfiddle.net/on1g619L/1/
Мені стало непокоїти те, що не було доступної в jQuery версії доступних функціональних можливостей. Коли я натрапив на рішення Дана, я підглянув можливість запропонувати щось для людей, які люблять програмувати в стилі jQuery OO. Це приємно і спритно і працює як шарм для мене.
Bada bing bada boom
$.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();
});
Використання
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// Do cool stuff
}
});
Новий API Intersection Observer вирішує це питання дуже безпосередньо.
Для цього рішення знадобиться поліфайл, оскільки Safari, Opera та Internet Explorer ще не підтримують це (поліфункція включена в рішення).
У цьому рішенні є вікно поза зоною, яке є ціллю (спостерігається). Коли вона потрапляє у поле перегляду, кнопка вгорі в заголовку прихована. Він відображається після того, як поле відкриє подання.
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>
до HTML
IntersectionObserver
це експериментальна особливість (яка може змінитися в майбутньому).
IntersectionObserver
запускається лише зворотний виклик після руху цілі відносно кореня.
observe
події запускається, негайно повідомляється про поточний стан перетину відстежуваного елемента. Отже, в чомусь - це адреси.
Усі відповіді, з якими я стикався тут, перевіряють, чи елемент розташований у поточному вікні перегляду . Але це не означає, що це видно .
Що робити, якщо даний елемент знаходиться всередині div з переповненим вмістом, і він прокручується з поля зору?
Щоб вирішити це, вам доведеться перевірити, чи містять елемент всі батьки.
Моє рішення робить саме це:
Це також дозволяє вказати, яка частина елемента повинна бути видимою.
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;
};
Це рішення ігнорувало той факт, що елементи можуть бути не видно через інші факти, наприклад opacity: 0
.
Я перевірив це рішення в Chrome і Internet Explorer 11.
Я вважаю, що прийнята відповідь тут надмірно складна для більшості випадків використання. Цей код добре виконує завдання (використовуючи jQuery) і розрізняє повністю видимі та частково видимі елементи:
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)
поза обробкою прокрутки.
Найпростіше рішення , як підтримка Element.getBoundingClientRect () вже став досконалим :
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
Я думаю, що це більш функціональний спосіб зробити це. Відповідь Дена не працює в рекурсивному контексті.
Ця функція вирішує проблему, коли ваш елемент знаходиться в інших дисках, що прокручуються, тестуючи будь-які рівні рекурсивно до HTML-тега, і зупиняється на першому помилковому.
/**
* 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)
);
};
Найбільш прийняті відповіді не працюють під час збільшення масштабування Google Chrome на Android. У поєднанні з відповіддю Дана для обліку Chrome на Android потрібно використовувати VisVportport . Наступний приклад враховує лише вертикальну перевірку і використовує jQuery для висоти вікна:
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);
Ось моє рішення. Він буде працювати, якщо елемент захований всередині контейнера, який прокручується.
Ось демонстрація (спробуйте відновити розмір вікна до)
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;
};
Мені потрібно було лише перевірити, чи він видно на осі Y (для прокрутки функції Ajax load-more-records).
Виходячи з рішення дан , я намагався очистити реалізацію, щоб легше використовувати її кілька разів на одній сторінці:
$(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()
);
}
});
Я використовую його: коли елемент прокручується до перегляду, я додаю клас, який запускає анімацію ключових кадрів CSS. Це досить просто і працює особливо добре, коли у вас є умовно анімація на сторінці 10+ речей.
$window = $(window)
поза обробкою прокрутки
Більшість звичаїв у попередніх відповідях провалюються в цих моментах:
-Коли видно будь-який піксель елемента, але не " кут ",
-Коли елемент більший за вікно перегляду та зосереджений ,
- Більшість із них перевіряють лише наявність особливого елемента всередині документа чи вікна .
Що ж, для всіх цих проблем у мене є рішення, і плюси:
-Ви можете повернутися,
visible
коли з’явиться лише піксель з будь-якої сторони, і це не кут,-Ви можете повертатися,
visible
хоча елемент більший за вікно перегляду,-Ви можете вибрати свій
parent element
або ви можете автоматично дати йому вибір,- Працює і на динамічно доданих елементах .
Якщо ви перевірте фрагменти нижче, ви побачите різницю в використанні overflow-scroll
контейнера елемента, що не спричинить жодних проблем і побачите, що на відміну від інших відповідей тут, навіть якщо піксель з’являється з будь-якої сторони або коли елемент більший, ніж вікно перегляду, і ми бачимо внутрішній пікселів елемента, який він все ще працює.
Використання просте:
// 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
Демонстрація, без crossSearchAlgorithm
якої корисно для елементів, більших за перегляд контрольних елементів3 внутрішніх пікселів, щоб побачити:
Розумієте, коли ви знаходитесь всередині елемента3, він не вказує, чи він видимий чи ні, тому що ми перевіряємо, чи елемент видно з боків або кутів .
І цей включає, crossSearchAlgorithm
що дозволяє все одно повертатися, visible
коли елемент більший за область перегляду:
JSFiddle грати з: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Цей код зроблений для більш точної інформації, якщо якась частина елемента відображається у вікні перегляду чи ні. Для параметрів продуктивності або лише вертикальних слайдів не використовуйте це! Цей код є більш ефективним у малюнках.
Краще рішення:
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;
}
Ось функція, яка повідомляє, чи елемент видимий у поточному огляді батьківського елемента:
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
);
}
Це перевіряє, чи переглядає елемент хоча б частково (вертикальний розмір):
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}
}
У мене було те саме питання і я вирішив це за допомогою getBoundingClientRect ().
Цей код є повністю "загальним" і його потрібно написати лише один раз, щоб він працював (не потрібно виписувати його для кожного елемента, який ви хочете знати, у вікні перегляду).
Цей код перевіряє, чи знаходиться він вертикально у вікні перегляду, а не по горизонталі . У цьому випадку змінна (масив) "елементи" містить усі елементи, які ви перевіряєте, вертикально знаходяться у вікні перегляду, тому захопіть будь-які потрібні вам елементи будь-де та збережіть їх там.
Значення "for loop" проходить через кожен елемент і перевіряє, чи знаходиться він вертикально у вікні перегляду. Цей код виконується щоразу, коли користувач прокручує! Якщо topBoudingClientRect (). Top менше 3/4 в області перегляду (елемент - одна чверть у вікні перегляду), він реєструється як "у вікні перегляду".
Оскільки код є загальним, ви хочете знати, який елемент знаходиться у вікні перегляду. Щоб дізнатися це, ви можете визначити його за спеціальним атрибутом, ім'ям вузла, ідентифікатором, назвою класу тощо.
Ось мій код (скажіть, чи він не працює; він протестований у Internet Explorer 11, Firefox 40.0.3, Chrome версії 45.0.2454.85 м, Opera 31.0.1889.174 та Edge з Windows 10, [ще не Safari ]) ...
// 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.');
}
};
Це легке і маленьке рішення, яке спрацювало на мене.
Приклад : Ви хочете побачити, чи видно елемент, який знаходиться в батьківському елементі, який має прокрутку переповнення.
$(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');
}
})
Усі відповіді тут визначають, чи елемент повністю міститься у вікні перегляду, а не лише певним чином видно. Наприклад, якщо внизу перегляду видно лише половину зображення, рішення тут не вдасться, вважаючи це "зовні".
У мене був випадок використання, коли я здійснюю ледаче завантаження через IntersectionObserver
, але через анімацію, що виникає під час спливаючого вікна, я не хотів спостерігати жодних зображень, які вже перетиналися під час завантаження сторінки. Для цього я використав такий код:
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));
Це в основному перевірка, щоб переконатися, що верхня або нижня межі незалежно знаходиться у вікні перегляду. Протилежний кінець може бути зовні, але поки знаходиться один кінець, він "видно" принаймні частково.
Я використовую цю функцію (вона перевіряє лише, чи y екран, оскільки більшість часу х не потрібен)
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;
}
}
Для подібного виклику мені дуже сподобалася ця суть, яка демонструє поліфункцію для scrollIntoViewIfNeeded () .
Все необхідне для кунг-фу, необхідне для відповіді, знаходиться в цьому блоці:
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
посилається на елемент, який ви хочете знати, якщо це, наприклад, overTop
або overBottom
- вам просто слід отримати дрейф ...