OK, but the recursive call searches up all of the parent chain also, so that makes it up to O(n^2) in the number of parents, admittedly only in the rare case where they are all scrollable.

I don't quite follow how it matters if the parent is outside the body, and if so why you want to just continue with the next parent.

Suppose the parent is outside the body. If it's overflow:visible, then that's irrelevant, right? Because the parent could be positioned way to the right, but the original element could be outside its bounds and on-screen. So it seems like you need to check overflow first.

If the parent is overflow:scroll or overflow:clip, then I agree you could cut off the computation early if the parent is offscreen, too - but that's not what you're doing, you're continuing with the next parent. I think you could just return false, unless I'm misunderstanding.

Still, my inclination is to do something like this:

if (element is outside the body)
    return false;

for each parent:
    if (parent clips or scrolls, AND the element is outside this parent)
        return false;

return true;

Does that work?