How to Handle Dropdown in Selenium: Complete Guide
Selenium handles two types of dropdowns differently. For native HTML <select> elements, use Selenium's built-in Select class — it provides select_by_visible_text(), select_by_value(), and select_by_index(). For custom dropdowns (div/ul-based menus), you click the trigger element to open the menu, then click the desired option like any other element.
Key Takeaways
Check if the dropdown is a <select> or a custom element. Right-click the dropdown in your browser and "Inspect." If the HTML is <select>, use the Select class. If it's a <div>, <ul>, or custom component, interact with it as a sequence of clicks.
Select class only works with native <select> elements. Using Select on a div-based dropdown throws UnexpectedTagNameException. Don't use it on anything that isn't a <select>.
select_by_visible_text() is the most readable method. Pass the exact text you see in the dropdown: select.select_by_visible_text("United States"). This is more maintainable than selecting by index.
Multi-select dropdowns require <select multiple>. Use select_by_visible_text() multiple times, or use select_by_index() in a loop. For single-select elements, only the last selection is retained.
Wait for dropdown options to load before selecting. If options are loaded via AJAX after the dropdown opens, wait for them to appear before calling select_by_visible_text().
Two Types of Dropdowns in Selenium
Before writing code, identify which type of dropdown you're dealing with. Right-click the dropdown in your browser, choose "Inspect," and look at the HTML.
Type 1: Native HTML <select> element
<select id="country">
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
</select>
Use Selenium's Select class. Simple, reliable.
Type 2: Custom dropdown (div/ul based)
<div class="dropdown">
<button class="dropdown-toggle">Select country</button>
<ul class="dropdown-menu">
<li data-value="us">United States</li>
<li data-value="uk">United Kingdom</li>
<li data-value="ca">Canada</li>
</ul>
</div>
Use click interactions — click the trigger, then click the option.
Type 3: React/Vue/Angular component dropdowns
<my-select-component ng-model="country" />
These render to either Type 1 or Type 2 in the DOM. Inspect the rendered output to determine which approach to use.
Handling Native Select Elements
Import the Select Class
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
Select by Visible Text
The most common and readable approach:
driver.get("https://example.com/form")
dropdown = driver.find_element(By.ID, "country")
select = Select(dropdown)
select.select_by_visible_text("United States")
The text must match exactly, including capitalization and whitespace. If the option says "United States" and you pass "united states", it will throw NoSuchElementException.
Select by Value
Select using the value attribute of the <option> element:
# <option value="us">United States</option>
select.select_by_value("us")
Useful when working with forms where the value matters for form submission, or when visible text might change between environments.
Select by Index
Select using the zero-based position:
# Select the first option (index 0)
select.select_by_index(0)
# Select the third option (index 2)
select.select_by_index(2)
Avoid this in production tests — index-based selection breaks when options are reordered or new options are added.
Get All Available Options
dropdown = driver.find_element(By.ID, "country")
select = Select(dropdown)
# Get all options as a list of WebElements
options = select.options
for option in options:
print(option.text) # Print each option's visible text
# Get all selected options (useful for multi-select)
selected = select.all_selected_options
print(selected[0].text) # Currently selected option text
# Get the first selected option
first_selected = select.first_selected_option
print(first_selected.text)
Verify Selection
After selecting, verify the expected option is selected:
select.select_by_visible_text("Canada")
assert select.first_selected_option.text == "Canada"
Multi-Select Dropdowns
Native HTML supports <select multiple> elements where multiple options can be selected simultaneously.
<select id="languages" multiple>
<option value="python">Python</option>
<option value="javascript">JavaScript</option>
<option value="java">Java</option>
<option value="go">Go</option>
</select>
select = Select(driver.find_element(By.ID, "languages"))
# Select multiple options
select.select_by_visible_text("Python")
select.select_by_visible_text("JavaScript")
select.select_by_visible_text("Go")
# Verify multiple selections
selected_texts = [opt.text for opt in select.all_selected_options]
assert "Python" in selected_texts
assert "JavaScript" in selected_texts
assert "Go" in selected_texts
# Deselect specific option
select.deselect_by_visible_text("JavaScript")
# Deselect all
select.deselect_all()
Note: deselect_all() throws NotImplementedError on single-select dropdowns. Only use it on <select multiple>.
Handling Custom Dropdowns
Custom dropdowns built with div/ul/li elements need a different approach. There's no Select class — you interact with them like any other element.
Basic Pattern: Click Trigger, Click Option
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Step 1: Click the dropdown trigger to open the menu
trigger = driver.find_element(By.CLASS_NAME, "dropdown-toggle")
trigger.click()
# Step 2: Wait for the menu to appear
WebDriverWait(driver, 5).until(
EC.visibility_of_element_located((By.CLASS_NAME, "dropdown-menu"))
)
# Step 3: Find and click the desired option
option = driver.find_element(By.XPATH, "//li[text()='United States']")
option.click()
Finding Options by Text with XPath
# Exact text match
option = driver.find_element(By.XPATH, "//li[text()='Canada']")
# Partial text match (useful when text has extra whitespace)
option = driver.find_element(By.XPATH, "//li[contains(text(),'Canada')]")
# In a specific dropdown (avoids matching other dropdowns on page)
option = driver.find_element(
By.XPATH, "//div[@class='country-dropdown']//li[text()='Canada']"
)
Reusable Helper Function
def select_dropdown_option(driver, dropdown_locator, option_text):
"""Select an option from a custom dropdown by visible text."""
# Open the dropdown
dropdown = driver.find_element(*dropdown_locator)
dropdown.click()
# Wait for options to appear
WebDriverWait(driver, 5).until(
EC.visibility_of_element_located((By.CLASS_NAME, "dropdown-menu"))
)
# Click the matching option
option = driver.find_element(
By.XPATH, f"//li[normalize-space(text())='{option_text}']"
)
option.click()
# Usage
select_dropdown_option(driver, (By.ID, "country-dropdown"), "United States")
Handling Searchable / Autocomplete Dropdowns
Some dropdowns have a search input that filters options as you type (Select2, Chosen, Combobox patterns).
from selenium.webdriver.common.keys import Keys
# Step 1: Click to open/activate the dropdown
dropdown = driver.find_element(By.CSS_SELECTOR, ".select2-selection")
dropdown.click()
# Step 2: Type in the search box
search_input = driver.find_element(By.CSS_SELECTOR, ".select2-search__field")
search_input.send_keys("United")
# Step 3: Wait for filtered results
WebDriverWait(driver, 5).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, ".select2-results__option"))
)
# Step 4: Click the matching result
option = driver.find_element(
By.XPATH, "//li[contains(@class,'select2-results__option') and contains(text(),'United States')]"
)
option.click()
Handling Dropdowns That Load Options Dynamically
Some dropdowns load options via AJAX after they're opened. If you try to select immediately, the options don't exist yet.
# Open dropdown
driver.find_element(By.ID, "region-dropdown").click()
# Wait for options to load (not just the dropdown container)
WebDriverWait(driver, 10).until(
lambda d: len(d.find_elements(By.CSS_SELECTOR, "#region-dropdown option")) > 1
)
# Now select
select = Select(driver.find_element(By.ID, "region-dropdown"))
select.select_by_visible_text("California")
For custom dropdowns:
# Open dropdown
driver.find_element(By.CLASS_NAME, "city-selector").click()
# Wait until options are populated (not just the list container)
WebDriverWait(driver, 10).until(
lambda d: len(d.find_elements(By.CSS_SELECTOR, ".city-option")) > 0
)
# Click the desired option
driver.find_element(By.XPATH, "//div[@class='city-option'][text()='Seattle']").click()
Common Errors and Fixes
UnexpectedTagNameException
selenium.common.exceptions.UnexpectedTagNameException:
Message: Tag name 'div' is not valid for use with the Select class
Cause: You used Select() on a <div> or other non-select element.
Fix: Inspect the element. If it's not a native <select>, use click-based interaction instead.
NoSuchElementException When Selecting Option
selenium.common.exceptions.NoSuchElementException:
Message: Could not locate element with visible text: United States
Cause: The option text doesn't match exactly (extra whitespace, different capitalization), or the dropdown isn't open yet.
Fix:
# Check for whitespace issues
options = select.options
for opt in options:
print(repr(opt.text)) # repr() shows hidden whitespace
# Use strip-safe XPath
option = driver.find_element(By.XPATH, "//li[normalize-space()='United States']")
StaleElementReferenceException
selenium.common.exceptions.StaleElementReferenceException
Cause: The dropdown refreshed or the DOM changed after you found the element but before you interacted with it.
Fix: Re-find the element after any page/DOM change:
# If parent page re-renders, re-locate the element
dropdown = driver.find_element(By.ID, "country") # Find fresh reference
select = Select(dropdown)
select.select_by_visible_text("Canada")
Element Not Interactable
selenium.common.exceptions.ElementNotInteractableException
Cause: The dropdown is disabled, covered by another element, or not yet visible.
Fix:
# Check if dropdown is enabled
dropdown = driver.find_element(By.ID, "country")
if dropdown.get_attribute("disabled"):
# Dropdown is disabled — wait for it to be enabled
WebDriverWait(driver, 10).until(
lambda d: not d.find_element(By.ID, "country").get_attribute("disabled")
)
# If covered by overlay, wait for overlay to disappear
WebDriverWait(driver, 10).until(
EC.invisibility_of_element_located((By.CLASS_NAME, "modal-overlay"))
)
Complete Working Example
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select, WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def test_form_with_dropdowns():
driver = webdriver.Chrome()
try:
driver.get("https://example.com/registration")
# Native select dropdown
country_dropdown = driver.find_element(By.ID, "country")
country_select = Select(country_dropdown)
country_select.select_by_visible_text("United States")
# Verify selection
assert country_select.first_selected_option.text == "United States"
# Custom dropdown (opens on click)
driver.find_element(By.ID, "state-dropdown").click()
WebDriverWait(driver, 5).until(
EC.visibility_of_element_located((By.CLASS_NAME, "state-options"))
)
driver.find_element(
By.XPATH, "//li[@class='state-option'][text()='California']"
).click()
# Submit form
driver.find_element(By.ID, "submit").click()
# Verify success
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.CLASS_NAME, "success-message"), "Registration complete")
)
finally:
driver.quit()
Summary
| Dropdown Type | Detection | Selenium Approach |
|---|---|---|
Native <select> |
<select> tag in HTML |
Select class with select_by_visible_text() |
| Custom (div/ul/li) | Non-select tag with click behavior | Click trigger, wait for menu, click option |
| Searchable (Select2) | Has a search input when opened | Click, type in search box, click result |
| Dynamic (AJAX options) | Options load after dropdown opens | Click, wait for options to load, then select |
| Multi-select | <select multiple> attribute |
Multiple select_by_visible_text() calls |
Always inspect the HTML first to determine which approach to use. The Select class is convenient but only works with genuine <select> elements.