Search code examples
pythonseleniumstaleelementreferenceexception

Understanding selenium move_to_element behaviour + StaleElementReference exception


I am using Python3.9+Selenium to write a small script that fills an online form for me.

A bit of context: the webpage contains a field (locationField) expecting a street address as input, and located on top of some sort of "google maps wrapper". When typing in the field, it loads a drop-down list of one element (locationField_sugg) with the compatible complete address, and when this option is selected the map zooms-in on the chosen part of the city. Other than this, there is a descriptionField to be filled with some random text, and then a submitButton to be clicked in order to send the form.

I noticed that if I use actionChains.move_to_element(locationField_sugg).click().perform() to click on the address in the drop-down list, then submitButton throws a StaleElementReference exception, while if I just use locationField_sugg.click() this is not the case, and the code proceeds as it should.

I've been reading through many Q/A about this notorious exception handling, but none of them seemed to explain the reason why this happens in my code. For me it seems to be related to the behaviour of move_to_element() in combination with the "map wrapper" (?) but I do not understand why, since this function is just supposed to move the mouse in the middle of a given element.

No reload nor other changes in the webpage seem to happen (I verified that if I query the button at the beginning and re-query at the end of the script, I get the same exact instance representation string). Besides, I query the submitButton right before performing an action on it, and I assume it is properly found since it can be printed.

Below there is a snippet of my code and of the output I get (Note: the code works properly if I use the alternative commented option, but I am curious to understand what I am missing)

CODE SNIPPET

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

websiteUrl = "https://mywebsite"
option = webdriver.ChromeOptions()
option.add_argument("-incognito")
browser = webdriver.Chrome(executable_path="/Applications/chromedriver", options=option)
browser.get(websiteUrl)
actionChains = ActionChains(browser)

# Write and select complete address
locationField = browser.find_element_by_id("location")
print("locationField = ", locationField)
locationField.send_keys("my location")
locationField_sugg = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.ID, "as-listbox")))
print("locationField_sugg = ", locationField_sugg)
#
# this throws stale element reference exception:
actionChains.move_to_element(locationField_sugg).click().perform()
#
# this does not:
# locationField_sugg.click()

# Write description
descriptionField = browser.find_element_by_id("description")
print("descriptionField = ", descriptionField)
descriptionField.send_keys("my description")

# Submit form
submitButton = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "span.value")))
print("submitButton = ", submitButton)
actionChains.move_to_element(submitButton).click().perform()

OUTPUT

locationField =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="e75539a7-ebd0-4090-9c04-0c4994afe03f")>
locationField_sugg =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="53ef0269-aceb-451a-b559-d9e5e7aa7851")>
descriptionField =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="dce478de-a23d-4ca2-817c-81ee5ce0c232")>
nowButton =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="f2036f1c-d164-4211-a37c-2ee50e5c55c1")>
Traceback (most recent call last):
  File "/Users/alice/Desktop/wasteComplaints_selenium.py", line 44, in <module>
    actionChains.move_to_element(nowButton).click().perform()
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/common/action_chains.py", line 80, in perform
    self.w3c_actions.perform()
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/common/actions/action_builder.py", line 76, in perform
    self.driver.execute(Command.W3C_ACTIONS, enc)
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
  (Session info: chrome=92.0.4515.131)

Solution

  • I'm not sure I know why this occurs without debugging it. Maybe the suggested address is disappearing at the moment when focus is removed from the input field? If so, when you insert the address string to the input field and then instantly clicking on the suggested address it works correct, but if after inserting the input address string and the suggested address appears you are applying actionChains.move_to_element this moves the mouse from it's initial position to the suggested address element. So the focus is moved from input field to the mouse cursor. This causes the suggested address to disappear and this is why StaleElementReference exception is thrown.