1

How to detect if an element inside a scrollable block is visible to user (i.e. is in the visible area of the scrollable parent)?

Is there a universal solution, not involving iterating over all parent nodes that have scroll?

P.S. One idea I had was getElementAtPoint, however it gives me headaches when I need to determine if at least 50% of the element is visible. So ideally the solution must involve collision detection between two rectangles: the element rectangle and the window.

P.P.S. Another idea I've come up with is to use scrollIntoView on the element in question, determine the difference in its position, and then scroll it back to original position. It appears scrollIntoView always does the right thing – scrolls both window and the inner scrollable blocks!

katspaugh
  • 15,752
  • 9
  • 61
  • 97
  • What problem you want to solve it? – maximkou Jun 14 '13 at 08:50
  • @maximkou, I want to check if element is visible on screen (assuming it can only be hidden behind scroll overflow). – katspaugh Jun 14 '13 at 08:53
  • Maybe will this code help you: https://github.com/morr/jquery.appear/blob/master/jquery.appear.js – maximkou Jun 14 '13 at 08:58
  • Or this answer: http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling – maximkou Jun 14 '13 at 09:00
  • @maximkou, those only assume window scroll, not inner elements scroll. – katspaugh Jun 14 '13 at 09:05
  • @katspaugh Well, I was just going to remove my comment, since I realized this. Also noticed you've tagged `getBoundingClientRect` too. – Teemu Jun 14 '13 at 09:39
  • @Teemu, rest assured that your input is appreciated anyway. – katspaugh Jun 14 '13 at 09:42
  • 1
    @katspaugh I tried to implement this with a simple iteration at [jsFiddle](http://jsfiddle.net/bZx97/), but it works correctly only in IE9 -10. Looks like other browsers are giving different values from `body/html.getBoundingClientRect()`. – Teemu Jun 14 '13 at 11:17
  • @Teemu, it represents the idea quite nicely, thank you! If you posted it as an answer I would gladly upvote it. – katspaugh Jun 14 '13 at 12:30

1 Answers1

1

I'm afraid this can't be done without iterating, and even less cross-browser, with some simple code.

Here's an example, how this can be done in IE. Unfortenately other browsers seem to return different values from body/html.getBoundingClientRect(). Also margins are treated differently, (IE ignores, others take them account).

getVisibilityPercent = function () {
    var target = document.getElementById('target'),
        height = target.offsetHeight,
        parent = target.parentElement,
        targetRect = target.getBoundingClientRect(),
        tLim, bLim,
        percent = 1;
    while (parent) {
        parentRect = parent.getBoundingClientRect();
        tLim = Math.max(targetRect.top, parentRect.top); 
        bLim = Math.min(targetRect.bottom, parentRect.bottom);
        percent *= (bLim - tLim) / height;
        percent = (percent < 0) ? 0 : percent;
        parent = parent.parentElement;
    }
    return +((percent * 100).toFixed(2));
};
Teemu
  • 21,017
  • 6
  • 49
  • 91