有没有纯JS(不用jQuery)可以检查元素是否可见的方法?
因此,给定一个DOM元素,如何检查它是否可见?我尝试过:
window.getComputedStyle(my_element)['display']);
但它似乎没有起作用。我想知道我应该检查哪些属性。以下一些可能值得检查:
display !== 'none'
visibility !== 'hidden'
还有其他我可能遗漏的吗?
有没有纯JS(不用jQuery)可以检查元素是否可见的方法?
因此,给定一个DOM元素,如何检查它是否可见?我尝试过:
window.getComputedStyle(my_element)['display']);
但它似乎没有起作用。我想知道我应该检查哪些属性。以下一些可能值得检查:
display !== 'none'
visibility !== 'hidden'
还有其他我可能遗漏的吗?
offsetParent
属性将会在通过显示样式属性隐藏它或者它的某个父级元素时返回null
。只需要确保该元素不是固定定位的。如果页面上没有position: fixed;
元素,检查此问题的脚本可能如下所示:// Where el is the DOM element you'd like to test for visibility
function isHidden(el) {
return (el.offsetParent === null)
}
另一方面,如果您有可能被捕捉到的固定位置元素,则不幸的是(而且速度会很慢),您将需要使用window.getComputedStyle()
。在这种情况下,函数可能是:
// Where el is the DOM element you'd like to test for visibility
function isHidden(el) {
var style = window.getComputedStyle(el);
return (style.display === 'none')
}
选项#2可能会更加直接,因为它涵盖了更多的边缘情况,但我敢打赌它也会慢得多,所以如果您需要重复执行此操作多次,最好还是避免使用它。
el.offsetParent
在IE9中对于非fixed元素无法正常工作。不过好像是这样的。(在IE11上没有问题。)所以最终还是用了getComputedStyle
。 - NickgetComputedStyle
的工作不正确:http://plnkr.co/edit/6CSCA2fe4Gqt4jCBP2wu?p=preview。
然而,offsetParent
也是如此-也许应该结合使用这两个方法? - guy mograbidisplay:none
时,getComputedStyle(element).display
的值为 table
,使其本质上无用。 - Michael其他所有的解决方案在某些情况下都出现了问题...
请查看获胜答案失败的情况:
http://plnkr.co/edit/6CSCA2fe4Gqt4jCBP2wu?p=preview
最终,我决定最好的解决方案是$(elem).is(':visible')
- 然而,这不是纯javascript,它是jquery..
所以,我瞥了一眼他们的源代码并找到了我想要的内容
jQuery.expr.filters.visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};
这是源代码:https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
visibility:hidden
属性的元素,此代码将返回true
。 - Yuval A.visibility:hidden
会使内容不再显示,但元素仍然占据宽度和高度! - Jacob van Lingenvisibility:hidden
的版本,可以在此返回行末尾添加 && window.getComputedStyle(elem).visibility !== "hidden"
。这样做似乎可行。 - dkniffin&&
,例如(elem.offsetWidth && elem.offsetHeight && elem.getClientRects().length)
,因为如果宽度或高度为0,那么元素对用户来说是不可见的。 - lanxion如果您希望用户可见:
function isVisible(elem) {
if (!(elem instanceof Element)) throw Error('DomUtil: elem is not an element.');
const style = getComputedStyle(elem);
if (style.display === 'none') return false;
if (style.visibility !== 'visible') return false;
if (style.opacity < 0.1) return false;
if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
elem.getBoundingClientRect().width === 0) {
return false;
}
const elemCenter = {
x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
};
if (elemCenter.x < 0) return false;
if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
if (elemCenter.y < 0) return false;
if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
let pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y);
do {
if (pointContainer === elem) return true;
} while (pointContainer = pointContainer.parentNode);
return false;
}
在(使用Mocha术语)上经过测试:
describe.only('visibility', function () {
let div, visible, notVisible, inViewport, leftOfViewport, rightOfViewport, aboveViewport,
belowViewport, notDisplayed, zeroOpacity, zIndex1, zIndex2;
before(() => {
div = document.createElement('div');
document.querySelector('body').appendChild(div);
div.appendChild(visible = document.createElement('div'));
visible.style = 'border: 1px solid black; margin: 5px; display: inline-block;';
visible.textContent = 'visible';
div.appendChild(inViewport = visible.cloneNode(false));
inViewport.textContent = 'inViewport';
div.appendChild(notDisplayed = visible.cloneNode(false));
notDisplayed.style.display = 'none';
notDisplayed.textContent = 'notDisplayed';
div.appendChild(notVisible = visible.cloneNode(false));
notVisible.style.visibility = 'hidden';
notVisible.textContent = 'notVisible';
div.appendChild(leftOfViewport = visible.cloneNode(false));
leftOfViewport.style.position = 'absolute';
leftOfViewport.style.right = '100000px';
leftOfViewport.textContent = 'leftOfViewport';
div.appendChild(rightOfViewport = leftOfViewport.cloneNode(false));
rightOfViewport.style.right = '0';
rightOfViewport.style.left = '100000px';
rightOfViewport.textContent = 'rightOfViewport';
div.appendChild(aboveViewport = leftOfViewport.cloneNode(false));
aboveViewport.style.right = '0';
aboveViewport.style.bottom = '100000px';
aboveViewport.textContent = 'aboveViewport';
div.appendChild(belowViewport = leftOfViewport.cloneNode(false));
belowViewport.style.right = '0';
belowViewport.style.top = '100000px';
belowViewport.textContent = 'belowViewport';
div.appendChild(zeroOpacity = visible.cloneNode(false));
zeroOpacity.textContent = 'zeroOpacity';
zeroOpacity.style.opacity = '0';
div.appendChild(zIndex1 = visible.cloneNode(false));
zIndex1.textContent = 'zIndex1';
zIndex1.style.position = 'absolute';
zIndex1.style.left = zIndex1.style.top = zIndex1.style.width = zIndex1.style.height = '100px';
zIndex1.style.zIndex = '1';
div.appendChild(zIndex2 = zIndex1.cloneNode(false));
zIndex2.textContent = 'zIndex2';
zIndex2.style.left = zIndex2.style.top = '90px';
zIndex2.style.width = zIndex2.style.height = '120px';
zIndex2.style.backgroundColor = 'red';
zIndex2.style.zIndex = '2';
});
after(() => {
div.parentNode.removeChild(div);
});
it('isVisible = true', () => {
expect(isVisible(div)).to.be.true;
expect(isVisible(visible)).to.be.true;
expect(isVisible(inViewport)).to.be.true;
expect(isVisible(zIndex2)).to.be.true;
});
it('isVisible = false', () => {
expect(isVisible(notDisplayed)).to.be.false;
expect(isVisible(notVisible)).to.be.false;
expect(isVisible(document.createElement('div'))).to.be.false;
expect(isVisible(zIndex1)).to.be.false;
expect(isVisible(zeroOpacity)).to.be.false;
expect(isVisible(leftOfViewport)).to.be.false;
expect(isVisible(rightOfViewport)).to.be.false;
expect(isVisible(aboveViewport)).to.be.false;
expect(isVisible(belowViewport)).to.be.false;
});
});
scrollIntoView
,对吧?这相当耗费资源。有没有其他聪明的方法? - Kim Kern使用和jQuery相同的代码:
the same code as jQuery does:jQuery.expr.pseudos.visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};
所以,在一个函数中:
function isVisible(e) {
return !!( e.offsetWidth || e.offsetHeight || e.getClientRects().length );
}
在我的Win/IE10、Linux/Firefox.45和Linux/Chrome.52上非常顺畅地运行,感谢jQuery(没有用到jQuery)!
e.offsetWidth
是整数,如果e.offsetWidth
大于零(元素可见),!e.offsetWidth
将返回false
。因此,在!
后添加另一个!
,即!!e.offsetWidth
,如果e.offsetWidth
大于零,则将返回true
。这是return e.offsetWidth > 0 ? true : false
或者显然的return e.offsetWidth > 0
的简写。 - Yvan!
而不是!!
吗? - Jarad!!
用于强制结果为布尔值。但是由于你想要相反的结果,只需要一个就可以了。 - Yvan对我来说,被接受的答案没有起作用。
The (elem.offsetParent !== null)
method works fine in Firefox but not in Chrome. In Chrome position: fixed
will also make offsetParent
return null
even the element if visible in the page.
User Phrogz conducted a large test (2,304 divs) on elements with varying properties to demonstrate the issue. https://dev59.com/DXVC5IYBdhLWcg3wZwTj#11639664 . Run it with multiple browsers to see the differences.
Demo:
//different results in Chrome and Firefox
console.log(document.querySelector('#hidden1').offsetParent); //null Chrome & Firefox
console.log(document.querySelector('#fixed1').offsetParent); //null in Chrome, not null in Firefox
<div id="hidden1" style="display:none;"></div>
<div id="fixed1" style="position:fixed;"></div>
The (getComputedStyle(elem).display !== 'none')
does not work because the element can be invisible because one of the parents display property is set to none, getComputedStyle
will not catch that.
Demo:
var child1 = document.querySelector('#child1');
console.log(getComputedStyle(child1).display);
//child will show "block" instead of "none"
<div id="parent1" style="display:none;">
<div id="child1" style="display:block"></div>
</div>
The (elem.clientHeight !== 0)
. This method is not influenced by position: fixed
and it also check if element parents are not-visible. But it has problems with simple elements that do not have a css layout and inline elements, see more here
Demo:
console.log(document.querySelector('#inline1').clientHeight); //zero
console.log(document.querySelector('#div1').clientHeight); //not zero
console.log(document.querySelector('#span1').clientHeight); //zero
<div id="inline1" style="display:inline">test1 inline</div>
<div id="div1">test2 div</div>
<span id="span1">test3 span</span>
The (elem.getClientRects().length !== 0)
may seem to solve the problems of the previous 3 methods. However it has problems with elements that use CSS tricks (other then display: none
) to hide in the page.
Demo
console.log(document.querySelector('#notvisible1').getClientRects().length);
console.log(document.querySelector('#notvisible1').clientHeight);
console.log(document.querySelector('#notvisible2').getClientRects().length);
console.log(document.querySelector('#notvisible2').clientHeight);
console.log(document.querySelector('#notvisible3').getClientRects().length);
console.log(document.querySelector('#notvisible3').clientHeight);
<div id="notvisible1" style="height:0; overflow:hidden; background-color:red;">not visible 1</div>
<div id="notvisible2" style="visibility:hidden; background-color:yellow;">not visible 2</div>
<div id="notvisible3" style="opacity:0; background-color:blue;">not visible 3</div>
所以,我向您展示的是没有一种方法是完美的。为了进行适当的可见性检查,您必须使用最后3种方法的组合。
这可能会有所帮助: 将元素定位在最左侧位置,然后检查offsetLeft属性来隐藏元素。如果你想使用jQuery,可以简单地检查:visible选择器并获取元素的可见状态。
<div id="myDiv">Hello</div>
CSS:
<!-- for javaScript-->
#myDiv{
position:absolute;
left : -2000px;
}
<!-- for jQuery -->
#myDiv{
visibility:hidden;
}
JavaScript:
var myStyle = document.getElementById("myDiv").offsetLeft;
if(myStyle < 0){
alert("Div is hidden!!");
}
jQuery :
if( $("#MyElement").is(":visible") == true )
{
alert("Div is visible!!");
}
Chrome 105(以及Edge和Opera)和Firefox 106引入了Element.checkVisibility()
函数,它会返回true
当元素可见时,并在不可见时返回false
。
该函数检查许多因素,这些因素会使元素变得不可见,包括display:none
、visibility
、content-visibility
和opacity
属性:
let element = document.getElementById("myIcon");
let isVisible = element.checkVisibility({
checkOpacity: true, // Check CSS opacity property too
checkVisibilityCSS: true // Check CSS visibility property too
});
旁注: checkVisibility()
之前被称为 isVisible()
。请参见此 GitHub 问题。
请在checkVisibility()
规范草案中查看。
checkVisibility
。 - amphetamachine根据MDN文档,交互观察器可以异步地观察目标元素与其祖先元素或顶层文档视口的交集变化。这意味着每当元素与视口相交时,交互观察器都会被触发。
截至2021年,除了IE浏览器,所有当前的浏览器都支持交互观察器。
const el = document.getElementById("your-target-element");
const observer = new IntersectionObserver((entries) => {
if(entries[0].isIntersecting){
// el is visible
} else {
// el is not visible
}
});
observer.observe(el); // Asynchronous call
处理程序将在初始创建时触发。然后,每当它变得稍微可见或完全不可见时,它都会被触发。当元素实际上不在视口中可见时,它被认为是不可见的。因此,如果您向下滚动并且元素从屏幕上消失,则观察器将触发,并且// el is not visible
代码将被触发-即使元素仍然“显示”(即没有display:none
或visibility:hidden
)。重要的是检查元素中是否有任何像素实际上在视口内是可见的。observer.observe(el)
后,您不会立即获得结果; - Liero// el is not visible
代码。我将把这个评论添加到答案中。 - joe综合上面几个回答:
function isVisible (ele) {
var style = window.getComputedStyle(ele);
return style.width !== "0" &&
style.height !== "0" &&
style.opacity !== "0" &&
style.display!=='none' &&
style.visibility!== 'hidden';
}
就像AlexZ所说的,如果您更明确地知道您要寻找的内容,这种方法可能比其他一些选项更慢,但它应该可以捕获所有隐藏元素的主要方式。
但是,这也取决于您认为什么算是可见的。例如,一个div的高度可以设置为0px,但根据溢出属性,其内容仍然可见。或者一个div的内容可以与背景颜色相同,因此对用户不可见,但仍在页面上呈现。或者一个div可能被移动到屏幕外或隐藏在其他div后面,或者其内容可能是不可见的,但边框仍然可见。到某种程度上,“可见”是一个主观的术语。
相比于AlexZ的getComputedStyle()解决方案,我有一个更高效的解决方案,当一个元素有'fixed'定位时,如果愿意忽略一些边缘情况(请查看注释):
function isVisible(el) {
/* offsetParent would be null if display 'none' is set.
However Chrome, IE and MS Edge returns offsetParent as null for elements
with CSS position 'fixed'. So check whether the dimensions are zero.
This check would be inaccurate if position is 'fixed' AND dimensions were
intentionally set to zero. But..it is good enough for most cases.*/
return Boolean(el.offsetParent || el.offsetWidth || el.offsetHeight);
}
顺便提一下:严格来说,"visibility"需要先定义。在我的情况下,只要我能够正常运行所有DOM方法/属性(即使不透明度为0或CSS可见性属性为'hidden'等),我就认为一个元素是可见的。
document.getElementById('snDealsPanel').style.visibility
。 - PSL