pythonseleniumdomstaleelementreferenceexceptionstaledataexception

How to use Selenium in Python to update multiple page elements without refreshing the DOM to avoid StaleElementReferenceException?


I have a school website html page that is used for publishing homework that looks something like this: table screenshot

When you click on each lesson, a javascript div element pops up where you write in the homework and submit via a button. I wanted to automate populating the entire table. The problem is that after each homework entry is submitted (via http post) the DOM refreshes. Trying to update the next entry obviously fails and throws the following exception:

selenium.common.exceptions.StaleElementReferenceException: Message: The element reference of (ELEMENT DESCRIBED) is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed.

I loop over all the lessons like this:

monthly_classes = driver.find_elements(By.XPATH, '//div[@class="homework_entry"]')
count = 0
for item in monthly_classes:
    if item.text == "Lesson 1":
        item.click()
        postHomework()
        count += 1
        print(item)
    else:
        print("Pass")

I have tried after submission to keep the DOM from reloading with the following code:

driver.execute_script("window.stop();")

and

driver.find_element(By.TAG_NAME, "body").send_keys("Keys.ESCAPE")

Any way to prevent the DOM from reloading? Is there a better way to try accomplish this task?


Solution

  • Every time selenium finds an element it creates an identifier for it. So when you submit something on that page, the page reloads and all the elements become new for selenium (they have now other identifiers).
    To resolve the problem you need to find all the elements again after submit.
    So first of all you need to know the quantity of the elements. Then you can start to loop through them by index. And at the end of the loop find the elements again:

    monthly_classes = driver.find_elements(By.XPATH, '//div[@class="homework_entry"]')
    monthly_classes_count = len(monthly_classes)
    
    for i in range(monthly_classes_count):
        if monthly_classes[i].text == "Lesson 1":
            monthly_classes[i].click()
            postHomework()
            count += 1
            print(item)
        else:
            print("Pass")
        monthly_classes = driver.find_elements(By.XPATH, '//div[@class="homework_entry"]')