r-d-r-b-3
r-d-r-b-3

Reputation: 325

Click intercepted, other element would receive the click. Allow this other element to receive the click

Goal:

I want to click the body by below code ;

WebDriverWait(driver,1).until(EC.element_to_be_clickable((By.XPATH, '//body'))).click()

Problem :

My solution needs to be flexible and in case that there is an overlay the click gets intercepted and it throws an error ;

Message: element click intercepted: Element <body>...</body> is not clickable at point (502, 337). 
Other element would receive the click: <div class="_4xvap4u"></div>

Question :

I searched for ages how to achieve to "allow the click" and in this case click the other element which the error displays.

So instead of my orginal click (body) i want to continue in case it's intercepted and click the element thats overlays the body (div / iframe which the error displays). I need this to be flexible so whatever is overlaying and would receive the click (div / iframe /modal / etc)

Upvotes: 1

Views: 1066

Answers (2)

Long
Long

Reputation: 15

I faced a similar issue before, and here's how I tackled it:

When you try to click on the <body> element and there's an overlay, Selenium throws an ElementClickInterceptedException because another element is intercepting the click. To handle this dynamically—regardless of what type of overlay it is—you can catch the exception and then click on the element that's actually receiving the click.

Here's a code snippet that does just that:

from selenium.common.exceptions import ElementClickInterceptedException
import re

try:
    WebDriverWait(driver, 1).until(EC.element_to_be_clickable((By.XPATH, '//body'))).click()
except ElementClickInterceptedException as e:
    # Extract the element from the exception message
    message = e.msg
    match = re.search(r'Other element would receive the click: (<.*?>)', message)
    if match:
        element_html = match.group(1)
        # Parse the HTML to get the tag and attributes
        match = re.match(r'<(\w+)([^>]*)>', element_html)
        if match:
            tag = match.group(1)
            attrs = match.group(2)
            # Build a CSS selector
            selector = tag
            class_match = re.search(r'class="([^"]+)"', attrs)
            if class_match:
                classes = class_match.group(1).split()
                selector += '.' + '.'.join(classes)
            id_match = re.search(r'id="([^"]+)"', attrs)
            if id_match:
                selector += '#' + id_match.group(1)
            # Find the overlaying element and click it
            overlay_element = driver.find_element(By.CSS_SELECTOR, selector)
            overlay_element.click()
        else:
            print("Couldn't parse the overlay element from the exception message.")
    else:
        print("No overlay element found in the exception message.")

This script attempts to click on the <body> element. If an overlay intercepts the click, it catches the exception, extracts the overlay element from the exception message, builds a CSS selector, and clicks on that overlay element.

Alternatively, you can click on whatever element is at the center of the viewport, which is often the overlay:

# Get the center coordinates of the viewport
x = driver.execute_script("return window.innerWidth / 2;")
y = driver.execute_script("return window.innerHeight / 2;")

# Click the element at those coordinates
driver.execute_script("""
    var elem = document.elementFromPoint(arguments[0], arguments[1]);
    if(elem) { elem.click(); }
""", x, y)

This method uses JavaScript to find and click the element at the center of the viewport, which should work regardless of what type of overlay is present.

Note: Parsing exception messages isn't the most robust solution, but it works when you don't have a consistent overlay element to target. If possible, it's better to identify the overlay elements beforehand or to use more specific selectors.

Upvotes: 1

Michael Mintz
Michael Mintz

Reputation: 15516

In this scenario, a regular selenium click is going to throw that exception if there's an overlay, but you can execute JS from selenium to get around that issue. How to simulate a click with JavaScript?

css_selector = "body"
script = (
    """var simulateClick = function (elem) {
           var evt = new MouseEvent('click', {
               bubbles: true,
               cancelable: true,
               view: window
           });
           var canceled = !elem.dispatchEvent(evt);
       };
       var someLink = document.querySelector('%s');
       simulateClick(someLink);"""
    % css_selector
)
driver.execute_script(script)

(If your css_selector contains quotes, you may need to escape them first, such as with re.escape(CSS_SELECTOR), before running that.)

And if you're looking for an easier way to do a JS click within selenium, there's a Python Selenium framework, https://github.com/seleniumbase/SeleniumBase, which already has a method for that: self.js_click("SELECTOR").


If it's always the same element overlay, you can click it first with an if statement if visible.

def is_element_visible(selector, by="css selector"):
    try:
        element = driver.find_element(by, selector)
        if element.is_displayed():
            return True
    except Exception:
        pass
    return False

if is_element_visible("div._4xvap4u"):
    driver.find_element("css selector", "div._4xvap4u").click()

WebDriverWait(driver,1).until(EC.element_to_be_clickable((By.XPATH, '//body'))).click()

If that selector is not guaranteed to be unique from the exception message Other element would receive the click: <div class="_4xvap4u"></div>, then it makes for a good feature request to https://github.com/SeleniumHQ/selenium. But if it is guaranteed to be a unique selector, you could parse it out and then click it.

Upvotes: 1

Related Questions