Как проверить, действительно ли элемент виден в JavaScript?
В JavaScript, как бы вы проверили, действительно ли элемент виден?
Я не имею в виду проверку visibility
а также display
атрибутов. Я имею в виду, проверяя, что элемент не
visibility: hidden
или жеdisplay: none
- под другим элементом
- прокручивал от края экрана
По техническим причинам я не могу включить какие-либо сценарии. Однако я могу использовать Prototype, как он есть на странице уже.
17 ответов
Для пункта 2.
Я вижу, что никто не предложил использовать document.elementFromPoint(x,y)
для меня это самый быстрый способ проверить, является ли элемент вложенным или скрытым другим. Вы можете передать смещения целевого элемента в функцию.
Вот тестовая страница PPK на elementFromPoint.
Я не знаю, насколько это поддерживается в старых или не очень современных браузерах, но я использую что-то вроде этого (без необходимости в каких-либо библиотеках):
function visible(element) {
if (element.offsetWidth === 0 || element.offsetHeight === 0) return false;
var height = document.documentElement.clientHeight,
rects = element.getClientRects(),
on_top = function(r) {
var x = (r.left + r.right)/2, y = (r.top + r.bottom)/2;
return document.elementFromPoint(x, y) === element;
};
for (var i = 0, l = rects.length; i < l; i++) {
var r = rects[i],
in_viewport = r.top > 0 ? r.top <= height : (r.bottom > 0 && r.bottom <= height);
if (in_viewport && on_top(r)) return true;
}
return false;
}
Он проверяет, что элемент имеет область> 0, а затем проверяет, находится ли какая-либо часть элемента в области просмотра и не скрыт ли он "под" другим элементом (на самом деле я проверяю только одну точку в центре элемента так что это не гарантировано на 100% - но вы можете просто изменить сценарий так, чтобы он повторялся по всем точкам элемента, если вам действительно нужно...).
Обновить
Изменена функция on_top, которая проверяет каждый пиксель:
on_top = function(r) {
for (var x = Math.floor(r.left), x_max = Math.ceil(r.right); x <= x_max; x++)
for (var y = Math.floor(r.top), y_max = Math.ceil(r.bottom); y <= y_max; y++) {
if (document.elementFromPoint(x, y) === element) return true;
}
return false;
};
Не знаю о спектакле:)
Как указал jkl, проверки видимости или отображения элемента недостаточно. Вы должны проверить его предков. Selenium делает это, когда проверяет видимость элемента.
Проверьте метод Selenium.prototype.isVisible в файле selenium-api.js.
http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails/selenium-core/scripts/selenium-api.js
Это то, что я до сих пор. Он охватывает как 1, так и 3. Однако я все еще борюсь с 2, так как я не очень знаком с Prototype (я скорее парень из jQuery).
function isVisible( elem ) {
var $elem = $(elem);
// First check if elem is hidden through css as this is not very costly:
if ($elem.getStyle('display') == 'none' || $elem.getStyle('visibility') == 'hidden' ) {
//elem is set through CSS stylesheet or inline to invisible
return false;
}
//Now check for the elem being outside of the viewport
var $elemOffset = $elem.viewportOffset();
if ($elemOffset.left < 0 || $elemOffset.top < 0) {
//elem is left of or above viewport
return false;
}
var vp = document.viewport.getDimensions();
if ($elemOffset.left > vp.width || $elemOffset.top > vp.height) {
//elem is below or right of vp
return false;
}
//Now check for elements positioned on top:
//TODO: Build check for this using Prototype...
//Neither of these was true, so the elem was visible:
return true;
}
Интересный вопрос.
Это был бы мой подход.
- Сначала проверьте этот element.style.visibility!== 'hidden' && element.style.display!== 'none'
- Затем проверьте с помощью document.elementFromPoint(element.offsetLeft, element.offsetTop), если возвращаемый элемент - это элемент, который я ожидаю, это сложно определить, если элемент полностью перекрывает другой.
- Наконец, проверьте, находятся ли offsetTop и offsetLeft в области просмотра с учетом смещений прокрутки.
Надеюсь, поможет.
Библиотека элементов прототипа является одной из самых мощных библиотек запросов с точки зрения методов. Я рекомендую вам проверить API.
Несколько подсказок:
Проверка видимости может быть трудной, но вы можете использовать
Element.getStyle()
метод иElement.visible()
методы объединены в пользовательскую функцию. СgetStyle()
Вы можете проверить фактический вычисленный стиль.Я не знаю точно, что вы имеете в виду под "под":) Если вы имели в виду под ним есть определенный предок, например, div-обертка, вы можете использовать
Element.up(cssRule)
:var child = $("myparagraph"); if(!child.up("mywrapper")){ // I lost my mom! } else { // I found my mom! }
Если вы хотите проверить родные элементы дочернего элемента, вы можете сделать это тоже:
var child = $("myparagraph"); if(!child.previous("mywrapper")){ // I lost my bro! } else { // I found my bro! }
Опять же, Element lib может помочь вам, если я правильно понимаю, что вы имеете в виду:) Вы можете проверить фактические размеры области просмотра и смещение вашего элемента, чтобы можно было рассчитать, находится ли ваш элемент вне экрана.
Удачи!
Я вставил тестовый пример для prototypejs по адресу http://gist.github.com/117125. Кажется, в вашем случае мы просто не можем доверять getStyle()
совсем. Для максимизации надежности функции isMyElementReallyVisible вы должны объединить следующее:
- Проверка вычисленного стиля (у dojo есть хорошая реализация, которую вы можете позаимствовать)
- Проверка viewportoffset (прототип нативный метод)
- Проверка z-index на наличие проблемы "под" (в Internet Explorer может быть ошибка)
Один из способов сделать это:
isVisible(elm) {
while(elm.tagName != 'BODY') {
if(!$(elm).visible()) return false;
elm = elm.parentNode;
}
return true;
}
Кредиты: https://github.com/atetlaw/Really-Easy-Field-Validation/blob/master/validation.js#L178
Вы можете использовать свойства clientHeight или clientWidth
function isViewable(element){
return (element.clientHeight > 0);
}
/**
* Checks display and visibility of elements and it's parents
* @param DomElement el
* @param boolean isDeep Watch parents? Default is true
* @return {Boolean}
*
* @author Oleksandr Knyga <oleksandrknyga@gmail.com>
*/
function isVisible(el, isDeep) {
var elIsVisible = true;
if("undefined" === typeof isDeep) {
isDeep = true;
}
elIsVisible = elIsVisible && el.offsetWidth > 0 && el.offsetHeight > 0;
if(isDeep && elIsVisible) {
while('BODY' != el.tagName && elIsVisible) {
elIsVisible = elIsVisible && 'hidden' != window.getComputedStyle(el).visibility;
el = el.parentElement;
}
}
return elIsVisible;
}
Пытаться element.getBoundingClientRect()
, Он вернет объект со свойствами
- низ
- Топ
- право
- оставил
- ширина - зависит от браузера
- высота - зависит от браузера
Проверьте, что ширина и высота элемента BoundingClientRect
не равны нулю, что является значением скрытых или невидимых элементов. Если значения больше нуля, элемент должен быть виден в теле. Затем проверьте, если bottom
собственность меньше чем screen.height
что означало бы, что элемент находится в области просмотра. (Технически вам также придется учитывать верхнюю часть окна браузера, включая панель поиска, кнопки и т. Д.)
Я бы не хотел перенаправлять вас в jQuery (как это часто делается), но эта дискуссия о том, когда элементы действительно видны, очень проницательна.
А начиная с jQuery 1.3.2 это больше не проблема.
Я не думаю, что проверка собственной видимости и свойств отображения элемента достаточно хороша для требования #1, даже если вы используете currentStyle/getComputedStyle. Вы также должны проверить предков элемента. Если предок скрыт, то и элемент тоже.
Ловить события перетаскивания мышью и области просмотра (onmouseup, onresize, onscroll).
Когда перетаскивание заканчивается, сделайте сравнение границы перетаскиваемого элемента со всеми "интересующими элементами" (то есть элементами с классом "dont_hide" или массивом идентификаторов). Сделайте то же самое с window.onscroll и window.onresize. Пометьте любые элементы, скрытые специальным атрибутом или именем класса, или просто выполните любое действие, которое вы хотите, там и тогда.
Скрытые тесты довольно просты. Для "полностью скрытого" вы хотите знать, находятся ли ВСЕ углы внутри границы перетаскиваемого элемента или вне области просмотра. Для частично скрытого вы ищете один угол, соответствующий тому же тесту.
Ответ на первый пункт довольно прост, если вы все еще можете использовать prototype (prototypejs):
$('HtmlElementID').visible(); returns: true|false
Проверьте свойство offsetHeight элементов. Если оно больше 0, это видно. Примечание: этот подход не охватывает ситуацию, когда видимость: скрытый стиль установлен. Но этот стиль в любом случае странный.
Вот пример сценария и тестовый пример. Охватывает расположенные элементы, видимость: скрыто, дисплей: нет. Не тестировал z-index, предположим, что он работает.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<style type="text/css">
div {
width: 200px;
border: 1px solid red;
}
p {
border: 2px solid green;
}
.r {
border: 1px solid #BB3333;
background: #EE9999;
position: relative;
top: -50px;
height: 2em;
}
.of {
overflow: hidden;
height: 2em;
word-wrap: none;
}
.of p {
width: 100%;
}
.of pre {
display: inline;
}
.iv {
visibility: hidden;
}
.dn {
display: none;
}
</style>
<script src="http://www.prototypejs.org/assets/2008/9/29/prototype-1.6.0.3.js"></script>
<script>
function isVisible(elem){
if (Element.getStyle(elem, 'visibility') == 'hidden' || Element.getStyle(elem, 'display') == 'none') {
return false;
}
var topx, topy, botx, boty;
var offset = Element.positionedOffset(elem);
topx = offset.left;
topy = offset.top;
botx = Element.getWidth(elem) + topx;
boty = Element.getHeight(elem) + topy;
var v = false;
for (var x = topx; x <= botx; x++) {
for(var y = topy; y <= boty; y++) {
if (document.elementFromPoint(x,y) == elem) {
// item is visible
v = true;
break;
}
}
if (v == true) {
break;
}
}
return v;
}
window.onload=function() {
var es = Element.descendants('body');
for (var i = 0; i < es.length; i++ ) {
if (!isVisible(es[i])) {
alert(es[i].tagName);
}
}
}
</script>
</head>
<body id='body'>
<div class="s"><p>This is text</p><p>More text</p></div>
<div class="r">This is relative</div>
<div class="of"><p>This is too wide...</p><pre>hidden</pre>
<div class="iv">This is invisible</div>
<div class="dn">This is display none</div>
</body>
</html>
Вот часть ответа, которая сообщает вам, находится ли элемент в области просмотра. Возможно, вам придется проверить, нет ли на нем ничего, используя elementFromPoint, но это немного дольше.
function isInViewport(element) {
var rect = element.getBoundingClientRect();
var windowHeight = window.innerHeight || document.documentElement.clientHeight;
var windowWidth = window.innerWidth || document.documentElement.clientWidth;
return rect.bottom > 0 && rect.top < windowHeight && rect.right > 0 && rect.left < windowWidth;
}