Есть ли эффективный способ определить, является ли элемент 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);
Вы можете использовать удивительную функцию pageVisibiliy из HTML5 API, если вам важно, если вкладка с вашей веб-страницей активна и видима.
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, а также добавлением isElementPartiallyInViewport:
Кстати, разница между 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. Это красиво и быстро и работает как шарм для меня.
Бада Бинг Бада Бум
$.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;
}
Я думаю, что это более функциональный способ сделать это. Ответ Дэна не работает в рекурсивном контексте.
Эта функция решает проблему, когда ваш элемент находится внутри других прокручиваемых элементов div, рекурсивно проверяя любые уровни вплоть до тега HTML, и останавливается на первом значении false.
/**
* 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 необходимо использовать visualViewport . Следующий пример учитывает только вертикальную проверку и использует 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 с функцией загрузки и записи большего количества записей).
Основываясь на решении Дана , я попытался очистить реализацию, чтобы несколько раз использовать ее на одной странице:
$(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
которой полезна для элементов больше, чем область просмотра, проверяет внутренние пиксели element3, чтобы увидеть:
Видите ли, когда вы находитесь внутри element3, он не может определить, видим он или нет, потому что мы только проверяем, видим ли элемент с боков или углов .
И этот включает в себя, 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 ().
Этот код является полностью «универсальным» и должен быть написан только один раз, чтобы он работал (вам не нужно выписывать его для каждого элемента, который вы хотите знать, находится в области просмотра).
Этот код только проверяет, находится ли он вертикально в области просмотра, а не горизонтально . В этом случае переменная (массив) 'elements' содержит все элементы, которые вы проверяете, чтобы они были вертикально в окне просмотра, поэтому возьмите любые элементы, которые вы хотите где угодно и сохраните их там.
Цикл for проходит по каждому элементу и проверяет, находится ли он вертикально в области просмотра. Этот код выполняется каждый раз, когда пользователь прокручивает! Если getBoudingClientRect (). Top меньше 3/4 области просмотра (элемент составляет одну четверть в области просмотра), он регистрируется как «в области просмотра».
Так как код является общим, вы захотите узнать, какой элемент находится в области просмотра. Чтобы выяснить это, вы можете определить его по пользовательскому атрибуту, имени узла, идентификатору, имени класса и многим другим.
Вот мой код (скажите, если он не работает; он был протестирован в Internet Explorer 11, Firefox 40.0.3, Chrome версии 45.0.2454.85 m, 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));
Это в основном проверяет, находится ли верхняя или нижняя граница независимо в области просмотра. Противоположный конец может быть снаружи, но пока один конец находится внутри, он «виден» хотя бы частично.
Я использую эту функцию (она только проверяет, является ли у экранным, поскольку в большинстве случаев х не нужен)
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;
}
}
Для аналогичной задачи я действительно наслаждался этой сущностью, которая предоставляет polyfill для 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
- вы просто должны получить дрейф ...