Puppeteer: Navigating Through Descendants to Click on Elements with Specific Values

Recently I started using Puppeteer and encountered an issue while trying to click on a specific element. The code snippet in question is:

await page.waitForSelector('.item-table > .grid-item > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');
await page.click('.item-table > .grid-item > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');

Given that there are multiple .item-table elements on the page, I am looking for a way to click on one that contains a specific text within its descendants, without knowing the exact level of the descendant. However, my attempts to solve this puzzle by adding > :contains("Foo bar") have been unsuccessful so far.

await page.waitForSelector('.item-table > :contains("Foo Bar") > .grid-item > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');
await page.click('.item-table > :contains("Foo Bar") > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');

If you have any insights or solutions on how to achieve this with Puppeteer, I would greatly appreciate it!

UPDATE: Below is the HTML structure I'm trying to scrape:

<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table">
    <div class="grid-item">
        <div class="grid-item-container">
            <div class="grid-table-container>
                <div class="grid-option-header">
                    <div class="grid-option-caption">
                        <div class="grid-option-name">
                            Foo Bar
                            <span>some other text</span>
                        </div>
                    </div>
                </div>
                <div class="grid-option-table">
                    <div class="grid-option">
                        <div class="grid-option-selectable">
                            <div></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="item-table"></div>
<div class="item-table"></div>

To clarify, I need to click on a div within the grid-option-selectable div, located inside an item-table div that includes the text "Foo Bar" among its descendants.

Answer №1

I will be making a small tweak to your markup in order to ensure that the code is functioning properly:

<!-- ... same ... -->
<div class="grid-option-selectable">
    <div>You caught me!</div>
</div>
<!-- ... same ... -->

It might be beneficial to add some similar-structured elements with different text content to validate that we are not encountering false positives.

The technique we are going to employ is outlined in this response (this answer within the same discussion offers alternatives and this separate conversation is relevant): using xpath to query the common parent between the target text and clickable element based on descendant text, then searching for the clickable element within that parent.

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch(/*{dumpio: true}*/);
  const page = await browser.newPage();
  await page.goto("http://localhost:8000");
  const xp = `
    //div[@class="item-table" 
          and descendant::*[contains(text(), "Foo Bar")]]
    //div[@class="grid-option-selectable"]
  `;
  const [el] = await page.$x(xp);
  console.log(await el.evaluate(el => el.innerText));
  await el.click();
  await browser.close();
})();

Output:

You caught me!

Please note that I am utilizing an exact class match, but in case your target element has multiple classes, you may need to adjust your query using contains. In that scenario, your xpath expression would look like this:

//div[contains(@class, "item-table")
      and .//*[contains(text(), "Foo Bar")]]
//div[contains(@class, "grid-option-selectable")]

Since the posting of this answer, page.$x has been deprecated from the library. Refer to How can I get an element by xpath? for more information.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Incorporating Vuetify into a Vue CLI application with the help of webpack

I am facing an issue with my Vue CLI application that uses Webpack and Vuetify. The Vuetify components are not loading properly, and I keep getting the following warnings: Unknown custom element: < v-app > - did you register the component correctly? ...

Exporting JavaScript formatting in Netbeans is a breeze

Does anyone know how to preserve the formatting for JavaScript in Netbeans 8.1 when exporting? After clicking on the export button and expanding Formatting, I couldn't find any option specifically for JavaScript. I've thought about locating the ...

What steps are involved in creating a default toolbar for a material picker in a React application?

Looking for assistance with customizing the toolbar for material picker (v3) by adding a title inside the dialog. I successfully implemented this in the KeyboardDatePicker following a helpful thread (https://github.com/mui-org/material-ui-pickers/issues/11 ...

Uncovering the Android tablet browser: A guide

I have implemented the code below to detect mobile/tablet browsers. function isMobile() { var index = navigator.appVersion.indexOf("Mobile"); return (index > -1); } Although it works well for desktop browsers and iOS devices (iPhon ...

Issue: "TypeError: Unable to retrieve dynamically imported module" encountered while utilizing dynamic imports in Remix application

I am currently facing an issue with dynamic imports in my Remix application. Whenever I try to dynamically import a module using the code below: const module = await import(../lib/lang/lang-${language.toLowerCase()}.js); An error occurs: TypeError: Fail ...

JS form validation malfunctioning

XHTML <form name="suggestion" method="post" action="suggestion.php" class="elegant-aero" onSubmit="return validate()" > <label> <span>Message :</span> <textarea id="Message" name="m ...

What is causing the backslash character to be removed from my ajax request?

When using Ajax to call a rest endpoint, the request takes two parameters: user and permission. $.ajax({ type: 'GET', cache: false, url: "/app/Rest/4.0/UserManagement/AddPermissionToUser", data: { username: encodeURI(user ...

PHP: Avoiding duplicate JavaScript imports

Hey there, I'm running into an issue with incorporating JavaScript, specifically the Google Maps API. Let's say I have a page that includes the library like this: <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=pla ...

An error occurred: an unexpected identifier was found when trying to import axios using './lib/axios.js' and require('axios')

I am currently working with a js file: test.js: const axios = require('axios'); console.log('test'); To set up the dependencies, I ran the command npm install This is how my folder structure is organized: test node_modules packa ...

Aligning divs, prevent duplicate HTML within multiple divs

Currently, I am attempting to troubleshoot a jsfiddle issue. The main problem lies in my desire for the first two divs (with class="fbox") to be aligned next to each other on the same level. Furthermore, I am looking to enable dragging the image into the ...

Why is my md-button in Angular Material displaying as full-width?

Just a quick question, I created a simple page to experiment with Angular Material. On medium-sized devices, there is a button that toggles the side navigation, but for some reason, it expands to full width. There's no CSS or Material directives causi ...

Guide to integrating animations into a React JS array sorting program

I am currently working on a project that utilizes react js to visually sort an array. I have successfully implemented the sorting algorithm, and the website now displays the correctly sorted array. However, I would like to incorporate an animation to show ...

Problem with Chrome Compatibility for Bootstrap Carousel

My webpage features a carousel implemented as a slider. It seems to be working fine in Firefox and mobile browsers, except for Chrome where it doesn't show up at all. I can't seem to figure out what's causing the issue. Here is the syntax I& ...

What is the way to create the appearance of a hand or arrow hovering over my "X" while I am closing out my Div using jQuery?

Hey there, I've got a handle on closing the div using a "p" tag in jQuery. The code snippet for that is provided below. However, I'm looking to change the cursor over the "X" to an arrow or hand symbol, similar to what you'd see with an "a" ...

Guide to creating a secure login page with the help of cookies

I'm in need of a quick guide on how to implement a login page for a website using cookies. Every user has a unique username and password. Is it necessary to store both the username and password in the cookies, or is just the username sufficient? Is ...

Can an internal/private function call a public function?

Just wondering if I'm missing something here, as I tried the following: (function() { var thing = function() { var doIt = function() { console.log("just do it"); this.updateValue(5); }; return { ...

"Exploring the Method of Inheriting from a BaseComponent in Angular 2

I’ve been working on an Angular project and I’m looking to streamline the creation of components with shared functionality. For example, let’s say I have a TeacherIndexComponent: @Component({ selector: 'app-teacher-index', templateUrl: ...

Is it possible to selectively export certain interfaces within a .d.ts file?

// configuration.d.ts export interface Configuration { MENU_STRUCTURE: Node[]; } interface Node { name: string; } Looking at the snippet above, I am aiming to only export Configuration. However, I noticed that I can also import Node from an ext ...

Top solution for efficiently capturing and storing user input in a React JS application: Event Handler

I've recently designed an input field for inputting details of items. In order to effectively capture and save the entered information, which of the following event handlers would be most suitable? onClick onChange onLoad onKeyPress ...

Navigating the realm of JavaScript and managing the uncertainties of floating point precision

I am exploring the development of a browser multiplayer game utilizing rollback netcode that operates a deterministic simulation on the clients. I successfully prototyped the netcode in Flash, but encountered a floating point roadblock along the way. As f ...