检查元素在DOM中是否可见

有没有什么办法可以检查一个元素是否在纯JS(没有jQuery)中可见?

因此,例如,在此页面中:如果您将鼠标hover在交易上(在顶部菜单上),则会出现交易窗口,但在开始时并未显示交易窗口。 它在HTML中,但不可见。

所以,给定一个DOM元素,我该如何检查它是否可见? 我试过: window.getComputedStyle(my_element)['display']); 但似乎没有工作。 我不知道应该检查哪些属性。 在我看来:

 display !== 'none' visibility !== 'hidden' 

任何其他我可能会失踪?

根据这个MDN文档 ,元素的offsetParent属性将通过显示样式属性隐藏,只要它或它的任何父母被隐藏。 只要确保元素不固定。 一个脚本来检查这个,如果你没有'position:fixed' 您网页上的元素可能如下所示:

 //Where el is the DOM element you'd like to test for visibility function isHidden(el) { return (el.offsetParent === null) } 

另一方面,如果你有位置固定的元素,可能会在这个search中被捕获,你会伤心地(而且慢)不得不使用window.getComputedStyle() 。 这种情况下的function可能是:

 //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可能更简单一些,因为它占了更多的边缘情况,但是我敢打赌它的速度也慢了很多,所以如果你不得不多次重复这个操作,最好避免它。

所有其他的解决scheme打破了我的一些情况..

看到获胜的答案打破在:

http://plnkr.co/edit/6CSCA2fe4Gqt4jCBP2wu?p=preview

最后,我决定最好的解决scheme是$(elem).is(':visible') – 但是,这不是纯粹的JavaScript。 这是jQuery ..

所以我偷看他们的来源,find我想要的东西

 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

这可能有助于:通过将元素置于最靠左的位置来隐藏元素,然后检查offsetLeft属性。 如果你想使用jQuery,你可以简单地检查:visibleselect器,并获取元素的可见性状态。

HTML:

 <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 hidden!!"); } 

的jsfiddle

如果您对用户可见的兴趣:

 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; } 

testing(使用摩卡术语):

 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; }); }); 

使用与jQuery相同的代码:

 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!

结合以上几个答案:

 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的后面,或者它的内容可能不可见,但边框仍然可见。 在一定程度上,“可见”是一个主观的术语。

如果我们只是收集检测能见度的基本方法,让我不要忘记:

 opacity > 0.01; // probably more like .1 to actually be visible, but YMMV 

至于如何获得属性:

 element.getAttribute(attributename); 

所以,在你的例子中:

 document.getElementById('snDealsPanel').getAttribute('visibility'); 

但是呢? 这在这里不起作用。 仔细观察,您会发现可见性正在被更新,而不是作为元素的属性,而是使用style属性。 这是尝试做你在做什么的许多问题之一。 除此之外:你不能保证在一个元素中有实际的东西可以看到,只是因为它的可见性,显示和不透明性都有正确的值。 它仍然可能缺乏内容,或者可能缺乏高度和宽度。 另一个对象可能会模糊它。 有关更多详细信息,Googlesearch快速了解了这一点 ,甚至还包括一个图书馆来解决问题。 (YMMV)

看看下面的问题,这个问题可能是重复的,并带有很好的答案,包括来自强大的John Resig的一些见解。 但是,您的具体使用情况与标准使用情况略有不同,因此我不会标记:

  • 如何判断一个DOM元素在当前视口中是否可见?
  • 如何检查一个元素是否真的可以用JavaScript?

(编辑:OP说他是刮板,不创build它们,所以下面不适用)一个更好的select? 将元素的可见性绑定到模型属性上,并始终使可见性与该模型相关,就像Angular对ng-show所做的一样。 你可以用你想要的任何工具来做到这一点:Angular,纯JS,不pipe。 更好的是,您可以随时改变DOM实现,但是您将始终能够从模型中读取状态,而不是DOM。 从DOM中读取你的真相是不好的。 而且慢。 更好地检查模型,并相信您的实现,以确保DOM状态反映模型。 (并使用自动化testing来确认这一假设。)

如果元素是可见的(display:block和visibillity:visible),但是一些父容器是隐藏的,那么我们可以使用clientWidthclientHeight来检查。

 function isVisible (ele) { return ele.clientWidth !== 0 && ele.clientHeight !== 0 && ele.style.opacity !== 0 && ele.style.visibility !== 'hidden'; } 

Plunker(点击这里)

与AlexZ的getComputedStyle()解决scheme相比,我有一个更高性能的解决scheme,如果有人愿意忽略一些边缘情况(检查注释):

 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.*/ if (!el.offsetParent && el.offsetWidth === 0 && el.offsetHeight === 0) { return false; } return true; } 

附注:严格地说,“可见性”需要先定义。 在我的情况下,我正在考虑一个可见的元素,只要我可以毫无问题地运行所有的DOM方法/属性(即使不透明度为0或CSS可见性属性为“隐藏”等)。

http://code.jquery.com/jquery-1.11.1.js中的jQuery代码有一个isHidden参数;

 var isHidden = function( elem, el ) { // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); }; 

所以看起来有一个额外的检查与所有者文件相关

我不知道这是否真的捕捉到以下情况:

  1. 隐藏在基于zIndex的其他元素后面的元素
  2. 元素与透明度充分使他们隐形
  3. 定位在屏幕外的元素(即左:-1000像素)
  4. 具有可见性的元素:隐藏
  5. 显示元素:无
  6. 元素没有可见的文本或子元素
  7. 高度或宽度设置为0的元素

所以我发现是最可行的方法:

 function visible(elm) { if(!elm.offsetHeight && !elm.offsetWidth) { return false; } if(getComputedStyle(elm).visibility === 'hidden') { return false; } return true; } 

这是build立在这些事实上的:

  • display: none元素(即使是嵌套元素)也没有宽度和高度。
  • 即使对于嵌套元素, visiblity也是hidden

因此,不需要testingoffsetParent或在DOM树中循环testing哪个父级visibility: hidden 。 即使在IE 9中,这也应该可以工作。

你可以争辩,如果opacity: 0和折叠的元素(有宽度,但没有高度,或反之亦然)也不是真的可见。 但是,他们又不是说隐藏的。

这是确定所有CSS属性(包括可见性)的方法:

HTML:

 <div id="element">div content</div> 

CSS:

 #element { visibility:hidden; } 

JavaScript的:

 var element = document.getElementById('element'); if(element.style.visibility == 'hidden'){ alert('hidden'); } else { alert('visible'); } 

它适用于任何CSS属性,是非常灵活和可靠的。

仅供参考,应该注意的是getBoundingClientRect()可以在某些情况下工作。

例如,使用display: none可以简单地检查元素是否隐藏,看起来有点像这样:

 var box = element.getBoundingClientRect(); var visible = box.width && box.height; 

这也是方便的,因为它也涵盖了零宽度,零高度和position: fixed情况。 但是,它不应该报告隐藏opacity: 0元素opacity: 0visibility: hidden (但都不会offsetParent )。

以下是我写的代码,用于查找几个相似元素中唯一可见的代码,并在不使用jQuery的情况下返回其“class”属性的值:

  // Build a NodeList: var nl = document.querySelectorAll('.myCssSelector'); // convert it to array: var myArray = [];for(var i = nl.length; i--; myArray.unshift(nl[i])); // now find the visible (= with offsetWidth more than 0) item: for (i =0; i < myArray.length; i++){ var curEl = myArray[i]; if (curEl.offsetWidth !== 0){ return curEl.getAttribute("class"); } } 

当且仅当元素及其所有祖先都可见时才返回true。 它仅查看displayvisibility样式属性:

  var isVisible = function(el){ // returns true iff el and all its ancestors are visible return el.style.display !== 'none' && el.style.visibility !== 'hidden' && (el.parentElement? isVisible(el.parentElement): true) };