Есть ли эффективный способ узнать, является ли элемент DOM (в документе HTML) в настоящее время видимым (отображается в область просмотра)?
(Вопрос относится к Firefox.)
как внутри текущего отображаемого прямоугольника. Спасибо. Перефразировал.
Не могли бы вы также обновить название?
Обратите внимание, что все ответы здесь дадут вам ложное срабатывание для элементов, которые находятся за пределами видимой области их родительского элемента (например, со скрытым переполнением), но внутри области области просмотра.
Я добавил мое собственное решение, который решает эту проблему
Учитывает ли какое-либо из этих решений z-индекс узла dom и как это может повлиять на видимость, в частности, путем скрытия элементов с более низким z-индексом?
Ни один из ответов не работает с сгенерированными элементами с абсолютным позиционированием.
Зависит от того, что вы подразумеваете под видимым. Если вы имеете в виду, отображается ли он в настоящее время на странице, учитывая положение прокрутки, вы можете рассчитать его на основе смещения элемента y и текущей позиции прокрутки.
Ответ 2019 года: IntersectionObserver
Я знаю, что это устарело, но это мое решение. Работает отлично! Демо Codesandbox
Есть миллион ответов, и большинство из них до смешного длинные. См. Здесь для двухстрочного



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Обновлять: Время идет, и наши браузеры тоже. Этот метод больше не рекомендуется, и вы должны использовать Решение Дэна, если вам не нужно поддерживать версию 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
);
}
В исходной опубликованной функции была ошибка. Необходимо сохранить ширину / высоту перед переназначением эл ...
Также было бы разумно немного абстрагироваться от этого и создать некоторые служебные функции. Я использую тот, который называется getViewport, который возвращает верхнюю / левую / нижнюю / правую часть видимого окна, и тот, который называется getPosition, который находит верхний / левый угол элемента.
Я пытаюсь получить эту функциональность в расширении Firefox, поэтому на случай, если кто-то еще окажется в такой же ситуации ... (веб-разработчики, вы, конечно, можете полностью игнорировать этот комментарий) Эта функция отлично работает в этом контексте, ЗА ИСКЛЮЧЕНИЕМ того, что window относится не к тому объекту, поэтому просто поместите var window = el.ownerDocument.defaultView в начало функции, и все готово.
Что, если элемент находится в прокручиваемом div и прокручивается за пределы представления?
Пожалуйста, просмотрите более новую версию скрипта ниже
В расширении Firefox el.ownerDocument.defaultView.innerWidth не является точным представлением области просмотра (хотя и window). Например, будет неправильно, если вы сделаете окно firefox уже, чем самая широкая установленная панель инструментов.
Также интересно узнать о вопросе @amartynov. Кто-нибудь знает, как просто определить, скрыт ли элемент из-за переполнения элемента-предка? Бонус, если это можно обнаружить независимо от того, насколько глубоко вложен ребенок.
Мне интересно, почему вы рекомендуете ответ ниже, пока он чистый javascript и, похоже, не использует ничего неподдерживаемого
@deadManN, как известно, проходит через DOM медленно. Этого достаточно, но производители браузеров также создали getBoundingClientRect специально для определения координат элементов ... Почему бы нам не использовать его?
Функция отлично работает, если нет блоков с полосами прокрутки. Предлагаю доработать блок while и окончательную проверку: while(elm.offsetParent) {elm = elm.offsetParent; top += elm.offsetTop - elm.scrollTop; left += elm.offsetLeft - elm.scrollLeft; } return (top < (window.innerHeight) && left < (window.innerWidth) && (top + height) > 0 && (left + width) > 0 );
Я предлагаю добавить developer.mozilla.org/en-US/docs/Web/API/…, хотя он не поддерживает IE11, поэтому вам придется использовать резервный метод.
Теперь большинство браузеров поддерживает метод 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 $ (документ) .ready (), потому что в этот момент нет гарантии, что был применен CSS. Ваш код может работать локально с вашим CSS на жестком диске, но после размещения на удаленном сервере он выйдет из строя.
После запуска DOMContentLoadedстили применяются, но изображения еще не загружены. Итак, мы должны добавить прослушиватель событий window.onload.
Мы пока не можем поймать событие масштабирования / сжатия.
В крайнем случае может быть следующий код:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Вы можете использовать замечательную функцию pageVisibiliy HTML5 API, если вам важно, активна и видна ли вкладка с вашей веб-страницей.
TODO: этот метод не обрабатывает две ситуации:
z-indexoverflow-scroll в контейнере элементаЯ использую это решение (однако остерегайтесь опечатки "дно"). Также есть кое-что, о чем следует знать, когда рассматриваемый нами элемент будет содержать изображения. Chrome (по крайней мере) должен дождаться загрузки изображения, чтобы получить точное значение boundingRectangle. Похоже, у Firefox нет этой "проблемы"
window.innerHeight и innerWidth не определены в IE. На howtocreate.co.uk/tutorials/javascript/browserwindow есть хорошая статья, показывающая различные способы получения высоты документа ... document.body.clientHeight кажется лучшим способом в целом. Я соответствующим образом отредактировал ваш ответ.
вот мой вариант проверки, он просто проверяет, виден ли элемент в vp вообще: gist.github.com/2300296
Если вам не нужно поддерживать IE 5.5 и ниже (чего, вероятно, нет), вы можете использовать (window.innerWidth || document.documentElement.clientWidth) и его эквиваленты для кросс-браузерного решения.
@Claudio: проблема в том, что страница ПОВТОРЯЕТСЯ после загрузки каждого изображения. Итак, если вы вызовете мою функцию до того, как появится изображение, вы получите правильный результат, но он действителен в течение нескольких миллисекунд, пока не появится изображение и контейнер не будет расширен.
@MatasPetrikas в вашей версии есть ошибки, но вы их исправили в комментариях
Почему это место после документа. , в "rect.bottom <= (window.innerHeight || document. documentElement.clientHeight)" ??
Работает ли это, когда у вас включена прокрутка в контейнере внутри тела. Например, здесь это не работает - agaase.github.io/webpages/demo/isonscreen2.html isElementInViewport (document.getElementById ("innerele")). innerele находится внутри контейнера с включенной прокруткой.
Нужно ли мне использовать все эти DOMContentLoaded load resize scroll? Любые проблемы с производительностью и т. д.!
Я вижу, что он не работает с другим элементом jQuery(window).on, кроме окна, но я вижу, что обработчик вызывается МНОГО раз при каждой прокрутке. Есть ли способ привязать событие к конкретному элементу?
В расчетах предполагается, что элемент меньше экрана. Если у вас высокие или широкие элементы, правильнее будет использовать return (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));.
Совет: тем, кто пытается реализовать это с помощью jQuery, просто напоминание о необходимости передать объект HTML DOM (например, isElementInViewport(document.getElementById('elem'))), а не объект jQuery (например, isElementInViewport($("#elem))). Эквивалент jQuery - добавить [0] следующим образом: isElementInViewport($("#elem)[0]).
Блестяще. Если не использовать jQuery, instanceof jQuery выдает ошибку.
@ Dan - if (jQuery != undefined) ничего не исправляет, он все равно выдаст ошибку ссылки, если jQuery не был определен. Способ исправить это (то есть предотвратить ошибку, если jQuery не был определен или не является конструктором) - это if (typeof jQuery == 'function' && el.instanceof jQuery), что не является пуленепробиваемым (потому что экземпляр не особенно полезно), но должно быть достаточно хорошим.
Вы может поймаете событие масштабирования / ущемления. Событие resize запускается, когда изменяется размер окна или масштабируется страница.
Вот моя версия, поддерживает элементы контейнера (например, если элемент находится внутри другого прокручиваемого элемента) и полную или частичную видимость (хотя только jQuery): gist.github.com/anonymous/f09b5cd487b5ad55c46e
@Mahn - Мне нравится ваше решение, похоже, именно то, что мне нужно. Можете привести пример использования? Я не понимаю, как ссылаться на этот тип функции.
После объявления @ToddProuty вы можете выполнить $("#elementInside").isInViewport("#parentElement") (возвращает true, если #elementInside полностью виден в #parentElement) или $("#elementInside").isInViewport("#parentElement", true) (возвращает true, если #elementInside хотя бы частично виден в #parentElement) или $("#elementInside").isInViewport() (возвращает true, если виден в главном окне )
Этот подход не работает при переполнении элементов, которые в текущем окне просмотра невидимы.
По крайней мере, в инструментах разработчика в Firefox getBoundingClientRect() дает мне значения немного шире, чем область просмотра для элементов с width:100%, то есть область просмотра составляет 815 пикселей, а значение «справа» - 815.2000122070312. Я использовал Math.floor() в свойствах rect, чтобы обойти это.
Как мне использовать это, чтобы получить все видимые элементы в текущем окне просмотра? Для бонуса у меня будут координаты относительно области просмотра.
Я хотел бы проверить наличие нескольких элементов jQuery, которые могут находиться в (или вне) области просмотра. Кажется, этот сценарий проверяет только один элемент. Когда в текущем окне просмотра находится несколько элементов, я хочу получить последний элемент, находящийся в текущем окне просмотра. Как это можно было сделать с помощью этого скрипта? Или какая-то модификация?
@SuperUberDuper Я создал Gist, который возвращает самый нижний элемент в вашем текущем окне просмотра. Если вы просто вернете массив visibleElements в этой функции, вы получите именно то, что вам нужно. См .: gist.github.com/vernondegoede/5b5638706aec5a80611010c88609d1 70
Что делать, если у вас есть коллекция элементов?
Алгебра этого требует пояснения. Это больше похоже на проверку того, является ли элемент полностью вне области просмотра. Он возвращает «невидимый» для элементов, которые только частично находятся за пределами области просмотра. См. Ответ @Walf для более интуитивной версии. (из isElementInViewport). Прослушивание событий отлично работает.
el is not defined
Эта дополнительная функция полезна для определения, когда прокручивать, когда она не видна: function alignToTop(el) { let rect = el.getBoundingClientRect(); return rect.top < 0 || rect.bottom <= (window.innerHeight || document.documentElement.clientHeight); }
Я обнаружил, что это работает почти идеально, за исключением чрезвычайно быстрой прокрутки, например. нажатие cmd вниз или cmd вверх иногда может быть неточным. Я нашел альтернативу - использовать github.com/stutrek/scrollMonitor, который всегда на 100% точен для меня и очень эффективен.
Однако это имеет огромное ограничение. Представьте себе узел SVG, даже такой простой, как прямоугольник, который повернут, скажем, на 45 градусов против часовой стрелки. Его (исходная) «левая» граница, теперь наклоненная на 45 градусов, может легко выходить за верхний правый угол области просмотра, если весь прямоугольник находится за пределами области просмотра, и все же ограничивающая рамка все равно будет перекрывать ее.
const onVisibilityChange = (el, обратный вызов) => () => isElementInViewport (el) && обратный вызов ()
Этот ответ проверяет, полностью ли отображается элемент в области просмотра. Если часть его отключена, это будет рассматривать его как не отображаемое. Если вы хотите знать, видна ли какая-либо часть элемента, используйте фрагмент из комментария @Roonaan.
Эй, думаю, на всякий случай стоит добавить if (!el) { return; }, чтобы избежать сбоев.
Этот код работает только с одним элементом, вы не можете прикрепить это событие, например, к. весь класс css. Я знаю, что можно выполнить цикл по всем элементам класса и присоединить к ним событие, но как насчет динамически добавляемых элементов?
Следует отметить, что getBoundingClientRect может быть ОЧЕНЬ медленным, на сложных страницах это может занять несколько сотен миллисекунд (!), Поэтому, если вы точно знаете, как расположена ваша страница и как вложены элементы, может быть лучше использовать clientLeft / clientTop и т. д.
мне не повезло .. он возвращает true, даже элемент не находится в области просмотра
Этот подход отлично подходит для меня. Мне нужна всего одна модификация. Я хочу определить, находится ли 50% элемента во вьюпорте или нет! Прямо сейчас он немедленно сообщает мне, даже если только верхняя часть элемента была прокручена во вьюпорте.
Uncaught ReferenceError: el is not defined
@Roonaan, ваш комментарий решил мою проблему. Раньше пропустил этот момент и получал, что getBoundingClientRect не является функцией.
См. Источник грани, который использует 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 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;
}
Вы должны попытаться объяснить, почему ваша версия лучше. В нынешнем виде он выглядит более или менее так же, как и другие решения.
Отличное решение, у него есть BOX / ScrollContainer и не используется WINDOW (только если он не указан). Взгляните на код, чем оцените, это более универсальное решение (много искал)
В современных браузерах вы можете попробовать API-интерфейс Intersection Observer, который дает следующие преимущества:
Intersection Observer становится полноценным стандартом и уже поддерживается в Chrome 51+, Edge 15+ и Firefox 55+ и находится в стадии разработки для Safari. Также доступен полифил.
Есть некоторые проблемы с ответ предоставлен Дэном, которые могут сделать его неподходящим подходом в некоторых ситуациях. Некоторые из этих проблем указаны в его ответе внизу, что его код будет давать ложные срабатывания для элементов, которые:
clipЭти ограничения демонстрируются в следующих результатах простой тест:

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))
);
}
Passing test:http://jsfiddle.net/AndyE/cAY8c/
И результат:

Однако у этого метода есть свои ограничения. Например, тестируемый элемент с меньшим z-индексом, чем другой элемент в том же месте, будет идентифицирован как скрытый, даже если элемент впереди фактически не скрывает какую-либо его часть. Тем не менее, этот метод имеет свои применения в некоторых случаях, которые не покрывает решение Дэна.
И element.getBoundingClientRect(), и document.elementFromPoint() являются частью спецификации рабочего проекта CSSOM и поддерживаются по крайней мере в IE 6 и более поздних версиях, а также в настольных браузерах самый в течение длительного времени (хотя и не идеально). См. Quirksmode для этих функций для получения дополнительной информации.
contains() используется, чтобы увидеть, является ли элемент, возвращаемый document.elementFromPoint(), дочерним узлом элемента, который мы проверяем на видимость. Он также возвращает истину, если возвращаемый элемент является тем же элементом. Это просто делает проверку более надежной. Он поддерживается во всех основных браузерах, Firefox 9.0 был последним из них, добавившим его. Если вам нужна более старая поддержка Firefox, проверьте историю ответов.
Если вы хотите проверить больше точек вокруг элемента на видимость, т. Е. Чтобы убедиться, что элемент не покрывается более чем, скажем, 50%, не потребуется много времени, чтобы скорректировать последнюю часть ответа. Однако имейте в виду, что, вероятно, будет очень медленно, если вы проверите каждый пиксель, чтобы убедиться, что он на 100% виден.
Вы хотели использовать doc.documentElement.clientWidth? Должен ли это быть «document.documentElement»? С другой стороны, это единственный метод, который также работает для таких случаев использования, как скрытие содержимого элемента для обеспечения доступности с помощью свойства CSS 'clip': snook.ca/archives/html_and_css/hiding-content-for-accessibil ity
@klamping: хороший улов, спасибо. Я скопировал его прямо из своего кода, где я использовал doc в качестве псевдонима для document. Да, мне нравится думать об этом как о достойном решении для крайних случаев.
У вас есть идеи по устранению проблемы с z-индексом?
@Christoph: хотя он на самом деле не прояснил это, я предполагаю, что Дэн говорит о том факте, что элемент может быть перед тем, который вы проверяете, но не скрываться (например, прозрачный слой). Для этого сценария, очевидно, довольно сложно определить, видим ли элемент или нет, потому что мое решение дало бы ложноотрицательные результаты. Его можно изменить так, чтобы он возвращал не-логический флаг, т.е. «скрытый», «видимый» или «неопределенный».
Я не считаю, что это работает с hide / slideup. Когда элемент был сдвинут / спрятан, функция не вернет true.
Есть ошибка. Я разглагольствую на скрипке. Результаты тестов Test 1: false и Test 2: true. Если я заставлю алгоритм использовать compareDocumentPosition, отредактировав строку, в которой установлен contains, так, чтобы она была установлена на compareDocumentPosition, то результаты будут Test 1: false, Test 2: false. Это потому, что 0x10 должен быть побитовым образом дополнен результатом compareDocumentPosition. Или 0x14 может использоваться для равенства, поскольку спецификации говорит, что содержащийся узел всегда следует.
Для меня это не работает. Но inViewport () в предыдущем ответе работает в FF.
Также может быть полезно проверить, что центр элемента виден, если у вас есть закругленные углы или применено преобразование, поскольку ограничивающие углы могут не возвращать ожидаемый элемент: element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Объявление efp было бы лучше efp = document.elementFromPoint.bind(document).
@schism, только если вас не волнует поддержка IE8 (и тот факт, что связанные функции плохо оптимизированы в некоторых браузерах) :-)
@AndyE Думаю, мне просто повезло, что мне не нужно об этом заботиться! :-) Кстати, похоже, это работает в 4.4 Webview, но не в Chrome ...
Ваше решение более элегантно, чем ответ, получивший наибольшее количество голосов. Однако, как и предыдущий, он не работает при переполнении элементов, которые находятся в текущем окне просмотра, но невидимы.
У меня странная ситуация, но я хочу ее отметить для будущих читателей. Мой проект встроен в другой через iframe. В iframe есть полоса прокрутки, потому что выделенное ему пространство больше, чем становится видимым. Ни у Дэна, ни у Энди нет подходов, подходящих для моей ситуации. Элементы в нижней части iframe, которые вам нужно будет прокрутить, считаются видимыми с помощью обоих подходов. Ответы rainyjune, ryanve и ally также дают ложное срабатывание. Я еще не нашел способа определить видимый размер iframe, только выделенный размер.
Не работал у меня на входах (хром канарейка 50). Не уверен, почему, может быть, родные углы закруглены? Мне пришлось немного уменьшить координаты, чтобы он работал el.contains (efp (rect.left + 1, rect.top + 1)) || el.contains (efp (rect.right-1, rect.top + 1)) || el.contains (efp (rect.right-1, rect.bottom-1)) || el.contains (efp (rect.left + 1, rect.bottom-1))
@rayjax это потому, что разработчики Chrome решили изменить поведение window.innerHeight / Width и сломать сеть, см. code.google.com/p/chromium/issues/detail?id=571297 и не стесняйтесь добавлять свою жалобу к остальным!
@AndyE, если я прав, innerHeight / Width повлияет только на первую часть функции (находится внутри окна), а не на вторую (отображается)?
@AndyE Это не всегда работает для элементов, которые шире / выше, чем область просмотра, потому что все углы могут быть за пределами экрана, даже если он виден
@AndyE, чтобы решить, что вы можете проверить углы пересечения ограничивающего прямоугольника клиента элемента и прямоугольника области просмотра
Также elementFromPoint может возвращать элементы с opacity: 0
Пробовал Ответ Дэна. тем не мение, алгебра, используемая для определения границ, означает, что элемент должен быть как ≤ размера области просмотра, так и полностью находиться внутри области просмотра, чтобы получить true, что легко приводит к ложноотрицательным результатам. Если вы хотите определить, находится ли элемент в области просмотра вообще, ответ Райанве близко, но тестируемый элемент должен перекрывать область просмотра, поэтому попробуйте следующее:
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() */;
}
Вот мое решение. Он будет работать, если элемент скрыт внутри прокручиваемого контейнера.
Вот демо (попробуйте изменить размер окна на)
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 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/
Мое решение более жадное и быстрое, когда элемент имеет какой-либо пиксель в области просмотра, он вернет false.
Мне это нравится. Лаконично. Вы можете удалить пробелы между именем функции и скобками, а также между скобками и скобками в первой строке. Никогда не любил те места. Возможно, это просто мой текстовый редактор, который все это кодирует цветом, чтобы его было легко читать. function aaa (arg) {statement} Я знаю, что это не ускоряет выполнение, а вместо этого подпадает под минимизацию.
В качестве государственной службы:
Ответ Дэна с правильными вычислениями (элемент может быть> окном, особенно на экранах мобильных телефонов) и правильным тестированием jQuery, а также добавлением isElementPartialInViewport:
Кстати, разница между 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.info("Fully in viewport: " + inVpFull);
console.info("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.info("Fully in viewport: " + inVpFull);
console.info("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.info("state changed");
// $(element).toggleClass('fixed', this.isAboveViewport)
});
</script>
-->
</body>
</html>
isElementPartiallyInViewport тоже очень полезен. Хороший.
Для isElementInViewport (e): ваш код даже не загружает изображения. Это правильно: function isElementInViewport (e) {var t = e.getBoundingClientRect (); return t.top> = 0 && t.left> = 0 && t.bottom <= (window.innerHeight || document.documentElement.clientHeight) && t.right <= (window.innerWidth || document.documentElement.clientWidth) }
@Arun chauhan: Ни один из моих кодов не загружает изображения, так зачем это нужно, и формула верна.
@StefanSteiger Многие ответы здесь содержат window.innerHeight || document.documentElement.clientHeight как часть проверки. Поскольку вы первый, кто обратил внимание на разницу между ними, может быть, вы также сможете объяснить, зачем вообще нужен этот запасной вариант? AFAIK window.innerHeight всегда будет иметь значение (не может быть null или undefined), поэтому единственное ложное значение, которое он может удерживать, - 0. Это похоже на реальный крайний случай, в котором я даже не уверен, как document.documentElement.clientWidth может внезапно оказаться полезным.
@targumon: Причина в поддержке старых браузеров.
@StefanSteiger согласно MDN поддерживается начиная с IE9, поэтому практически безопасно (по крайней мере, в моем случае) просто использовать window.innerHeight напрямую. Спасибо!
@targumon: Да, но это значит, что не в IE8.
@StefanSteiger Надеюсь, вам больше не нужно поддерживать IE8. Microsoft больше не поддерживает его с января 2016 года!
isElementPartiallyInViewport мне очень помог. благодарю вас!
Основываясь на решение Дана, я попытался очистить реализацию, чтобы было проще использовать ее несколько раз на одной странице:
$(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) вне обработчика прокрутки
Это проверяет, находится ли элемент хотя бы частично в поле зрения (вертикальный размер):
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}
}
Я считаю, что принятый здесь ответ слишком сложен для большинства случаев использования. Этот код хорошо справляется со своей задачей (с использованием 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.info("Element is partially visible (above viewable area)");
} else if (windowScrollTop > bottomOfElement && windowScrollTop > topOfElement) {
// Element is hidden (above viewable area)
console.info("Element is hidden (above viewable area)");
} else if (scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement) {
// Element is hidden (below viewable area)
console.info("Element is hidden (below viewable area)");
} else if (scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement) {
// Element is partially visible (below viewable area)
console.info("Element is partially visible (below viewable area)");
} else {
// Element is completely visible
console.info("Element is completely visible");
}
});
Вам обязательно нужно кэшировать $window = $(window) вне обработчика прокрутки.
Я думаю, что это более функциональный способ сделать это. Ответ Дэна не работают в рекурсивном контексте.
Эта функция решает проблему, когда ваш элемент находится внутри других прокручиваемых div, путем рекурсивного тестирования любых уровней до тега 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)
);
};
Меня беспокоит отсутствие доступной версии функциональности, ориентированной на jQuery. Когда я наткнулся на Решение Дэна, я заметил возможность предоставить что-то людям, которые любят программировать в стиле jQuery OO. Это красиво и быстро, и работает на меня как шарм.
Bada bing bada бум
$.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
}
});
Будет ли фигурной скобки достаточно, чтобы закрыть оператор if?
Я не мог заставить его работать с несколькими элементами одного и того же класса.
@TheWhizofOz Я обновил свой ответ, чтобы привести примеры других возможных вариантов использования, которые вы подняли. удачи.
У меня был тот же вопрос, и я решил это с помощью 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.info(elements[i].nodeName + ' ' +
elements[i].className + ' ' +
elements[i].id +
' is in the viewport; proceed with whatever code you want to do here.');
}
};
Для аналогичной задачи мне очень понравился это суть, который предоставляет полифилл для 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 - вы просто должны получить дрейф ...
Все ответы, которые я здесь встретил, проверяют только, является ли элемент расположен внутри текущего окна просмотра. Но это не значит, что это видно.
Что, если данный элемент находится внутри 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.
Я использую эту функцию (она проверяет только, находится ли y на экране, поскольку большую часть времени x не нужен)
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;
}
}
Это простое и компактное решение, которое мне помогло.
Пример: вы хотите увидеть, отображается ли элемент в родительском элементе, который имеет прокрутку переполнения.
$(window).on('scroll', function () {
var container = $('#sidebar');
var containerHeight = container.height();
var scrollPosition = $('#row1').offset().top - container.offset().top;
if (containerHeight < scrollPosition) {
console.info('not visible');
} else {
console.info('visible');
}
})
Вот функция, которая сообщает, является ли элемент видимым в текущем окне просмотра элемента родитель:
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
);
}
Новый 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 - экспериментальная функция (которая может измениться в будущем).
@KarthikChintala - он поддерживается во всех браузерах, кроме IE, а также доступен полифилл.
Не отвечает на вопрос OP, поскольку обнаруживает только изменения: IntersectionObserver запускает обратный вызов только после перемещения цели относительно корня.
При вызове observe немедленно запускается событие, сообщающее вам текущее состояние пересечения отслеживаемого элемента. Так что в некотором роде - адреса.
Как можно проще, ИМО:
function isVisible(elem) {
var coords = elem.getBoundingClientRect();
return Math.abs(coords.top) <= coords.height;
}
Самое простое решение как поддержка Element.getBoundingClientRect () имеет стать идеальным:
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
Как это работает в мобильных браузерах? Большинство из них содержат ошибки в отношении области просмотра, их заголовок поднимается или опускается при прокрутке, а также разное поведение при отображении клавиатуры, в зависимости от того, Android это или iOS и т. д.
@Kev Должен работать нормально в зависимости от того, когда вы вызываете этот метод. Если вы вызовете его, а затем измените размер окна, результат, очевидно, больше не будет правильным. Вы можете вызывать его при каждом изменении размера в зависимости от типа функциональности, которую вы хотите. Не стесняйтесь задать отдельный вопрос о вашем конкретном сценарии использования и напишите мне здесь.
Большинство использований в предыдущих ответах терпят неудачу в этих точках:
-When any pixel of an element is visible, but not "a corner",
-When an element is bigger than viewport and centered,
-Most of them are checking only for a singular element inside a document or window.
Что ж, для всех этих проблем у меня есть решение и плюсы:
-You can return
visiblewhen only a pixel from any sides shows up and is not a corner,-You can still return
visiblewhile element bigger than viewport,-You can choose your
parent elementor you can automatically let it choose,-Works on dynamically added elements too.
Если вы проверите приведенные ниже фрагменты, вы увидите разницу в использовании 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, чтобы увидеть:
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) };
// Return false if it's not in the viewport
if (rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom) {
return false;
}
var flag = false;
// Return true if left to right any border pixel reached
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;
}
}
// Return true if top to bottom any border pixel reached
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) {
// Another algorithm to check if element is centered and bigger than viewport
if (flag == false) {
var x = rect.left;
var y = rect.top;
// From top left to bottom right
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;
// From top right to bottom left
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;
}
// Check multiple elements visibility
document.getElementById('container').addEventListener("scroll", function() {
var elementList = document.getElementsByClassName("element");
var console = document.getElementById('console');
for (var i=0; i < elementList.length; i++) {
// I did not define parent, so it will be element's parent
if (isVisible(elementList[i])) {
console.innerHTML = "Element with id[" + elementList[i].id + "] is visible!";
break;
} else {
console.innerHTML = "Element with id[" + elementList[i].id + "] is hidden!";
}
}
});
// Dynamically added elements
for(var i=4; i <= 6; i++) {
var newElement = document.createElement("div");
newElement.id = "element" + i;
newElement.classList.add("element");
document.getElementById('container').appendChild(newElement);
}#console { background-color: yellow; }
#container {
width: 300px;
height: 100px;
background-color: lightblue;
overflow-y: auto;
padding-top: 150px;
margin: 45px;
}
.element {
margin: 400px;
width: 400px;
height: 320px;
background-color: green;
}
#element3 {
position: relative;
margin: 40px;
width: 720px;
height: 520px;
background-color: green;
}
#element3::before {
content: "";
position: absolute;
top: -10px;
left: -10px;
margin: 0px;
width: 740px;
height: 540px;
border: 5px dotted green;
background: transparent;
}<div id = "console"></div>
<div id = "container">
<div id = "element1" class = "element"></div>
<div id = "element2" class = "element"></div>
<div id = "element3" class = "element"></div>
</div>Видите ли, когда вы внутри element3, он не может определить, виден он или нет, потому что мы проверяем только, видим ли элемент из стороны или углы.
И этот включает crossSearchAlgorithm, который позволяет вам по-прежнему возвращать visible, когда элемент больше, чем область просмотра:
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) };
// Return false if it's not in the viewport
if (rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom) {
return false;
}
var flag = false;
// Return true if left to right any border pixel reached
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;
}
}
// Return true if top to bottom any border pixel reached
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) {
// Another algorithm to check if element is centered and bigger than viewport
if (flag == false) {
var x = rect.left;
var y = rect.top;
// From top left to bottom right
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;
// From top right to bottom left
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;
}
// Check multiple elements visibility
document.getElementById('container').addEventListener("scroll", function() {
var elementList = document.getElementsByClassName("element");
var console = document.getElementById('console');
for (var i=0; i < elementList.length; i++) {
// I did not define parent so it will be element's parent
// and it will do crossSearchAlgorithm
if (isVisible(elementList[i],null,true)) {
console.innerHTML = "Element with id[" + elementList[i].id + "] is visible!";
break;
} else {
console.innerHTML = "Element with id[" + elementList[i].id + "] is hidden!";
}
}
});
// Dynamically added elements
for(var i=4; i <= 6; i++) {
var newElement = document.createElement("div");
newElement.id = "element" + i;
newElement.classList.add("element");
document.getElementById('container').appendChild(newElement);
}#console { background-color: yellow; }
#container {
width: 300px;
height: 100px;
background-color: lightblue;
overflow-y: auto;
padding-top: 150px;
margin: 45px;
}
.element {
margin: 400px;
width: 400px;
height: 320px;
background-color: green;
}
#element3 {
position: relative;
margin: 40px;
width: 720px;
height: 520px;
background-color: green;
}
#element3::before {
content: "";
position: absolute;
top: -10px;
left: -10px;
margin: 0px;
width: 740px;
height: 540px;
border: 5px dotted green;
background: transparent;
}<div id = "console"></div>
<div id = "container">
<div id = "element1" class = "element"></div>
<div id = "element2" class = "element"></div>
<div id = "element3" class = "element"></div>
</div>JSFiddle, с которым можно поиграть: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Этот код предназначен для более точной информации, отображается ли какая-либо часть элемента в представлении или нет. Не используйте это для вариантов исполнения или только для вертикальных слайдов! Этот код более эффективен при рисовании.
Наиболее распространенные ответы не работают при увеличении масштаба в 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);
Все ответы здесь определяют, полностью ли элемент содержится в области просмотра, а не просто каким-то образом виден. Например, если в нижней части представления видна только половина изображения, решения здесь не сработают, учитывая, что это «снаружи».
У меня был случай использования, когда я выполнял ленивую загрузку через 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));
Это в основном проверка того, находится ли верхняя или нижняя граница независимо в области просмотра. Противоположный конец может быть снаружи, но пока один конец находится внутри, он хотя бы частично «виден».
Уточнение: Видно, как в пределах текущего отображаемого прямоугольника. Видимый, не скрытый или отображаемый: нет? Видно, а не за чем-то еще в Z-порядке?