JavaScript: Retrieving the coordinates of the visible area of an element

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.

Answer №1

My solution utilizes the Intersection Observer API for achieving the desired outcome.

While this workaround serves its purpose, it falls short in terms of synchronicity when invoking the function and lacks cross-browser compatibility.

This demonstration showcases the envisioned final version that I aim to achieve.

var options = {
    // root: document.querySelector('#scrollArea'),
    // rootMargin: '0px',
    threshold: 1
}
var callback = function(entries, observer) {
    const r = entries[0].intersectionRect;
    document.getElementById('target').setAttribute('style', `top: ${r.top}px;left: ${r.left}px;height: ${r.height}px;width: ${r.width}px;`);
    observer.unobserve(c);
};

const c = document.querySelector('.c');
setInterval(() => {
  var observer = new IntersectionObserver(callback, options);
  observer.observe(c);
}, 200);
.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" />

Explanation: every 200ms we calculate the visible area of ​​the blue element and highlight it.

An illustration of how I implement this with Selenide:

    val containerSelector = "div.testEl"
    val rect = executeAsyncJavaScript<String>(
        "var callback = arguments[arguments.length - 1];" +
                "var el = document.querySelector('" + containerSelector + "');" +
                "new IntersectionObserver(" +
                "    (entries, observer) => {" +
                "        observer.unobserve(el);" +
                "        var r = entries[0].intersectionRect;" +
                "        callback([r.x, r.y, r.height, r.width].map(v => Math.ceil(v)).join(','));" +
                "    }," +
                "    { threshold: 1 }" +
                ").observe(el);"
    )!!.split(',').toTypedArray();

    LEFT_TOP_X = rect[3].toInt() / -2 + 1 // +1 for Float tolerance
    LEFT_TOP_Y = rect[2].toInt() / -2 + 1 // +1 for Float tolerance

    // Move cursor to TOP LEFT
    Selenide.actions().moveToElement(el, LEFT_TOP_X, LEFT_TOP_Y)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Substitute placeholders in the HTML code with Angular syntax

Lately, I have been encountering some issues with AngularJS. I have defined multiple scopes like this: app.controller('mainController', function($scope) { $scope.siteURL = "my website url"; }); When using the {{siteURL}} variable in ...

Angular2 - Access to fetch at 'https://example.com' from

Requesting some guidance on integrating the forecast API into my angular2 application. Currently facing a Cross-Origin Error while attempting to access the API. Any suggestions on resolving this issue? search(latitude: any, longitude: any){ consol ...

Leverage the camera functionality in both native and web applications using Ionic/AngularJS and Cordova

Could you provide some guidance on how to use the Camera feature in both web and native environments? I have tried implementing it using the code snippet below, taken from ng-cordova documentation: $scope.takePicture = function() { var options ...

Is there a way to access the value of a variable within a loop inside a function and store it in a global variable?

I am struggling to retrieve the value of a variable after it has passed through a loop. I attempted to make it a global variable, but its value remains unchanged. Is there any way to achieve this? Below is my code snippet where I am attempting to access t ...

Incorporate an onclick event to the elements within the array

I'm currently working on iterating over an array and adding an onclick event to each element. My goal is to be able to click on each div and have them log their values in the console. However, I am facing a challenge in applying the onclick event to e ...

reactjs function continually returning undefined

My approach is rather simple. It involves iterating through an array of objects and retrieving the object with the specified id in the parameter. Here's how it looks: returnValueToDelete(id) { this.state.rows.map(function(value) { if (value ...

Crafting redirect rules in React that avoid redirecting to the same route

In my project, there is a file named AuthenticatedRoute.tsx, which serves as a template for all the protected/authenticated routes in my Router.tsx file. export default ({ component: C, authUser: A, path: P, exact: E }: { component, authUser, path, ex ...

What is the significance of the error message '[WDS] Disconnected!' in the context of using webpack and Vue.js?

Currently, I am engaged in a Django project that utilizes Vue.js for the frontend. Whenever I refresh the page, I encounter the "[WDS] Disconnected!" error. Despite the website's full functionality and absence of issues, this error appears every time ...

Save unique data for each tab in the browser

In my web application, I store information about recently visited pages, which I'll refer to as type A. When a user visits a different page type, called B, I display a menu at the top with a button that links back to the most recently visited A-page. ...

Error in Robot Framework: Unable to assign attribute

Having some trouble with my robot framework. I'm attempting to run my code on GitLab, but it's not working. Could someone please take a look at my problem? *** Variables *** ${URL} https://opensource-demo.orangehrmlive.com/web/index.php/auth ...

The JSONP request connected to the user input field and button will only trigger a single time

Hello all, I'm new to this and have been searching the internet high and low to see if anyone has encountered a similar issue before, but haven't had any luck. I've been attempting to set up a JSONP request to Wikipedia that is connected to ...

Tips for using the .innerHTML method to reference multiple indexes

How can I set the .innerHTML for multiple indexes of an array? I currently have an array let poles = Array.prototype.slice.call(document.querySelectorAll(".pole")); This array contains 9 empty divs, such as : <div class="pole" id="1"></div> ...

The functionality of Everyauth seems to be malfunctioning in the latest version of Express

Currently, I am utilizing nodejs, express 4, and everyauth for social network authentication. I have encountered an issue where upon clicking Accept from Google and getting redirected back to my /, an error message appears: _http_outgoing.js:335 throw ne ...

Utilizing i18n for moment formatting techniques

As I switch from moment to date-fns, I came across this code that involves moment, which I don't quite understand. However, I do comprehend the map function. this.events[0].dates.map(date => moment(date).format(this.$i18n.locale)) I set up an e ...

Contrast in executing an Arrow Function versus a regular Function when passing them as Props in REACTJS

Here is the component code: <SelectCampanha titulo="Preenchimento obrigatório" listaOpcoes={ category ? riskModel.filter((item) => item.CategoryType.includes(categoria) ...

Using DefaultSeo does not override NextSeo in every component

I am looking to dynamically change the Head tag using next-seo. While browser validation will show NEXTSeo for individual pages, Twitter, Firebase's card validation tool, and others will default to next-seo-config.js. Does anyone have a solution? S ...

An individual in a chat App's UserList experiencing issues with incorrect CSS values. Utilizing Jquery and socketio to troubleshoot the problem

Currently, I am testing a new feature in a chat application that includes displaying a user list for those who have joined the chat. The challenge is to change the color of a specific user's name on the list when they get disconnected or leave the cha ...

Vanilla JavaScript error: Unable to access property

I am working on implementing a header with a logo and navigation that includes a menu toggle link for smaller viewports. My goal is to achieve this using Vanilla JS instead of jQuery. However, when I click on the menu toggle link, I encounter the followin ...

My script is unable to access the session variable

Two $_SESSION variables seem to be inaccessible in any script on my page, yet I can confirm their existence in the PHP code of the same page by using echo to display their values. When trying to display these $_SESSION variables in jQuery using the code b ...

Having trouble with closing the window on Mozilla and Chrome? Keep getting a scripting error message?

To close this window, click <a onclick="self.close();" href="#">here</a>. This is the code I am using. When I submit in Chrome and Mozilla, an error occurs. The error message is: "Script must not be allowed to close a window that was not o ...