2

I need to select elements with ids that contain square brackets.

i.e.

#element[0]

However, I keep getting:

Error: failed to find element matching selector "element[0]"

I've escaped the element in the selector with \, but that doesn't work.

page.select("#fruit\[0\]", "apples")

Double backslash escapes don't work either. i.e.:

page.select("#fruit\\[0\\]", "apples")

UPDATE: The element I'm trying to select:

<select id="fruit[0]">
  <option>Please make a selection</option>
  <option>apples</option>
</select>

NOTE: I get the same error even when I try to use the page.waitFor method with the query above.

Hyyan Abo Fakher
  • 3,497
  • 3
  • 21
  • 35
kashiB
  • 207
  • 4
  • 14

3 Answers3

6

Use [id="fruit[0]"]:

You can use the CSS attribute selector in reference to the element's id attribute:

await page.waitFor( '[id="fruit[0]"]' );

await page.select( '[id="fruit[0]"]', 'apples' );
Community
  • 1
  • 1
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
2

Escaping using encoded data

If you even try to do document.querySelector('#fruit[0]') on your browser, you will get same error. The escape doesn't work because by the time puppeteer reads it, it is already parsed and doesn't have the same escaped value. Few ways to escape them.

Say we have an element like this,

<a href="/" id="sample[112]">Bar</a>

Now if you want to use vanila JS, you can try the following,

enter image description here

Which reads the following:

< document.querySelector(CSS.escape('#sample[112]'))
> null

< document.querySelector('#sample[112]')
> Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#sample[112]' is not a valid selector.

< document.querySelector('#sample\5b 112\5d')
> ncaught DOMException: Failed to execute 'querySelector' on 'Document': '#sampleb 112d' is not a valid selector.

< document.querySelector('#sample\\5b 112\\5d')
> <a href="/" id="sample[112]">Bar</a>

As you can see above, the left bracket is 5b and right bracket is 5d, and we even had to escape that and had to put space so browser could parse it.

You should try out above codes and find which one works for your site.

Real case scripts

Let's face it, your target website does not have value for the options. The best way will be to select by text, shamelessly copying from this answer.

Also, I could escape it twice and get data without encoding them. After all, nodeJS is different than the browsers console, and everything will act differently when on a script.

Source used for HTML,

<select id="sample[1]"><option>mango</option><option>apple</option></select>

And puppeteer code used for testing,

const puppeteer = require("puppeteer");

function setSelectByText(selector, text) {
  // Loop through sequentially//
  const ele = document.querySelector(selector);
  console.log(ele);

  for (let ii = 0; ii < ele.length; ii++) {
    if (ele.options[ii].text == text) {
      // Found!
      ele.options[ii].selected = true;
      return true;
    }
  }
  return false;
}

puppeteer.launch().then(async browser => {
  console.log("Opening browser");
  const page = await browser.newPage();

  console.log("Going to site");
  await page.goto("http://0.0.0.0:8080"); // temporary website created for testing

  await page.evaluate(setSelectByText, "#sample\\[1\\]", "apple"); // we can use this over and over, without creating a separate function, just passing them as arguments

  await page.screenshot({ path: "test.png" });

  await browser.close();
});

enter image description here

Md. Abu Taher
  • 17,395
  • 5
  • 49
  • 73
  • I just tried this. No luck :(. I get the exact same error: (node:4995) UnhandledPromiseRejectionWarning: Error: Evaluation failed: DOMException: Failed to execute 'querySelector' on 'Document': '#fruit[0]' is not a valid selector. at __puppeteer_evaluation_script__:1:33 – kashiB Sep 10 '18 at 06:30
  • Ah wait, I rememberd the issue, let me get back with a solid reply soon. – Md. Abu Taher Sep 10 '18 at 06:31
  • @kashiB Now I added more answer. :) Peace. – Md. Abu Taher Sep 10 '18 at 06:43
  • I appreciate the effort Md :) Sadly, none of the suggestions worked. `Error: failed to find element matching selector "#fruits\5b 0\5d` I'm going to stick with Selenium for now as I have my script humming there. I was looking at Puppeteer because Google Cloud Functions now support headless Chrome with Puppeteer on the Node runtime environment. I think this has convinced me to just stick to a Java compute engine instance for the time being. – kashiB Sep 10 '18 at 06:51
  • I had same problem, that's why I suggested these. I gave the solution from an actual app. :) Maybe there is something small missing. – Md. Abu Taher Sep 10 '18 at 06:53
  • @kashiB tried final time to help, check and try again. What else can be said. – Md. Abu Taher Sep 10 '18 at 07:22
  • 2
    The section about backticks is misleading: `"#fruit[0]"` is as effective as the backticks version, and if you’re worried about weird & uncommon selectors, the latter will break when you try to match `input[value="${foo}"]`. Regarding matching by text, given that we’re talking about Puppeteer, it would be more efficient and reliable to [use `$x` with a `contains` clause](https://stackoverflow.com/a/48131384/8492116) than to roll your own function. – Aankhen Sep 10 '18 at 10:09
  • @Aankhen you are totally on spot. Can we use xpath to select options by text? :) If yes, please create an answer so I can upvote it :D – Md. Abu Taher Sep 10 '18 at 17:48
  • Yup, you can (see the link in my previous comment), but I don’t think that’s the issue here, heh. – Aankhen Sep 10 '18 at 17:59
  • @Aankhen Thanks. `$x` works for waitFor, but I'm still stuck trying to **set** a select element's value. – kashiB Sep 11 '18 at 15:42
  • Ah, now I understand. Let me write that up. – Aankhen Sep 11 '18 at 16:18
  • Wow, are you still stuck? Ah, need to try this on GCP myself it seems. – Md. Abu Taher Sep 11 '18 at 16:19
  • 2
    See [Grant Miller’s answer](https://stackoverflow.com/a/52263050/8492116). – Aankhen Sep 11 '18 at 16:27
1

You may be referencing the ID incorrectly.

If you're programmatically generating the IDs when creating the HTML elements (i.e. fruit[0], fruit[1]), then what's being stored is the value and not the reference.

Example: I have an arrayfruit = [apple, banana] and I create an element... <button id=fruit[0]>Click me</button> If I want to select that button then I need page.select('#apple')instead of page.select('#fruit[0]')

Taylor Krusen
  • 963
  • 6
  • 10
  • Nevermind. I see you're using quotes in the example so it should store the string exactly as how you're setting it. – Taylor Krusen Sep 10 '18 at 06:36
  • Perhaps. But why is this also failing: `await page.waitFor('#answerlist[0]')` – kashiB Sep 10 '18 at 06:36
  • Note that this depends on how you’re creating the element. If I say `document.body.innerHTML = ""`, the ID will be the literal string `fruit[0]`, not the first element of the `fruit` array, and the correct selector will be `#fruit\[0\]`. – Aankhen Sep 10 '18 at 10:02