Puppeteer: click an item with text

Is there any method (not found in the API) or solution to click an element with text?

For example, I have html:

<div class="elements"> <button>Button text</button> <a href=#>Href text</a> <div>Div text</div> </div> 

And I want to click an element in which the text is wrapped (click the button inside.), For example:

 Page.click('Button text', '.elements') 

Any solution?

+39
puppeteer
source share
8 answers

You can use the XPath selector with the page. $ X (expression) :

 const linkHandlers = await page.$x("//a[contains(text(), 'Some text')]"); if (linkHandlers.length > 0) { await linkHandlers[0].click(); } else { throw new Error("Link not found"); } 

Check out clickByText for a complete example. It takes care of escaping quotes, which is a bit complicated with XPath expressions.

+53
source share

You can also use page.evaluate() to click on elements derived from document.querySelectorAll() that have been filtered by text content:

 await page.evaluate(() => { [...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click(); }); 

Alternatively, you can use page.evaluate() to click an element based on its textual content using document.evaluate() and the corresponding XPath expression:

 await page.evaluate(() => { const xpath = '//*[@class="elements"]//button[contains(text(), "Button text")]'; const result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null); result.iterateNext().click(); }); 
+4
source share

The current top answer for tokland only works on text nodes, and not on nodes with other elements inside.

Short answer

This XPath expression will request a button that contains the text "Button Text":

 const [button] = await page.$x("//button[contains(., 'Button text')]"); if (button) { await button.click(); } 

To also comply with the <div class="elements"> surrounding buttons, use the following code:

 const [button] = await page.$x("//div[@class='elements']/button[contains(., 'Button text')]"); 

explanation

To explain why using a text node ( text() ) is incorrect in some cases, let's look at an example:

 <div> <button>Start End</button> <button>Start <em>Middle</em> End</button> </div> 

First, let's check the results if it contains(text(), 'Text') :

  • //button[contains(text(), 'Start')] will return both two nodes (as expected)
  • //button[contains(text(), 'End')] will return only one node (first), since text() returns a list with two texts ( Start and End ), but contains will only check the first
  • //button[contains(text(), 'Middle')] will not return any results, as text() does not include the text of child nodes

Here are the XPath expressions for contains(., 'Text') that work with the element itself, including its child nodes:

  • //button[contains(., 'Start')] will return both two buttons
  • //button[contains(., 'End')] will again return both two buttons
  • //button[contains(., 'Middle')] will return one (last button)

So in most cases it makes sense to use . instead of text() in an XPath expression.

+4
source share

made a quick decision to be able to use advanced CSS selectors, such as ": contains (text)"

so with this library you can just

 const select = require ('puppeteer-select'); const element = await select(page).getElement('button:contains(Button text)'); await element.click() 
+3
source share

Here is my solution:

 let selector = 'a'; await page.$$eval(selector, anchors => { anchors.map(anchor => { if(anchor.textContent == 'target text') { anchor.click(); return } }) }); 
+2
source share

Decision

 (await page.$$eval(selector, a => a .filter(a => a.textContent === 'target text') ))[0].click() 
+2
source share

First, a simple way is to add an identifier to the button to make sure that you press the correct button.

In this case, I will use two puppeteer functions and a condition:

 let my_button = await page.$eval(".elements > button", btn => btn.textContent); if(my_button == "Button text") { await page.click(".elements > button"); } 

Just check if there are any more divs with the same ".elements" class, in this case use a different and special selector. I am sure there are cleaner solutions, but I am also new to the puppeteer.

0
source share

You do not need to specify text to click it, the page.click () action will search for the selector and click in the middle. If you cannot contact this class, try right-clicking on the item in the developer tools, and "Copy> Copy Selector" should work.

In this case

 await page.click('.elements'); 

Should be enough. If this does not work, add the 'button' parameter. This helps me in rare cases:

 await page.click('.elements', {button: 'left'}); 
0
source share

All Articles