How do I check if an element is really visible with JavaScript? [duplicate]
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
ordisplay: 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.
Solution 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.
From MDN's documentation:
The
elementFromPoint()
method—available on both the Document and ShadowRoot objects—returns the topmost Element at the specified coordinates (relative to the viewport).
Solution 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 :)
Solution 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