ScreenshotNeo

BlogHow-to

How to Handle Dropdowns in Selenium WebDriver With Python

Select and verify options in native HTML dropdowns with Selenium Python, handle multi-selects and custom widgets, and fix common errors.

By the ScreenshotNeo team4 October 20268 min read

To handle a dropdown in Selenium WebDriver with Python, first check whether it is a native HTML <select>. For a native select, locate the element, wrap it with Selenium’s Select helper, choose an option by its visible text or value, and verify the selected state. If the dropdown is a custom JavaScript widget built from elements such as div or li, use regular WebDriver interactions instead: Select only supports native select and option elements.

1. Check whether the dropdown is a native select

Inspect the element in the browser’s developer tools. A native control has a <select> element containing <option> elements. A custom control may look like a dropdown but use a button, div, or list items for its trigger and options.

Selenium’s select-list guide says the Select class works only with HTML select and option elements. Passing a custom widget’s trigger to Select raises UnexpectedTagNameException; use the custom-widget approach below instead.

2. Select an option in a native dropdown

Install Selenium if it is not already in your environment with python -m pip install selenium. The following script is runnable with a Selenium-compatible browser and driver available to your environment. It opens a page containing a native dropdown, selects Canada, verifies the result, and closes the browser:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select

# Replace this URL and locator with your page and dropdown.
driver = webdriver.Chrome()
try:
    driver.get("https://example.com/form")
    dropdown = Select(driver.find_element(By.ID, "country"))
    dropdown.select_by_visible_text("Canada")

    selected = dropdown.first_selected_option
    assert selected.text.strip() == "Canada"
finally:
    driver.quit()

Replace the example URL and locator with values from your application. Selenium’s Select constructor checks that the wrapped element is a select tag. The Python bindings documented by Selenium 4.49.0 support Python 3.10 and later; your installed Selenium and browser setup may have different requirements, so check the Python API documentation for your version.

Choose the matching method that expresses the test

Method Use it when Example
select_by_visible_text(text) The user-facing label is what the test intends to choose. select_by_visible_text("Canada")
select_by_value(value) The option’s stable HTML value is the intended form value. select_by_value("ca")
select_by_index(index) Position itself matters and the order is controlled. select_by_index(2)

Visible text makes a test read like the user’s choice. A stable value can be less sensitive to label changes. Index selection depends on option order, so it can become brittle if the page reorders options. If no option matches the requested text, value, or index, Selenium raises NoSuchElementException.

Wait for dynamically loaded options

If another action loads the options asynchronously, wait for the option you need before selecting it. Use a condition tied to the actual page state rather than a fixed sleep:

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

wait = WebDriverWait(driver, 10)
wait.until(
    EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#country option[value="ca"]')
    )
)
country = Select(driver.find_element(By.ID, "country"))
country.select_by_value("ca")
assert country.first_selected_option.get_attribute("value") == "ca"

Choose a wait condition that matches the application: the element may need to be present, visible, enabled, or changed to a particular state. If selecting an option triggers another asynchronous update, wait for and assert that downstream result too.

3. Handle multi-select controls

A native <select multiple> can have several selected options. Call a selection method once for each desired option, then inspect all_selected_options to verify the complete selection:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select

languages = Select(driver.find_element(By.NAME, "languages"))
languages.select_by_value("python")
languages.select_by_value("go")

selected_values = {
    option.get_attribute("value")
    for option in languages.all_selected_options
}
assert selected_values == {"python", "go"}

For a single-select control, first_selected_option returns the selected option. For a multi-select, it returns the first selected option, while all_selected_options exposes all selected options. Deselect methods are intended for multi-select controls. For example, deselect_all() on a single-select raises NotImplementedError.

4. Work with a custom JavaScript dropdown

If inspection shows a custom trigger and custom option elements, do not pass the trigger to Select. Locate the trigger, click it, wait until the desired option is available, click that option, and verify the resulting selected state. The exact locators depend on the page markup; this example shows the interaction pattern, with selectors to replace for your application:

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

wait = WebDriverWait(driver, 10)
trigger = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "[data-testid='country-trigger']"))
)
trigger.click()

option = wait.until(
    EC.element_to_be_clickable((
        By.XPATH,
        "//li[@role='option' and normalize-space()='Canada']"
    ))
)
option.click()

# Replace this assertion with the state exposed by your widget.
selected_label = wait.until(
    EC.visibility_of_element_located(
        (By.CSS_SELECTOR, "[data-testid='country-value']")
    )
)
assert selected_label.text.strip() == "Canada"

Custom widgets may expose state through an updated label, an aria-selected attribute, or a hidden form field. Assert the state that represents a completed selection in your application. For keyboard-only widgets, use the documented keyboard interaction and wait for the same resulting state.

5. Verify selection and page behavior

A successful click is not enough if the test depends on what the form submits or how the page reacts. For native controls, inspect first_selected_option or all_selected_options; check a stable option value when that is what the form submits. If selection updates another field or submits the form, wait for that field or result and assert it as well.

WebDriver element interactions perform interactability checks and can scroll an element into view. If an interaction fails, check whether the element is visible, enabled, covered by another element, or still being updated by the page. For asynchronously rendered options, wait for the relevant state before interacting.

6. Troubleshooting

Symptom or error Likely cause Fix
UnexpectedTagNameException The element passed to Select is not a native <select>. Inspect the DOM. For a custom widget, interact with its trigger and options using normal WebDriver actions.
NoSuchElementException while selecting The visible text, value, or index does not match an available option, or options have not loaded yet. Inspect the current option labels and values. For dynamic options, wait for the required option, then select it.
A disabled dropdown cannot be wrapped Selenium’s guide documents that constructing a Select for a disabled select is disallowed from Selenium 4.5 onward. Check whether an earlier page interaction must enable the control. Confirm the installed Selenium version and current element state.
The selection appears to revert or not persist The page may update asynchronously, reject the choice, or rerender the control. Wait for the selected option or the application’s resulting state, then assert it. Avoid relying on a fixed pause.
A click times out or is intercepted The target may not yet be clickable, may be outside the active view, or may be covered by an overlay. Wait for clickability, inspect overlays and visibility, and use the actual interactive element for a custom widget.
deselect_all() raises NotImplementedError The select is not a multi-select. Use deselection methods only when the native select has the multiple attribute.

7. Performance, reliability, and test cost

  • Use condition-based waits. They let the test continue as soon as the expected state appears and prevent fragile timing assumptions. Set a timeout that fits the page’s expected behavior.
  • Use stable locators. IDs, names, or application-provided test attributes are easier to maintain than selectors tied to styling or changing element positions.
  • Avoid unnecessary browser restarts. Reusing a browser session within an appropriate test fixture avoids setup work, while independent tests should still keep their state isolated.
  • Keep assertions tied to behavior. Verify the selected value or downstream result your test cares about instead of adding redundant checks after every action.
  • Diagnose screenshots carefully. A screenshot can show what was visible when a test failed, but it does not prove that a native option was selected or submitted. Inspect the DOM and selected state too.

For a screenshot of a page state while diagnosing a failed browser run, you can capture the page locally with WebDriver’s screenshot methods. If you need a screenshot API instead, ScreenshotNeo is a website screenshot API and MCP server. It removes cookie and consent banners, newsletter popups, and chat widgets before capture, and only clean shots are billed; bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing.

Or skip the browser setup

For a standalone page capture, ScreenshotNeo takes a URL in one GET request and returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options. This captures a page; it does not operate the dropdown as a Selenium test would.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Replace the sample URL with the page you want to capture and supply your API key. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. The MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Learn more at ScreenshotNeo and the API docs.

Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card required.

Frequently asked questions

How do I select a dropdown value in Selenium Python?

For a native <select>, wrap its WebElement in Select and call a selection method such as select_by_value, then verify the selected option.

How do I select an option by visible text?

Call select_by_visible_text("the label") on a Select object created from the native select element.

Why doesn’t Selenium Select work with my dropdown?

The control may be a custom widget made from elements other than native <select> and <option>. Use the page’s interactive trigger and option elements with regular WebDriver actions.

Can I select more than one option?

Yes, if the native select has the multiple attribute. Select each desired option and inspect all_selected_options.

ScreenshotNeo is a product of Yorker Media.