Как определить, виден ли элемент DOM в текущей области просмотра?
Существует ли эффективный способ определить, виден ли элемент DOM (в HTML-документе) в данный момент (находится ли он в области видимости)?
(Вопрос касается браузера Firefox.)
5 ответ(ов)
Ваш вариант функции для проверки, выходил ли элемент за границы видимой области (viewport) выглядит отлично. Вот перевод на русский:
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
Функция isElementOutViewport
использует метод getBoundingClientRect()
, чтобы получить размеры и положение элемента относительно видимой области. Она возвращает true
, если элемент полностью вне области просмотра (например, если его нижняя граница меньше 0, или правая граница меньше 0, или левая граница больше ширины окна, или верхняя граница больше высоты окна).
Если вам нужно проверить, находится ли элемент в области видимости (viewport), вы можете использовать этот код.
Также вот jsFiddle с вашей реализацией: Ссылка на jsFiddle.
Если у вас есть дополнительные вопросы или нужна помощь, не стесняйтесь задавать!
На все ответы, с которыми я столкнулся здесь, проверяется только то, находится ли элемент в пределах текущего окна просмотра. Однако это не означает, что он видим.
Что, если данный элемент находится внутри div с переполненным содержимым и был прокручен вне видимости?
Чтобы решить эту проблему, вам потребуется проверить, содержится ли элемент во всех родительских элементах.
Мое решение делает именно это:
Оно также позволяет установить, какую часть элемента необходимо сделать видимой.
Element.prototype.isVisible = function(percentX, percentY){
var tolerance = 0.01; // необходима, потому что прямоугольники, возвращаемые getBoundingClientRect, предоставляют позицию с точностью до 10 десятичных знаков
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) {
// Элемент частично видим (выше видимой зоны)
console.log("Элемент частично видим (выше видимой зоны)");
} else if (windowScrollTop > bottomOfElement && windowScrollTop > topOfElement) {
// Элемент скрыт (выше видимой зоны)
console.log("Элемент скрыт (выше видимой зоны)");
} else if (scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement) {
// Элемент скрыт (ниже видимой зоны)
console.log("Элемент скрыт (ниже видимой зоны)");
} else if (scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement) {
// Элемент частично видим (ниже видимой зоны)
console.log("Элемент частично видим (ниже видимой зоны)");
} else {
// Элемент полностью видим
console.log("Элемент полностью видим");
}
});
Этот код позволяет понять, находится ли элемент в видимой области страницы и в каком состоянии — полностью виден, частично виден или скрыт.
Пользователи, столкнувшиеся с задачей определения видимости элементов на странице, часто имеют проблемы с некоторыми крайними случаями. Среди распространённых недостатков предыдущих решений следует отметить:
- Определение видимости работает только при наличии видимого пикселя, но не учитывает "углы";
- Элементы, которые больше области просмотра и расположены по центру, обрабатываются некорректно;
- Проверка ведётся только для одного элемента внутри документа или окна.
Однако для всех указанных проблем существует решение, обладающее такими преимуществами:
- Метод может вернуть
visible
, если хотя бы один пиксель элемента отображается, при этом игнорируя углы; - И по-прежнему возможно вернуть
visible
, даже если элемент больше области просмотра; - Вы можете задать
родительский элемент
или позволить системе выбрать его автоматически; - Решение также работает с динамически добавляемыми элементами.
Ниже представлен фрагмент кода, демонстрирующий, что использование overflow-scroll
в контейнере элемента не вызовет проблем. В отличие от других представленных решений, даже если виден пиксель с любой стороны или элемент больше области просмотра и мы видим внутренние пиксели элемента, всё будет работать корректно.
Использование очень простое:
// Для проверки видимости элемента с любой стороны
isVisible(element)
// Для проверки видимости элементов внутри выбранного родителя
var parent = document; // Предполагаем, что мы проверяем, виден ли 'element' внутри 'document'
isVisible(element, parent)
// Для проверки видимости элементов, даже если они больше области просмотра
isVisible(element, null, true) // Без указания родителя
isVisible(element, parent, true) // С указанием родителя
Вот демонстрация без crossSearchAlgorithm
, полезного для проверки внутренних пикселей элементов, которые больше области просмотра:
function isVisible(element, parent, crossSearchAlgorithm) {
var rect = element.getBoundingClientRect(),
prect = (parent != undefined) ? parent.getBoundingClientRect() : element.parentNode.getBoundingClientRect(),
csa = (crossSearchAlgorithm != undefined) ? crossSearchAlgorithm : false,
efp = function (x, y) { return document.elementFromPoint(x, y) };
// Вернуть false, если элемент не в области просмотра
if (rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom) {
return false;
}
var flag = false;
// Вернуть true, если достигнут любой пиксель границы слева направо
for (var x = rect.left; x < rect.right; x++) {
if (element.contains(efp(rect.top, x)) || element.contains(efp(rect.bottom, x))) {
flag = true;
break;
}
}
// Вернуть true, если достигнут любой пиксель границы сверху вниз
if (flag == false) {
for (var y = rect.top; y < rect.bottom; y++) {
if (element.contains(efp(rect.left, y)) || element.contains(efp(rect.right, y))) {
flag = true;
break;
}
}
}
if (csa) {
// Другой алгоритм для проверки, если элемент расположен по центру и больше области просмотра
if (flag == false) {
var x = rect.left;
var y = rect.top;
// С верхнего левого до нижнего правого
while(x < rect.right || y < rect.bottom) {
if (element.contains(efp(x,y))) {
flag = true;
break;
}
if(x < rect.right) { x++; }
if(y < rect.bottom) { y++; }
}
if (flag == false) {
x = rect.right;
y = rect.top;
// С верхнего правого до нижнего левого
while(x > rect.left || y < rect.bottom) {
if (element.contains(efp(x,y))) {
flag = true;
break;
}
if(x > rect.left) { x--; }
if(y < rect.bottom) { y++; }
}
}
}
}
return flag;
}
Код создан для более точного определения, отображается ли хотя бы часть элемента в области просмотра. Если вам нужно более производительное решение или обработка только вертикальной прокрутки, возможно, лучше использовать другие подходы. Этот код более эффективен в ситуациях, требующих точного визуального отображения.
Вот функция, которая определяет, находится ли элемент в видимой области текущего родительского элемента:
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
);
}
Эта функция принимает на вход два параметра: el
— элемент, который нужно проверить, и pa
— родительский элемент. Сначала мы проверяем, используем ли мы jQuery, и если да, преобразуем объекты jQuery в DOM-элементы.
Далее функция использует метод getBoundingClientRect()
, чтобы получить размеры и положение как проверяемого элемента, так и родительского. Затем она проверяет, пересекаются ли границы обоих элементов, и возвращает true
, если элемент виден в пределах родительского элемента, и false
в противном случае.
Этот подход удобен для проверки видимости элемента в рамках его родителя, что может быть полезно в различных сценариях, например, для реализации lazy loading или при прокрутке.
Получить координаты (X,Y) HTML-элемента
Как изменить класс элемента с помощью JavaScript?
Эквивалент document.createElement в jQuery?
Как выбрать элемент по имени с помощью jQuery?
Как запустить JavaScript после загрузки страницы?