1

I want to be able to mouse over a page, have it highlight the elements underneath the cursor (in the same way as the developer tools inspector), and when I click on the mouse obtain the XPath for the element. Enabling the element inspector and receiving the XPath to be done via code, and element selection by a human.

I can enable the devtools inspector behaviour by using Chrome Devtools Protocol and using the Overlay.setInspectMethod with inspectNode=searchForNode.

Upon a mouse click I then receive a DOM.BackendNodeId of the clicked element from the Overlay.inspectNodeRequested event

The devtools protocol does not seem to have any built in way to then obtain the XPath. So I found this javascript implementation of the Chrome Developer Tools Copy->Xpath functionality.

Whilst I can evaluate the javascript via CDT Protocol, after many hours of attempts, I cannot work out how to obtain a reference to the Node to execute the javascript function when all I have is the BackendNodeId.

Is there a way to get a reference in javascript to the node, or is there a way to get the nodes XPath in CDTProtocol ?

OrdinaryOrange
  • 2,420
  • 1
  • 16
  • 25
  • Use DOM.describeNode to traverse the DOM hierarchy and build the xpath as you go up. – wOxxOm Jul 10 '20 at 09:12
  • Did think of that, seemed like a terribly inefficient way to tackle the problem. Was hoping for a cleaner solution. – OrdinaryOrange Jul 10 '20 at 21:51
  • I don't understand what's inefficient. You would need to walk the hierarchy anyway so the only added step is the command call. You can use promisify the call and `await` it for cleaner code. – wOxxOm Jul 11 '20 at 04:52
  • I'm making the assumption that traversing the DOM via the CDT protocol (which is via a socket connection to the browser) would be slower that traversing the DOM directly via JS in the browser. I have not evaluated the perf difference and are just going on assumption. Were you thinking of using DOM.describeNode another way ? – OrdinaryOrange Jul 11 '20 at 06:33
  • There's only one way to use DOM.describeNode AFAICT. Indeed, if you need to make like 100 calls then you'll probably see a difference I guess, but normally it shouldn't be a concern. It's also safe, unlike running in JS context, where a site may redefine/extend some of the native types/prototypes. – wOxxOm Jul 11 '20 at 06:39
  • Thanks for your comments, I'll fall back to a DOM.describeNode approach if I get no hits on this question for an alternative method. – OrdinaryOrange Jul 11 '20 at 06:44

1 Answers1

1

Found the proper solution

First I needed to obtain a reference to the Javascript object via

DOM.resolveNode, {backendNodeId: nId}

Then extract the objectId from the returned JSON object. Which will look something like returned JSON object from resolveNode call

Then I can call whatever function I like, with the element as context

Runtime.callFunctionOn, {functionDeclaration: "function(){getXPathForElement(this)}", objectId: "{"injectedScriptId":3,"id":3}"

OrdinaryOrange
  • 2,420
  • 1
  • 16
  • 25
  • Great finding. How did you get `Runtime.callFunctionOn` working? If I run your code snipet on my objectId I got a `ReferenceError: getXPathForElement is not defined↵…` How did you declare `getXPathForElement` in the Runtime context? – xavierraffin May 26 '21 at 19:44
  • For my testing I used the getXpathForElement function from from https://gist.github.com/yakimchuk/49d3c595b07c438585ccecffde3c561d. I then use the EvaluateExpression function of the Puppeteer page object to load it into the runtime ctx. HTH – OrdinaryOrange May 28 '21 at 00:15