Interact with visible elements by automating mouse clicks with puppeteer

When attempting to click on various elements within a page, my goal is to do so only if they are visible. While achieving this in selenium with the is_displayed method was simple, I have struggled to find a similar approach in puppeteer. I attempted to implement something along these lines:

try {
    await page
      .waitForSelector(id, visible=true, timeout=0)
      .then(() => {
        element.click()
      });
...

Unfortunately, this approach does not work as expected with simple elements like :

<a class="cookie-close" href="#">
OK
</a>

Additionally, I have been unable to determine how to achieve this using the element.click method in puppeteer.

Answer №1

Answer Concisely

const element = await page.waitForSelector('a.cookie-close', { visible: true });
await element.click();

In this code snippet, the page.waitForSelector function is utilized to choose a visible element with the selector a.cookie-close. Once the selector is identified, elementHandle.click is employed to trigger a click event on it.

Detailed Clarification

Among the available functions in Puppeteer, only page.waitForSelector and page.waitForXPath come equipped with an option that validates the presence and visibility of an element. Upon invocation, Puppeteer verifies if the style attribute visibility isn't set to hidden and confirms the existence of a visible bounding box for the specified element.

Ensuring Non-Emptiness of Element

In scenarios where a visibly present element might be devoid of content (e.g., <span></span>), you can extend your query as illustrated below:

const element = await page.waitForSelector('SELECTOR:not(:empty)', { visible: true });

This revised approach incorporates pseudo selectors like :empty and :not to confirm the inclusion of child nodes or text within the targeted element. For situations demanding the selection based on specific textual content inside the element, consider exploring insights shared in this referenced response.

Answer №2

Just like Selenium, it is recommended to utilize Puppeteer's waitForSelector function which can check for the presence and visibility of DOM elements.

try {
  // An error will be thrown if the element is not present or visible.
  await chromePage.waitForSelector("div.hello", {
    visible: true
  });
  await chromePage.click("div.hello");
} catch(err) {
  console.log(err);
}

Answer №3

If you're looking for a more natural way to ensure an element is visible before clicking on it, this code snippet provides a solution. It functions similarly to the explanation given by @joquarky:

click() {
    let retries = 5;
    const hoverAndClick = () => {
        return this._element!.hover() // this._element is ElementHandle
            .then(() => {
                return this._element!.click();
            })
            .catch(err => {
                if (retries <= 0) {
                    throw err;
                }
                retries -= 1;
                sleep(1000).then(hoverAndClick);
            });
    };

    return hoverAndClick();
}

The key difference here is that we allow for multiple attempts in case the element is undergoing a transition or is temporarily hidden by its parent elements.

Answer №5

If you're looking for a different approach, you might want to consider the following code snippet:

// checks if element exists
let elem = await page.evaluate(() => {
  return document.getElementById(id);
});

// use Vanilla JS to verify if element is visible
if (elem && !element.hidden) {
  await page.click(id);
}

Answer №6

Dealing with a similar issue led me to develop the following solution:

await page.waitForSelector('#clickable');
await page.evaluate(() => {
  let el = document.querySelector('#clickable');
  if (isVisible(el)) {
    el.click();
    return true;
  }
  return false;

  function isVisible(el) {
    if (el.offsetParent === null && el !== document.body) {
      return false; // not attached to document
    }
    if (el.offsetWidth <= 0 || el.offsetHeight <= 0) {
      return false; // lacks width or height
    }
    let style = el.style;
    if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
      return false; // affected by hidden CSS attributes
    }
    if (el === document.body) {
      return true;
    }
    return isVisible(el.offsetParent);
  }
});

This approach validates the element's connection to the document, ensuring it has some dimensions and remains visible based on CSS styles display:none, visibility:hidden, or opacity:0.

Please be aware that unconventional hiding methods like large negative margin/padding may impact the effectiveness of this code.

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

What is the best way to use checkboxes to highlight table rows in Jquery Mobile?

I have a Jquery Mobile site with a table. Each table row contains checkboxes in the first cell. I am trying to achieve the following: a) Highlight a row when the user clicks on it b) Highlight a row when the user checks the checkbox I have made progr ...

Encountering a NoClassDefFoundError when working with Selenium: org/openqa/selenium/NoAlertPresentException

Issue with Running Selenium Code from Command Line: I am encountering a NoClassDefFoundError while trying to execute a code snippet in JUnit through Eclipse using Selenium. The code runs smoothly within Eclipse, but when I attempt to share it with my team ...

Transforming date and timezone offset into an isoDate format using moment.js

When retrieving data from the API, I encounter Date, Time, and Offset values in separate columns. My goal is to obtain an ISO date while maintaining the original date and time values. const date = "2019-04-15" const time = "13:45" const ...

React's useState feature is doubling the increment

I have created a basic form management system with a historical feature. A simplified version of this system can be seen on codesandbox import { useState } from "react"; import "./styles.css"; const sample = ["what", "w ...

utilizing various ajax functions

I'm having trouble using additional functions with the "complete:" option in JQuery ajax after the "selectOptionSort" function. Can anyone help me figure out what's wrong? $('#tipos').change(function(){ $("#marcas ...

What are the steps to effectively utilize data retrieved from readFragment using Apollo?

Once a user logs in, the system returns a jwt token and a user object containing the id and firstName, which are then stored in cache (refer to the image link provided below). https://i.stack.imgur.com/oSXZ5.png I aim to retrieve the user information fro ...

issue with deploying a software package and getting it installed

I developed a basic package containing only a simple <div> x </div> and published it using npm publish. When I attempted to install it in a project using npm i, I encountered the following error message: Support for the experimental syntax &apo ...

Using JavaScript to sort data within specific timeframes

How can I extract data based on timestamps greater than 06? "use strict" const data = [ {timestamp: "2020-04-23 05:05", totalAvg: 2.596211180124224}, {timestamp: "2020-04-23 05:10", totalAvg: 3.22052273203436}, {timestamp: "2020-04-23 05:15", t ...

Is it possible to execute a controller function only when the textarea has completely loaded?

My current setup includes a textarea as shown below: <textarea rows="3" maxlength="144" ng-maxlength="144" type="text" name="testPost" id="testPost_{{item.id}}" ng-init="focusText('testPost', item.id)" ng-model=" ...

The value of document.readyState remains as 'complete' even while the page is still actively loading on the frontend

Below is the code I implemented to verify if the page has finished loading: JavascriptExecutor js = (JavascriptExecutor)Driver; pageLoadStatus = js.executeScript("return document.readyState").toString(); Despite still visibly loading, the document.readyS ...

Creating a tooltip with a left arrow and a bordered design

I am looking to create a tooltip that displays its content after clicking on the tooltip tip icon. Currently, it only works on hover, but I want it to be clickable and mobile responsive. Desktop design should resemble: https://i.sstatic.net/FQPyt.png Mob ...

Implementing Javascript to insert IFRAME into the DOM

I'm looking to incorporate an iframe into my webpage. The iframe needs to link to a specific URL. I attempted to add the following code to my HTML, but it's not functioning as expected: document.createElement('<iframe src='http://ex ...

Displaying specific choices depending on the previous selection made

I am facing an issue in Laravel where I have two selection options, and one depends on the other. Despite multiple attempts, I haven't been able to resolve it. The database structure is as follows: companies id title channels id company_id title I ...

What is the best way to display a segment of an SVG on a Canvas element?

Main Issue: The main objective here is to display a specific part of an SVG image on a fixed size Canvas element within a web page. Approach I Tried: After considering various options, such as using CanVG, I thought about utilizing the viewBox attribute ...

Discovering a particular element involves iterating through the results returned by the findElements method in JavaScript

I am attempting to locate and interact with a specific item by comparing text from a list of items. The element distinguished by .list_of_items is a ul that consists of a list of li>a elements. I am uncertain about how to transfer the determined elemen ...

Is the JavaScript progress bar dysfunctional when used with object-oriented JavaScript code, but functions properly with arrow functions?

After posting a question about the JavaScript progress bar not working with object-oriented JavaScript code on Stack Overflow, I decided to try rewriting the script using arrow functions. Here is the new code: document.getElementById("barButton").addEve ...

Launch all external links in Browser (NWjs)

While attempting to create my own independent version of Google Chat using NWjs, I encountered some obstacles. When a link is opened in the NWjs window, it opens in another NWjs window instead of the default system browser, which is what I want. I attemp ...

Juggling Browsers with Protractor

Is there a way to exclude a specific browser from the 'ALL' argument in the protractor config file? For example, how would I exclude Internet Explorer (iexplore) from the desired browsers list using the browser: 'ALL -iexplore' syntax? ...

The AJAX request encountered an error due to an Unexpected End of JSON Input

My AJAX code is encountering an error message. parsererror (index):75 SyntaxError: Unexpected end of JSON input at parse (<anonymous>) at Nb (jquery.min.js:4) at A (jquery.min.js:4) at XMLHttpRequest.<anonymous> (jquery.min.js: ...

AngularJS and CodeIgniter collaborating to bring server-side pagination

I am currently working on integrating pagination using AngularJS and CodeIgniter. The current implementation requires fetching all records at once. However, I aim to modify it so that the data can be retrieved one page at a time from the server during run ...