Explain Codes LogoExplain Codes Logo

How do you click on an element with text in Puppeteer?

javascript
xpath
puppeteer
selectors
Nikita BarsukovbyNikita BarsukovΒ·Sep 22, 2024
⚑TLDR

You can exploit XPath along with page.$x() in Puppeteer to identify and click any element through its text. Here’s how you can have it done for a 'Example Text' linked element:

const [link] = await page.$x("//a[contains(text(), 'Example Text')]"); if (link) await link.click(); // This way, you won't even have to buy it a drink first! πŸ˜‰

It spots an <a> element having 'Example Text'. You can have the XPath modified as per different elements or text.

Dealing with sophisticated XPath

A more particular XPath comes to your rescue when you encounter difficult situations such as buttons or elements under divs. Example:

const [button] = await page.$x("//div[@class='elements']/button[contains(., 'Button Text')]"); if (button) await button.click(); // Yep, we're coming for you, 'Button Text'. Run, but you can't hide! 😁

In here, we're aiming at a button carrying 'Button Text' within a div classed 'elements'. Usage of . in place of text() brings in all child node text, providing a playfield for a robust search.

Befriend whitespace and control case sensitivity

Thanks to XPath for normalize-space() and translate() functions, it can work around whitespace and case sensitivity ensuring your text conditions are adaptive:

const [button] = await page.$x("//button[contains(translate(normalize-space(.), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'button text')]"); if (button) await button.click(); // Playing hide and seek with cases and whitespaces, are we? Not on my watch! πŸ•ΆοΈ

This chunk of code will trigger a button click regardless of case sensitivity or whitespace.

Ditch XPath with pre-text selectors

Puppeteer 18.0.0 introduced text-prefixed selectors for those who are not fans of XPath:

await page.click('text/Button text');

It’s way simpler than XPath approach and works amazingly when the text is non-nested.

Manage dynamic content

In terms of active web apps, you might face situations where elements are just not available on time. With Puppeteer, you don't need to worry! Use waitForXPath() and waitForFunction():

await page.waitForXPath("//button[contains(text(), 'Load More')]"); const [loadMoreButton] = await page.$x("//button[contains(text(), 'Load More')]"); if (loadMoreButton) await loadMoreButton.click(); // Patience is a virtue, they say. Especially when waiting for the button to 'Load More' patience! πŸ˜†

Or wait for the matching text:

await page.waitForFunction( text => document.body.innerText.includes(text), {}, 'Button Text' ); await page.click('button:contains("Button Text")');

Quoting game: Advanced level

When aiming for text with quotes, don't forget to escape them properly:

const [button] = await page.$x("//button[contains(text(), \"Bob's Burgers\")]"); if (button) await button.click(); // Bob might not give out his Burgers that easily, but we got our ways! 😎

Leverage CSS selectors with jQuery

If jQuery is enabled, you can use the :contains() pseudoselector:

await page.click('button:contains("Resume")'); // CSS selectors, jQuery, and 'Resume', a match made in heaven! πŸ˜‡

Safe and sound clicks

There might be times when you'd need to convert NodeList to an array or apply document.evaluate for proper element actions:

const elements = await page.evaluate(() => { const buttons = [...document.querySelectorAll('button')]; return buttons.map(button => ({ text: button.textContent, node: button })); }); const targetButton = elements.find(button => button.text.includes('Resume')); if (targetButton) await targetButton.node.click(); // Go find, NodeList to Array. Armed and ready! πŸ›‘οΈ

Precise element interaction took place with NodeList turning into arrays, giving access to array methods.

Facts & Hazards

Know that XPath is case-sensitive and might be fragile when HTML structures change. Also, different Puppeteer versions may have different behaviors and syntax requirements, so always keep an eye on the current documentation.

Additional tips for the smart coder

  • Use result.iterateNext().click() when you want XPath to step through possible matches.
  • Bring aboard puppeteer-select for an extra set of CSS selector capabilities, emulating jQuery's :contains CSS extension.
  • Add parent class to selectors for container-specificity to avoid ambiguous clicks on repeating text.