可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question already has an answer here:
-
Check if element is visible in DOM
17 answers
In JavaScript, how would you check if an element is actually visible?
I don\'t just mean checking the visibility
and display
attributes. I mean, checking that the element is not
visibility: hidden
or display: none
- underneath another element
- scrolled off the edge of the screen
For technical reasons I can\'t include any scripts. I can however use Prototype as it is on the page already.
回答1:
For the point 2.
I see that no one has suggested to use document.elementFromPoint(x,y)
, to me it is the fastest way to test if an element is nested or hidden by another. You can pass the offsets of the targetted element to the function.
Here\'s PPK test page on elementFromPoint.
回答2:
I don\'t know how much of this is supported in older or not-so-modern browsers, but I\'m using something like this (without the neeed for any libraries):
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;
}
It checks that the element has an area > 0 and then it checks if any part of the element is within the viewport and that it is not hidden \"under\" another element (actually I only check on a single point in the center of the element, so it\'s not 100% assured -- but you could just modify the script to itterate over all the points of the element, if you really need to...).
Update
Modified on_top function that check every pixel:
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;
};
Don\'t know about the performance :)
回答3:
As jkl pointed out, checking the element\'s visibility or display is not enough. You do have to check its ancestors. Selenium does this when it verifies visibility on an element.
Check out the method Selenium.prototype.isVisible in the selenium-api.js file.
http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails/selenium-core/scripts/selenium-api.js
回答4:
Interesting question.
This would be my approach.
- At first check that element.style.visibility !== \'hidden\' && element.style.display !== \'none\'
- Then test with document.elementFromPoint(element.offsetLeft, element.offsetTop) if the returned element is the element I expect, this is tricky to detect if an element is overlapping another completely.
- Finally test if offsetTop and offsetLeft are located in the viewport taking scroll offsets into account.
Hope it helps.
回答5:
This is what I have so far. It covers both 1 and 3. I\'m however still struggling with 2 since I\'m not that familiar with Prototype (I\'m more a jQuery type of guy).
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;
}
回答6:
Prototype\'s Element library is one of the most powerful query libraries in terms of the methods. I recommend you to check out the API.
A few hints:
Checking visibility can be a pain, but you can use the Element.getStyle()
method and Element.visible()
methods combined into a custom function. With getStyle()
you can check the actual computed style.
I don\'t know exactly what you mean by \"underneath\" :) If you meant by it has a specific ancestor, for example, a wrapper div, you can use Element.up(cssRule)
:
var child = $(\"myparagraph\");
if(!child.up(\"mywrapper\")){
// I lost my mom!
}
else {
// I found my mom!
}
If you want to check the siblings of the child element you can do that too:
var child = $(\"myparagraph\");
if(!child.previous(\"mywrapper\")){
// I lost my bro!
}
else {
// I found my bro!
}
Again, Element lib can help you if I understand correctly what you mean :) You can check the actual dimensions of the viewport and the offset of your element so you can calculate if your element is \"off screen\".
Good luck!
I pasted a test case for prototypejs at http://gist.github.com/117125. It seems in your case we simply cannot trust in getStyle()
at all. For maximizing the reliability of the isMyElementReallyVisible function you should combine the following:
- Checking the computed style (dojo has a nice implementation that you can borrow)
- Checking the viewportoffset (prototype native method)
- Checking the z-index for the \"beneath\" problem (under Internet Explorer it may be buggy)
回答7:
One way to do it is:
isVisible(elm) {
while(elm.tagName != \'BODY\') {
if(!$(elm).visible()) return false;
elm = elm.parentNode;
}
return true;
}
Credits: https://github.com/atetlaw/Really-Easy-Field-Validation/blob/master/validation.js#L178
回答8:
Catch mouse-drag and viewport events (onmouseup, onresize, onscroll).
When a drag ends do a comparison of the dragged item boundary with all \"elements of interest\" (ie, elements with class \"dont_hide\" or an array of ids). Do the same with window.onscroll and window.onresize. Mark any elements hidden with a special attribute or classname or simply perform whatever action you want then and there.
The hidden tests are pretty easy. For \"totally hidden\" you want to know if ALL corners are either inside the dragged-item boundary or outside the viewport. For partially hidden you\'re looking for a single corner matching the same test.
回答9:
I don\'t think checking the element\'s own visibility and display properties is good enough for requirement #1, even if you use currentStyle/getComputedStyle. You also have to check the element\'s ancestors. If an ancestor is hidden, so is the element.
回答10:
I\'d hate to redirect you to jQuery (like often is done), but this discussion about when elements are really visible is very insightful.
And since jQuery 1.3.2 this is no longer a problem.
回答11:
Check elements\' offsetHeight property. If it is more than 0, it is visible. Note: this approach doesn\'t cover a situation when visibility:hidden style is set. But that style is something weird anyways.
回答12:
Here is a sample script and test case. Covers positioned elements, visibilty: hidden, display: none. Didn\'t test z-index, assume it works.
<!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>
回答13:
/**
* 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;
}
回答14:
You can use the clientHeight or clientWidth properties
function isViewable(element){
return (element.clientHeight > 0);
}
回答15:
Try element.getBoundingClientRect()
.
It will return an object with properties
- bottom
- top
- right
- left
- width -- browser dependent
- height -- browser dependent
Check that the width and height of the element\'s BoundingClientRect
are not zero which is the value of hidden or non-visible elements. If the values are greater than zero the element should be visible in the body. Then check if the bottom
property is less than screen.height
which would imply that the element is withing the viewport. (Technically you would also have to account for the top of the browser window including the searchbar, buttons, etc.)
回答16:
The answer to the first point is quite simple if you\'re still able to use prototype (prototypejs):
$(\'HtmlElementID\').visible(); returns: true|false
回答17:
Here is a part of the response that tells you if an element is in the viewport.
You may need to check if there is nothing on top of it using elementFromPoint, but it\'s a bit longer.
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;
}