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 programmatically change the event tied to an element's click

<div id="filters"></div> <div id="available"> <span class="badge badge-pill" onclick="addFilter()">Label</span> </div> function addFilter(event) { document.getElementById("filters").appendChild(eve ...

When using React, draggable components with input fields may lose their ability to receive focus when clicking on the input field

<Draggable axis="y" grid={[135,135]} onStop={this.handleStop} defaultPosition={{x: this.props.task.positionX, y: this.props.task.positionY,}}> <div id="edit-task-component"> <form onSubmit={this.handleSubmit} id=" ...

Unable to move cursor in contenteditable section

I am currently working on developing a rich text editor in React, but I have encountered an issue that has me stuck. The problem I am facing is that as I type each character, the insertion point does not update accordingly, causing the cursor to remain stu ...

There seems to be an issue: [ng:areq] - Please visit the following link for more information: http://errors.angularjs.org/1

Hey there! I'm in need of some assistance. Just started learning Angular and tried setting it up like this. This is the structure of my files: AboutController.js function AboutController( $scope ){ $scope.data = { "data" : { "name ...

Sort the data - a selection menu is included in each row of the table

My issue involves a datatable where each row must have a select item with multiple options to choose from. The goal is to filter the rows based on the selected option when using the default search bar located above the table. However, I've encountered ...

Identifying when a user has inputted incorrect $routeparams

How can I restrict user input to only "id" as a query parameter in the URL? $scope.$on('$routeUpdate', function () { var id = $routeParams.id //check if user has entered any params other than "id". //if yes do someting }); I want ...

Error unfound: [CLIENT_MISSING_INTENTS]: The Client requires valid intents to function properly

I've gone through multiple tutorials, but I keep encountering an uncaught TypeError. Despite following the suggested solutions, the error persists. I even tried implementing the "intent" solution, but it's prompting a change in "const client = ne ...

Using JQuery to manipulate `this`, its children, its siblings, and more

Can anyone help me determine the most effective way to send data from a get request to a specific div? My challenge is to send it only to a div that is related to the one triggering the action. For example, when I click on the message_tab, I want to send t ...

Cookie Consent has an impact on the performance of PageSpeed Insights

On my website, I have implemented Cookie Consent by Insights. The documentation for this can be found at However, I noticed a significant drop in my Google PageSpeed Insight scores after loading the JavaScript source for Cookie Consent. The main issue hig ...

How to break down JSON into individual elements using JavaScript

{ "name": "Sophia", "age": "Unknown", "heroes": ["Batman", "Superman", "Wonder Woman"], "sidekicks": [ { "name": "Robin" }, { "name": "Flash Gordon" }, { "name": "Bucky Barnes" } ...

Is it possible to invoke a helper function by passing a string as its name in JavaScript?

I'm encountering a certain issue. Here is what I am attempting: Is it possible to accomplish this: var action = 'toUpperCase()'; 'abcd'.action; //output ===> ABCD The user can input either uppercase or lowercase function ...

Assistance with XPATH: Locating specific text and locating the subsequent button within a few divs down

New to XPATH... I'm attempting to locate specific text within a span element, followed by the next button in the code block below. The numbers associated with ember change dynamically, so I can't rely on those for identification. My goal is to lo ...

Exploring the Power of jQuery Deferreds and Promises through Multiple getJSON Requests

I'm currently exploring the concept of jquery deferred/promises. I have a decent grasp on how to handle one ajax call, but I'm struggling with managing multiple ajax calls simultaneously. Here is a link to a jsfiddle showcasing my attempt: http:/ ...

Import JSON Data into Angular-nvD3 Chart (AngularJS)

I am seeking help to load encoded JSON Data retrieved from a database via queries into an Angular-nvD3 graph. I am unsure about the best approach to achieve this task. The encoded JSON data is fetched using API queries from a database table called PRODUCT ...

Error encountered when attempting to initiate a second screenshare on Chrome due to an invalid state

I am interested in utilizing Screensharing in Chrome. After following a guide and creating an extension to access the deviceId for getUserMedia, I was able to successfully start streaming my screen. However, when I attempted to stop the stream using the pr ...

Countdown malfunction: wrong date displayed

Utilizing the Countdownjs library in my project is resulting in an incorrect day count. Incorporating AngularJS, here is the custom directive I've implemented for the countdown: .directive('tempoPercorrido', function($interval){ ret ...

Execute JavaScript function after completion of CSS animation using jQuery

I'm working on an accordion feature that uses CSS animation to expand the clicked item. The expansion is triggered by li:target. However, I'm facing an issue where when clicking on an item, the scroll position doesn't align correctly with t ...

`the issue of $scope object not being passed correctly to ng-if and ng-class ternary conditions

**app.js:** $scope.servers = [ {name:'SQL Server', status:"up"}, {name:'Web Server', status:"down"}, {name:'Index Server', status:"down"} ]; **index.html:** <table> ...

Setting the column width and border-top in Highcharts

Hey there, I'm facing an issue with my highcharts diagram. 1) Can anyone help me adjust the column width to match the width of the month area? (I've tried changing "width" in CSS but it's not working) 2) Also, how can I remove all borders ...

JavaScript code to make titles slide in as the user scrolls

Looking for a better way to make all titles slide in upon scrolling instead of coding individually? Consider using a forEach loop! Here's how you can modify the code below: function slideInLeft() { document.querySelectorAll('.subtitle-left ...