5

Is it possible to determine if an html element is visible to the user?

Example

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.

First Approach

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?

Alp
  • 29,274
  • 27
  • 120
  • 198
  • I doubt there's any way for JavaScript to detect if an overlapping image has transparent 'colors'. So even if you did identify that the object was completely covered by another image, you couldn't know if that image was actually obscuring what's beneath it. – Blazemonger Mar 28 '12 at 19:57
  • You are right on that. But i can live with that restriction. – Alp Mar 28 '12 at 20:09

3 Answers3

9

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

Selvakumar Arumugam
  • 79,297
  • 15
  • 120
  • 134
2

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!

Community
  • 1
  • 1
IanW
  • 743
  • 5
  • 15
  • Assumed i dont know which one of both elements is in the front, how can i determine it? – Alp Mar 28 '12 at 20:07
  • If you needed that, I suppose you could do `elementWithinElement(first, second)` If true, "second" is in front. Then, `elementWithinElement(second, first)` If true, "first" is in front. – IanW Mar 28 '12 at 20:12
  • I tried it on this page with `elementWithinElement(jQuery('#chat-feature'), jQuery('#sidebar'))` but it returns false. – Alp Mar 28 '12 at 20:12
  • Oh, try grabbing the javascript object. `elementWithinElement(jQuery('#chat-feature').get(0), jQuery('#sidebar').get(0))` Also note that I had to edit the function a couple times when I realized some mistakes, so make sure you have the current version :) – IanW Mar 28 '12 at 20:14
  • oh sure, you are right. thanks, that works. let me test it on my page. – Alp Mar 28 '12 at 20:16
1

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 :)

The Angry Saxon
  • 792
  • 2
  • 7
  • 24
  • Good answer. I think this might be the only way. Get the rectangle coords of the element you're checking and see if that's within the rectangle of the datepicker div. To get the rectangle of the object, get the X and Y offset (top left point of the rectangle) and then x + width, y + height (bottom right point of the rectangle) – IanW Mar 28 '12 at 19:49
  • Yeah exactly what I was thinking. Sounds easy in theory putting into practice I can imagine is a little harder. Although you might be pleasantly surprised :) – The Angry Saxon Mar 28 '12 at 19:51
  • What you describe is something like collision detection, which should be quite straightforward to implement. But how can i determine which of both is in the front? – Alp Mar 28 '12 at 20:04
  • @Alp Check out my answer with the code. Are you using a jQuery DatePicker? If so, the datepicker element will be 'display:none' when it's hidden. In this case, elementWithinElement would return false (meaning the element underneath is visible) – IanW Mar 28 '12 at 20:10
  • I guess you'd have to add z-indexing to the item you want in the front. and then check that the z-index is higher than 0 – The Angry Saxon Mar 28 '12 at 20:12