0

In the puppeteer api docs an ElementHandle is defined:

ElementHandle represents an in-page DOM element. ElementHandles can be created with the page.$ method.

So given an ElementHandle I should be able to do all the basic functions you can do with the page: .click, .$$, .$x, .hover, etc

The same doc defines page.$x(expression)

expression <string> expression to evaluate (Xpath Expression)
returns: <Promise<Array<ElementHandle>>>

So I have the following that returns an array of ElementHandles:

const cameraRowList = await page.$x("//div[contains(@class, 'camera-row')]");

I actually get 5 items in the array as expected. So I need to loop through this list and check the ElementHandles for an svg object that has the id='locationChecked'. Then click it if it does. (all but the 4th one has this id currently)

for (const cameraRow of cameraRowList) {
   const [cameraHasLocation] = await cameraRow.$x("//div[@class='hasLocation']//*[@id='locationChecked']");
   if (cameraHasLocation) {
      const [cameraSelectBox] = await cameraRow.$x("//div[contains(@class, 'checkbox')]");
      await cameraSelectBox.click();
   }
}

The problem is two fold.

First: When it loops through the 5 ElementHandles in the Array, it always finds/evaluates the 'locationChecked' object, even when on the 4th it is not there.

Second: when it executes the cameraSelectionBox.click(); It always click the first one.

This tells me that it's probably ignoring the scope of the ElementHandle and using the entire page instead. and then executing on the first one it finds for each pass of the array.

What can i do to ensure the scope stays within the ElementHandle being used?

Jeuke
  • 133
  • 1
  • 10
  • Did you try using `document.evaluate` on the browser and see what you get there. It's what $x is using. – hardkoded Jan 20 '20 at 11:50
  • within the chrome console? Not sure the syntax to use. I've never used document.evaluate. – Jeuke Jan 20 '20 at 16:32
  • Do you have some public HTML to play around? – hardkoded Jan 20 '20 at 16:36
  • I don't. I was able to get the syntax to work. document.evaluate("//div[@class='statsRow']//div[@class='hasLocationIcon']//*[@id='locationChecked']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; does return the first item in the list – Jeuke Jan 20 '20 at 16:50
  • What happens if you pass the second `cameraRow` as the third parameter there? do you get the locationChecked of the second row? – hardkoded Jan 20 '20 at 17:49
  • Oh, I put the above in the console. Did you want me to put it in code? – Jeuke Jan 20 '20 at 19:37
  • document.evaluate is not available in puppeteer. evaluate a function built into puppeteer, and it expects very different parameters. – Jeuke Jan 20 '20 at 20:00
  • Does this answer your question? [limit scope to an array of elementHandles (for selection/click/etc)](https://stackoverflow.com/questions/59848474/limit-scope-to-an-array-of-elementhandles-for-selection-click-etc) – hardkoded Jan 21 '20 at 20:14

1 Answers1

0

I figured it out. The problem lies in my xpath.

const [cameraHasLocation] = await cameraRow.$x("//div[@class='hasLocation']//*[@id='locationChecked']");

I should have made the search relative "./" I was using Global "//" which ignores the elementHandle scope:

const [cameraHasLocation] = await cameraRow.$x("./div[@class='hasLocation']//*[@id='locationChecked']");
Jeuke
  • 133
  • 1
  • 10