3

I would like to get elements query selector by passing x,y coordinates.

Or maybe few elements that satisfy the coordinates.

Maybe I can do mouse.click(x, y) and then get clicked element but I don't want page to take any actions just get the query of the element.

Is there possibility to do that?

ROKIKOKI
  • 571
  • 2
  • 9
  • 24
  • Possible duplicate of [Retrieve the position (X,Y) of an HTML element](https://stackoverflow.com/questions/442404/retrieve-the-position-x-y-of-an-html-element) – Seblor Feb 20 '19 at 13:30

2 Answers2

6

If you just need a topmost element by viewport coordinates, you can try DocumentOrShadowRoot.elementFromPoint(). If you need to compose a unique selector, you can try to process element array from the DocumentOrShadowRoot.elementsFromPoint() (add more checks to the example below — classes, ids, attributes, children count and order etc.):

'use strict';

const puppeteer = require('puppeteer');

(async function main() {
  try {
    const browser = await puppeteer.launch();
    const [page] = await browser.pages();

    await page.goto('https://example.org/');

    console.log(await page.evaluate(() => {
      return document.elementFromPoint(100, 100).tagName;
    }));

    console.log(await page.evaluate(() => {
      return document.elementsFromPoint(100, 100)
                     .map(({ tagName }) => tagName).reverse().join(' > ');
    }));

    await browser.close();
  } catch (err) {
    console.error(err);
  }
})();
DIV
HTML > BODY > DIV
vsemozhebuty
  • 12,992
  • 1
  • 26
  • 26
  • Any possibility to get something like this?: #productInfo > div > div > table > tbody > tr:nth-child(1) – ROKIKOKI Feb 21 '19 at 09:39
  • I do not know any automatic way to do this. You can extend the function in `.map()` to add some checks for id. class names and siblings with the same tag name and amplify the returned string with results. – vsemozhebuty Feb 21 '19 at 09:44
  • [I've asked in the protocol repository](https://github.com/ChromeDevTools/devtools-protocol/issues/158). In [an old mailing group question](https://groups.google.com/d/topic/chrome-debugging-protocol/ZhmzuBNnZoQ/discussion) it is suggested to reimplement [`Elements.DOMPath.cssPath` function](https://github.com/ChromeDevTools/devtools-frontend/blob/f97c852c172db4904c2a0b2c4cef93f2ebe5068f/front_end/elements/DOMPath.js#L23) from the devtolls code. – vsemozhebuty Feb 21 '19 at 10:26
2

You have to consider first that you might get many elements on the same position, including the main HTML element.

First, you need to get all the elements:

Array.prototype.slice.call(document.getElementsByTagName("*"))

Then, we now that each element has a function called getClientRects, which will return an array of "boxes".

Based on that, we can filter all the elements having one box in the coordinate you need:

var x = 150;
var y = 1250;
Array.prototype.slice.call(document.getElementsByTagName("*")).filter(e =>
     Array.prototype.slice.call(e.getClientRects()).find(rect => 
         rect.top <= x && rect.bottom >= x && rect.left <= y && rect.right >= y))

You could do something like that in your evaluate call in Puppeteer.

hardkoded
  • 18,915
  • 3
  • 52
  • 64