Is there a way to calculate the visible area of an element on the screen, taking into account any hidden parts due to CSS properties like overflow: scroll and position: absolute?
The goal is to create a function called getVisiblePart(el)
that will return the visible rectangle coordinates as follows:
Visible Rect is: {x: 10, y: 20, height: 50, width: 700}
Context: This need arises from a requirement in the W3C specification for webdriver interactions: https://w3c.github.io/webdriver/webdriver-spec.html#element-interactability
The in-view centre point of an element refers to the center point of the visible area within the viewport.
Frameworks such as selenoid/selenide rely on this principle to locate and interact with elements during end-to-end tests by calculating offsets. The challenge lies in determining the exact visible area dimensions of an element at any given moment, especially when considering offsets such as upper left corner calculations.
A potential solution within selenoid/selenide could be:
Selenide.actions().moveToElement(el, getVisiblePart(el).width / -2, getVisiblePart(el).height / -2)
I have researched similar topics extensively, including:
- How can I tell if a DOM element is visible in the current viewport? (only addresses visibility without accounting for partial hiding)
- How to check if element is visible after scrolling? (similar limitation)
- Is it possible to programmatically determine whether W3C action commands are used? (close but inadequate answer)
Existing solutions either provide a binary visibility status or overlook scenarios where an element may be partially hidden due to CSS properties like overflow: scroll.
For instance, consider the following scenario involving scrolls (finding the visible position of a blue
rectangle despite scroll positions):
.a {
height: 250px;
overflow: scroll;
padding-top: 100px;
background: yellow;
}
.b {
height: 500px;
overflow: scroll;
}
.c {
height: 1000px;
background: blue;
}
#target {
border: 2px dashed red;
position: absolute;
pointer-events: none;
transform: translate(-1px,-1px); /*because of border*/
}
<div class="a">
<div class="b">
<div class="c" />
</div>
</div>
<div id="target" />
I have addressed part of this problem by leveraging the Intersection Observer API, but I find this approach lacking due to its lack of synchronicity and cross-browser compatibility concerns.