4

when I attempt to click a button located behind a popup menu I receive the following error message.

*** selenium.common.exceptions.ElementClickInterceptedException: Message: Element <input id="submitButton" class="search-button icon-search active" type="submit"> is not clickable at point (729.2000122070312,22) because another element <div id="monetate_lightbox_mask" class=""> obscures it

This error message is able to identify the name of what is blocking my click

How can I get this name (as an element) so that I can make modifications such as,

element = <div id="monetate_lightbox_mask" class="">

browser.execute_script("""var element = arguments[0]; element.parentNode.removeChild(element);""", element)

The wait function is not applicable as this popup does not go away. I have tried webdriver.ActionChains, but it doesn't not solve this issue

Rhys
  • 4,926
  • 14
  • 41
  • 64

2 Answers2

7

Another interesting workaround would be to perform that click via javascript - in that case, it would not matter what is in front of it or blocking it:

submit_button = driver.find_element_by_id("submitButton")
driver.execute_script("arguments[0].click();", submit_button)

Also, be aware of the differences between a regular selenium click and click via javascript:

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • 1
    I haven't investigated your second solution but this solution works. thanks! – Rhys Dec 17 '18 at 01:27
  • 1
    If you are writing a script that is to emulate user behavior, understand that by clicking (or otherwise interacting with) an element using JS is not something that a user can do. At that point, you are likely traveling down a path that no user could follow... or even something detrimental by skipping a popup warning, etc. There's nothing wrong with doing this from time to time, just be aware this can cause issues. – JeffC Dec 17 '18 at 18:12
  • 1
    ... there... that's a good add. There's some really good explanation in that answer. I'm going to bookmark that one for later so I don't have to repeat myself so often. :) – JeffC Dec 17 '18 at 18:14
3

If I understand correctly, you basically don't want to hardcode monetate_lightbox_mask id and parse it out of the exception message. It is possible with something like:

import re

from selenium.common.exceptions import ElementClickInterceptedException


error_pattern = re.compile(r'another element <div id="(.*?)".*? obscures it')
submit_button = driver.find_element_by_id("submitButton")

try:
    submit_button.click()
except ElementClickInterceptedException as e:
    print("Blocking element detected. Removing..")

    blocking_element_id = error_pattern.search(e).group(0)  # TODO: error handling
    blocking_element = driver.find_element_by_id(blocking_element_id)
    browser.execute_script('var element = arguments[0]; element.parentNode.removeChild(element);', blocking_element)

    print("Element removed. Clicking again...")
    submit_button.click()

Here we are applying a regular expression pattern to the error message to extract the id value which introduces this assumption of that blocking element to have an id. We could though improve it to look for all the attributes and then use these attributes to locate a blocking element.

Instead of a regular expression, we could even use something like BeautifulSoup HTML parser to parse that error message and find HTML elements inside:

In [1]: from bs4 import BeautifulSoup

In [2]: data = """
   ...: *** selenium.common.exceptions.ElementClickInterceptedException: Message: Element <input id="submitButton" cla
   ...: ss="search-button icon-search active" type="submit"> is not clickable at point (729.2000122070312,22) because 
   ...: another element <div id="monetate_lightbox_mask" class=""> obscures it
   ...: """

In [3]: soup = BeautifulSoup(data, "html.parser")

In [4]: for element in soup():
   ...:     print(element)
   ...:     
<input class="search-button icon-search active" id="submitButton" type="submit"/>
<div class="" id="monetate_lightbox_mask"> obscures it
</div>

In [5]: blocking_element = soup()[-1]

In [6]: blocking_element.name
Out[6]: 'div'

In [7]: blocking_element.attrs
Out[7]: {'class': [''], 'id': 'monetate_lightbox_mask'}

Another note: if there are multiple blocking popups, you may need to apply this method recursively.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • thanks for the responses. I'll test these and soon as I can and let you know – Rhys Dec 16 '18 at 23:13
  • 1
    While I think this is an interesting idea in theory, I think in practice it's not going to work. Think about the times that you've seen this message and the blocking element that was listed in the exception message. How many of those blocking elements if clicked would have dismissed the dialog, etc.? I think the better practice is to know the site and properly handle these popups, etc. by knowing when they appear and how to dismiss them. – JeffC Dec 17 '18 at 18:10
  • 1
    @JeffC good points JeffC, it's not quite practical as at least the element representation in the message would not necessarily allow you to uniquely locate that element. Definitely more straightforward to handle it in a site-specific manner. Thanks. – alecxe Dec 17 '18 at 18:12