Puppeteer - click on span with specified text

Viewed 1626

I'am currently web scraping one page with puppeteer. I am trying to make something which can speed up my work.

My HTML Markup looks something like this

<div class="inner-wrapper" style="">
<div class="style-001">
    <ul role="listbox" aria-multiselectable="true" class="style-002">
    <li role="option" aria-selected="false" class="style-003">
        <span class="style-004">First Option</span>
    </li>
    <li role="option" aria-selected="false" class="style-003">
        <span class="style-004">Second Option</span>
    </li>
    <li role="option" aria-selected="false" class="style-003">
        <span class="style-004">Third Option</span>
    </li>      
    </ul>
</div>

I was trying to select specific 'li' based on span's text. This page is dynamic so sometimes 'Third Option' will have index [3] and sometimes other one. It have to be selected just by span's text because It won't work in other situations. I can't use class because this website uses same classes for every 'li'.

I did research on google and stackoverflow and didn't find working solution. I tried selecting by xpath, js selector and more. And even I tried something like this:

const [link] = await page.$x('//div[2]/div/div/section/div[2]/div/div/div/ul/li[2]/span[contains(text(), "First Option")]'); 
await link.click();

or

let selectOption = await page.waitForXPath(`//div[2]/div/div/section/div[2]/div/div/div/ul/li[contains(text(),'First Option')]`);
await selectOption.click();

If someone can help me, I will huge appreciate.

@EDIT If it is important clicking on any option this website changes aria-selected="false" to "true". Maybe it will help in any way?

1 Answers

You can try and use page.evaluate and than use querySelectorAll and filter the li by text than use a forEach to click on the il with the specific text.

await page.evaluate(() => {
  Array.from(document.querySelectorAll('li')).filter(li => {
    return li.innerText == 'First Option' // filter il for specific text
  }).forEach(element => {
    if (element) element.click(); // click on il with specific text
  });
});

If the first one does not work you can try.

await page.evaluate(() => {
  Array.from(document.querySelectorAll('div > div > ul > li')).filter(li => {
    return li.innerText == 'First Option' // filter il for specific text
  }).forEach(element => {
    if (element) element.click(); // click on il with specific text
  });
});

You could also give this a try and i think this would be the best way todo it.

await page.evaluate(() => {
  const elements = [...document.querySelectorAll('div > div > ul > li')];
  const targetElement = elements.find(e => e.innerText == 'First Option');
  if (targetElement) targetElement.click();
});

or

await page.evaluate(() => {
  const elements = [...document.querySelectorAll('li')];
  const targetElement = elements.find(e => e.innerText == 'First Option');
  if (targetElement) targetElement.click();
});
Related