Marley
Marley

Reputation: 147

python selenium - selecting option from dropdown

I am attempting to use the below code to use selenium (python) to select an option from a dropdown on a webpage.

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

path_to_chromedriver = 'C:/Users/User_1/chromedriver/chromedriver'
browser = webdriver.Chrome(executable_path = path_to_chromedriver)

url = 'https://lifeinsurance.rac.com.au/rac/get-a-quote?productid=51'
browser.get(url)

wait = WebDriverWait(browser, 10)

# residence listbox
drop_down = browser.find_element_by_xpath('//*[@id="divApplicantDetails__1"]/div[8]/div/div[2]/span/span/span[1]')

browser.execute_script('arguments[0].style.display="inline";', drop_down)
wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="divApplicantDetails__1"]/div[8]/div/div[2]/span/span/span[1]')))

wait.until(EC.element_to_be_clickable((By.XPATH, r'//*[@id="ddResidentialStatusId__1_listbox"]/li[1]'))).click()

There are occasions where it works (and an option from the dropdown is selected), but on other occasions an option from the dropdown is NOT selected (and no error raised either).

I would appreciate any feedback on how to consistently select an option from the dropdown noted in the code provided.

Upvotes: 0

Views: 3794

Answers (4)

Herker
Herker

Reputation: 582

Dropdowns without Select()

I remember I had some very annoying issues regarding dropdowns with Selenium, without Select()

Use the ActionsChains module

To use ActionsChains you need to import the following...

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By

It is much easier to select a dropdown option by its text

actions = ActionsChains(browser)        
dropdown = browser.find_element(By.XPATH, "//*[text()='dropdown option']").click()
actions.click(on_element=dropdown)
actions.perform()

If that doesn't work, try to insert time.sleep as well.

Upvotes: 0

cle-b
cle-b

Reputation: 109

First you can click on the select box which is always clickable

driver.find_element_by_xpath("//span[@class='k-input' and text()='Select residential status']").click()

Then you can click on the desired item only when the list of residential status is visible (aria-hidden='false')

driver.find_element_by_xpath("//ul[@id='ddResidentialStatusId__1_listbox' and @aria-hidden='false']/li[@class='k-item' and text()='Other']").click()

In order to work, this solution need to setup implict wait for your script

driver = webdriver.Chrome(executable_path = path_to_chromedriver)
driver.implicitly_wait(5)

Edit: I do more tests and this solution doesn't works at each time. So I suggest you to add a small pause even it's preferable to avoid this. In this case I think it's not breakable as it's only used in order to allow the list box to be rendered completely.

driver.find_element_by_xpath("//span[@class='k-input' and text()='Select residential status']").click()
driver.find_element_by_xpath("//ul[@id='ddResidentialStatusId__1_listbox' and @aria-hidden='false']")
time.sleep(1) 
driver.find_element_by_xpath("//ul[@id='ddResidentialStatusId__1_listbox' and @aria-hidden='false']/li[@class='k-item' and text()='Other']").click()

Upvotes: 1

JeffC
JeffC

Reputation: 25596

I wouldn't try to manipulate the CSS of an element. Just approach it like a user would... click on the dropdown, click on an option. I used different locators but this worked for me.

driver.find_element_by_css_selector("span[aria-owns='ddResidentialStatusId__1_listbox']")).click()
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#ddResidentialStatusId__1_listbox > li")))
options = driver.find_elements_by_css_selector("#ddResidentialStatusId__1_listbox > li"))
options[0].click() # clicks "Australian citizen or permanent resident"

python doesn't have visibilityOfAllElementsLocatedBy() like Java does so I had to wait for a single element, then get all of them, and then click the first one. If you really want to just click the first one, you can just do

wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#ddResidentialStatusId__1_listbox > li"))).click()

and skip the last two lines of code. I left it as is because I'm assuming that you will want to (Jedi mind powers working on you) throw this in a function and select different options.

Upvotes: 1

yong
yong

Reputation: 13712

def select_resident_status(resident_status):
    // click the arrow down to make all options list out
    driver.find_element_by_css_selector('span[aria-owns*="ResidentialStatus"] span.k-select').click()

    // select option by pass-in option text
    driver.find_element_by_css_selector('div[id*="ResidentialStatus"][style*="display: block"] ul')
          .find_element_by_xpath('./li[text()="'+resident_status+'"]').click()

Upvotes: 1

Related Questions