Reputation: 29739
Is it possible to determine if an html element is visible to the user?
A page has an input field with a datepicker. If the user clicks on the input field, another div appears which allows the user to select the desired date.
As long the datepicker is visible it hides elements which are behind it. I need a way to tell if an element is hidden or not.
One way would be to check and compare the z-index
values. But if they are note explicitly set, they are always auto
.
Another way could be a way to check if an element is visible to the user. But i can't think of any way to do so.
The :visible
selector does not work in this situation, because the element is only hidden to the user's eyes but still visible.
Any suggestions?
Upvotes: 6
Views: 4435
Reputation: 1
function traversParents(el, callback, depth = 10) {
let level = depth;
let parent = el.parentElement;
while (level > 0 && parent) {
const stop = callback(parent);
if (stop) break;
parent = parent.parentElement;
level--;
}
}
function findTopmost(els) {
let highest = 0;
let highestZIndex = 0;
const getZIndex = (el) => {
const computedStyle = window.getComputedStyle(el);
const isAbsolute = computedStyle.getPropertyValue('position') === 'absolute' ? 0.5 : 0;
return Number.parseInt(computedStyle.getPropertyValue('z-index')) || isAbsolute;
}
els.forEach((el, i) => {
let zIndex = getZIndex(el);
traversParents(el, (parent) => {
if (zIndex > 0) return true;
zIndex = getZIndex(parent);
}, 15);
if (zIndex > highestZIndex) {
highestZIndex = zIndex;
highest = i;
}
});
return els[highest];
}
Upvotes: 0
Reputation: 79830
I tried a different approach using elements coordinates (getBoundingClientRect
) and then using elementFromPoint
to see if the element is hidden or visible.
DEMO (Follow the instruction on the right side)
var rectPos = this.getBoundingClientRect();
var result = 0;
if (this == document.elementFromPoint(rectPos.left,
rectPos.top)) {
result++;
}
if (this == document.elementFromPoint(rectPos.left,
rectPos.bottom - 1)) {
result++;
}
if (this == document.elementFromPoint(rectPos.right - 1,
rectPos.top)) {
result++;
}
if (this == document.elementFromPoint(rectPos.right - 1, rectPos.bottom - 1)) {
result++;
}
if (result == 4) {
result = 'visible';
} else if (result == 0) {
result = 'hidden';
} else {
result = 'partially visible';
}
Further Readings: getBoundingClientRect, elementFromPoint
Upvotes: 10
Reputation: 773
This might work. I haven't tested it. It's a modified version of some code I found here.
function elementWithinElement(elemPossiblyCovered, elemPossiblyCovering)
{
var top = elemPossiblyCovered.offsetTop;
var left = elemPossiblyCovered.offsetLeft;
var width = elemPossiblyCovered.offsetWidth;
var height = elemPossiblyCovered.offsetHeight;
while (elemPossiblyCovered.offsetParent)
{
elemPossiblyCovered = elemPossiblyCovered.offsetParent;
top += elemPossiblyCovered.offsetTop;
left += elemPossiblyCovered.offsetLeft;
}
return (
top >= elemPossiblyCovering.offsetTop &&
left >= elemPossiblyCovering.offsetLeft &&
(top + height) <= (elemPossiblyCovering.offsetTop + elemPossiblyCovering.offsetHeight) &&
(left + width) <= (elemPossiblyCovering.offsetLeft + elemPossiblyCovering.offsetWidth)
);
}
So it'd be something like:
if(elementWithinElement(myTextbox, theDatepickerDiv))
{
// It's hidden
}else
{
//It's visible
}
Edit: Some of the code wasn't updated. Should be fixed now.
Edit Again: Fixed the code and tested it. It works!
Upvotes: 2
Reputation: 792
the only way I can think of is by getting the offset of each item and checking that onclick of something that the offset of the new item isn't within the offset of anything previous. Obviously that just the theory behind it making something that does that will take a long time. Good luck :)
Upvotes: 1