How to click a link with a specific text with Puppeteer

By

Learn how to click a link or button by its text in Puppeteer using page.$x with an XPath contains() selector, handy for a cookie Accept all button.

~~~

To click a link with a specific text in Puppeteer, use an XPath expression that matches on the element’s text content. I wanted to click an “Accept all” cookie button, and this is the code I used:

const [linkcookie] = await page.$x("//a[contains(., 'Accept all')]")
if (linkcookie) {
  await linkcookie.click()
}

Why XPath? CSS selectors can target ids, classes, and attributes, but they can’t select an element by its text. XPath can, and page.$x() runs an XPath query against the page.

Let’s decode the expression. //a finds all a elements anywhere in the document. The [contains(., 'Accept all')] part filters them, keeping only the ones whose text contains “Accept all”. The dot means “the text of this element, including nested children”, so it still matches when the label is wrapped in a span inside the link.

page.$x() returns an array of all matching elements. Destructuring with const [linkcookie] = ... grabs the first one. If nothing matched, the array is empty and linkcookie is undefined, which is why the if check matters. Calling .click() on undefined would crash the script.

Note that if the button is a button HTML element (it depends on the HTML markup used), you have to use

page.$x("//button[contains(., 'Accept all')]")

instead 👍

Open the page in your browser DevTools and inspect the element to see which tag the site uses.

The pitfall: the banner appears late

Cookie banners are often injected by a script after the initial page load. If you run page.$x() too early, it finds nothing, even though the button shows up a moment later.

The fix is to wait for the element before querying it:

await page.waitForXPath("//a[contains(., 'Accept all')]")
const [linkcookie] = await page.$x("//a[contains(., 'Accept all')]")
await linkcookie.click()

waitForXPath() polls the page until the element exists, or fails with a timeout error if it never appears.

One more thing: the match is case sensitive. 'Accept all' will not match a button labeled “Accept All”. Check the exact text in the page markup before blaming the code.

Also see my full Puppeteer tutorial

Tagged: Node.js · All topics
~~~

Related posts about node: