Cactus
Cactus

Reputation: 924

Selenium and Python: explicit wait for postback in select (wait for Javascript with Selenium)

I am trying to select various elements of a list, working with Python and Selenium. The <select>class comes with a Postbackin Javascript like:

onchange="javascript:setTimeout('__doPostBack(\'dum1$dum2$dum3$ListBoxThingsToSelect\',\'\')', 0)"

When I try to select a value with Selenium, it only works for the first value and then the DOM changes and I am getting a StaleElementReferenceException: Element is no longer attached to the DOMerror.

Thus I try to wait for the element to become available before clicking on it. I do this with the following code and the Expected Conditions (EC):

for row in vals_deselect:
    elem1 = WebDriverWait(browser, 100).until(EC.element_to_be_clickable((
            By.XPATH, "/html/body/form/div[11]/div[2]/fieldset/center/table/tbody/tr[3]/td[3]/select[2]/option[" + str(row) + "]")))
    elem1.click()

This sometimes works for selecting more than 1 values but at some point either fails to click (the value is not clicked on) or results again in a StaleElementReferenceException: Element is no longer attached to the DOMerror. Could anybody help? My theory is that the select element is located and clickable, but the page continues to load because there are more select elements to be loaded. Thus the DOM changes and when I try elem1.click()the DOM has changed.

How could I wait for the entire field to be loaded? What exactly does the postbackdo? I can unfortunately not post a link to the page, it is internal.

EDIT1

The vals_deselectis as list of indices of the indices I want to deselect. These indices do then get pasted into the XPATHin the loop to select the specific option from the select. vals_deselect = [1, 4, 5, 7, 9, 12]

EDIT2 I have also tried the following:

print("jQuery.active: " + str(browser.execute_script("return jQuery.active")))
print("readyState: " + browser.execute_script("return document.readyState"))
WebDriverWait(browser, 100).until(EC.element_to_be_clickable((By.XPATH,
             "/html/body/form/div[11]/div[2]/fieldset/center/table/tbody/tr[3]/td[3]/select[2]/option[" + str(row)+ "]"))).click()

It always shows that the jquery.activeis 0 and the readyStateis complete. I am getting desperate to solve this. I thought by checking the readyStateand jquery.active I could check for the postback to be done. Any ideas by anyone?

The postback is looking exactly like on this page: http://aspalliance.com/articleViewer.aspx?aId=895&pId=-1 I need to wait for it to be done but how do I do this?

Upvotes: 2

Views: 1538

Answers (2)

Guy
Guy

Reputation: 50899

If the <select> is refreshed after each time you choose an option you can use the expected condition staleness_of. You should also use the Select to choose an option from <select> tag

for row in vals_deselect:
    selectElement = driver.find_element_by_xpath("/html/body/form/div[11]/div[2]/fieldset/center/table/tbody/tr[3]/td[3]/select[2]")
    Select(selectElement).deselect_by_index(row)
    WebDriverWait(browser, 100).until(EC.staleness_of(selectElement))

As a side note, you should avoid using absolute path in your xpath. Try to find unique identifier for the element you are looking for as as close as possible in the html hierarchy.

Update

To answer the questions in the comments

Could you please explain to me how this works? Does the fact that the element becomes stale mean that the post back has finished? Also, could you please explain what you meant by your comment about the xpath?

WebElement become stale when it refreshed or changed, which means there is a new element even if it looks exactly the same (like in your case) or deleted. It can happen when the entire DOM is refreshed or changed or just a specific element. EC.staleness_of will wait until an element is no longer attached to the DOM or until the defined time is up.

There are several reasons not to use absolute xpath. Performance (much slower than relative xpath or methods like find_element_by_id), readability and fragility (imagine someone adds a <div> tag before the <table>. Absolute xpath will fail, relative will still work).

Upvotes: 2

Chanda Korat
Chanda Korat

Reputation: 2561

Try this,

from selenium.webdriver.common.action_chains import ActionChains

for row in vals_deselect:
        while not browser.execute_script("return document.readyState")== "complete":
            continue
        else:
            WebDriverWait(browser, 100).until(EC.element_to_be_clickable((
                By.XPATH, "/html/body/form/div[11]/div["
                          "2]/fieldset/center/table/tbody/tr[3]/td[3]/select["
                          "2]/option[" + str(row) + "]")))
            ActionChains(browser).move_to_element(elem1).click().perform()

Upvotes: 0

Related Questions