How to Handle Dropdowns in Selenium WebDriver with Java
Learn when Selenium’s Select helper works, how to choose and verify options in Java, and how to handle custom dropdowns with reliable waits.
First check the page’s HTML. If the control is a native <select> containing <option> elements, use Selenium’s Select class. If it is a JavaScript widget built from elements such as div or li, interact with its trigger and visible option as ordinary WebDriver elements. Select is specifically for native select lists; it does not operate custom dropdowns. Selenium’s select-list guide documents this distinction.
1. Set up a Java example you can run
This small Maven project creates a browser page with a native dropdown, chooses an option by its label, and verifies the selected value. It uses a Base64 data URL so the example does not depend on a third-party demo page.
Create pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>selenium-dropdowns</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.49.0</version>
</dependency>
</dependencies>
</project>
Create src/main/java/DropdownExample.java:
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;
public class DropdownExample {
public static void main(String[] args) {
String html = "<!doctype html><html><body>"
+ "<label for='pet'>Pet</label>"
+ "<select id='pet' name='pet'>"
+ "<option value='cat'>Cat</option>"
+ "<option value='dog'>Dog</option>"
+ "<option value='bird'>Bird</option>"
+ "</select></body></html>";
String dataUrl = "data:text/html;base64," + Base64.getEncoder()
.encodeToString(html.getBytes(StandardCharsets.UTF_8));
WebDriver driver = new ChromeDriver();
try {
driver.get(dataUrl);
WebElement selectElement = driver.findElement(By.id("pet"));
Select pet = new Select(selectElement);
pet.selectByVisibleText("Dog");
String selectedValue = pet.getFirstSelectedOption().getAttribute("value");
if (!"dog".equals(selectedValue)) {
throw new AssertionError("Expected dog, got " + selectedValue);
}
System.out.println("Selected: " + pet.getFirstSelectedOption().getText());
} finally {
driver.quit();
}
}
}
Run it from the project directory with mvn compile, then mvn exec:java -Dexec.mainClass=DropdownExample after adding Maven’s exec plugin, or run the compiled class from your IDE. Selenium Manager can manage supported browser drivers when the browser is installed; check the Selenium getting-started guide for setup details. The dependency above reflects the Java example version identified in the source research; use the version approved by your project.
2. Choose the right selection method
Locate the actual <select> element, then pass it to Select. The helper offers three common matching methods:
| Method | Matches | Use it when |
|---|---|---|
selectByVisibleText("Dog") |
Displayed option text | The label is clear and stable for the test. |
selectByValue("dog") |
The option’s HTML value |
The value is a stable application identifier. |
selectByIndex(1) |
The option position | Position itself is what the test intends to cover. |
Visible text corresponds to what a user sees; value targets the option attribute. Index is tied to ordering, so a reorder can make a test select a different option. That is a practical consequence of positional matching, not a Selenium guarantee. The Java Select API lists the methods and their matching behavior.
WebElement countryElement = driver.findElement(By.name("country"));
Select country = new Select(countryElement);
country.selectByVisibleText("Canada");
// Or: country.selectByValue("ca");
// Or: country.selectByIndex(2);
Use an exact label or known value when possible. If the option list is populated asynchronously, wait for the option or expected state before selecting. Avoid selecting by index just because it is convenient when the order may change.
3. Inspect options and verify the result
A selection call is an action; a test should also check the resulting state. getOptions() returns all options, getFirstSelectedOption() returns the selected option for a single select, and getAllSelectedOptions() returns all selected options. isMultiple() tells you whether the control supports multiple selections.
Select select = new Select(driver.findElement(By.id("pet")));
for (WebElement option : select.getOptions()) {
System.out.printf("label=%s value=%s selected=%s%n",
option.getText(), option.getAttribute("value"), option.isSelected());
}
select.selectByValue("dog");
WebElement selected = select.getFirstSelectedOption();
if (!"dog".equals(selected.getAttribute("value"))) {
throw new AssertionError("Dropdown did not retain the expected value");
}
For dynamic pages, verify any dependent result too—for example, that choosing a country updates the state list. The selected option alone may not prove that the application’s change handler completed.
4. Handle a multiple select
A native list allows multiple selections only when its <select> has the multiple attribute. Select each desired option, then compare the actual selected options with the expected set.
Select toppings = new Select(driver.findElement(By.id("toppings")));
if (!toppings.isMultiple()) {
throw new IllegalStateException("Expected a multiple select");
}
toppings.selectByValue("olives");
toppings.selectByVisibleText("Mushrooms");
for (WebElement option : toppings.getAllSelectedOptions()) {
System.out.println(option.getText());
}
toppings.deselectByValue("olives");
// Or clear all selections:
toppings.deselectAll();
Available deselection methods include deselectByVisibleText, deselectByValue, deselectByIndex, and deselectAll. Deselect methods are valid only for multiple selects; calling them on a single-select control raises UnsupportedOperationException. Confirm that a requested option is selected or deselected instead of assuming the operation succeeded.
5. Handle custom JavaScript dropdowns
A control that looks like a dropdown may be a button or div that opens an overlay, with options represented by other elements. Do not wrap that trigger in Select. Inspect the DOM and accessibility attributes to find the real trigger and the option element, then click them using WebDriver. The exact locators depend on the widget.
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Example only: replace these locators with the widget's actual DOM attributes.
By trigger = By.cssSelector("[role='combobox']");
By option = By.xpath("//*[@role='option' and normalize-space()='Canada']");
wait.until(ExpectedConditions.elementToBeClickable(trigger)).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(option)).click();
wait.until(ExpectedConditions.attributeToBe(trigger, "aria-expanded", "false"));
This pattern assumes the widget exposes a combobox trigger, option roles, and an aria-expanded state. Replace those assumptions with the markup and behavior your page actually uses. Some widgets need keyboard navigation, a search-field entry, scrolling, or a wait for a network-backed option list. Selenium’s WebDriver documentation covers ordinary element interactions and locating elements; it does not prescribe one universal custom-dropdown sequence.
6. Wait for the state the page needs
For a native select that exists but whose options arrive later, wait for a specific option to appear before constructing or using Select. For a custom dropdown, wait for the menu or option to become visible and clickable. Use explicit waits tied to the expected condition rather than a fixed sleep.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
By selectLocator = By.id("country");
By canadaOption = By.cssSelector("#country option[value='ca']");
wait.until(ExpectedConditions.presenceOfElementLocated(selectLocator));
wait.until(ExpectedConditions.presenceOfElementLocated(canadaOption));
new Select(driver.findElement(selectLocator)).selectByValue("ca");
If selecting triggers an update, wait for the changed field or result, not merely for the selection command to return. Keep the timeout appropriate to your environment; excessively long timeouts can slow failure diagnosis.
7. Disabled selects and disabled options
Check whether the select or target option is disabled before treating a selection failure as a locator problem. Selenium’s guide notes that as of Selenium 4.5, constructing a Select for a disabled <select> is not allowed. The guide also documents that an option with a disabled attribute may not be selected and can raise UnsupportedOperationException. Confirm the element state and the Selenium version in the project when investigating this behavior.
8. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
UnexpectedTagNameException |
The located element is not a native select, often a custom wrapper. |
Inspect the DOM. Use Select only on the real native select; otherwise click the widget’s trigger and option elements. |
NoSuchElementException from selection |
No option matches the exact text or value, or the options have not loaded yet. | Inspect getOptions(), verify whitespace/case and option attributes, and wait for the expected option. |
ElementNotInteractableException or click interception |
The custom option is hidden, covered, outside the viewport, or not yet ready. | Open the widget, wait for visibility and clickability, and use the actual visible option locator. |
UnsupportedOperationException |
The select is single-valued when using deselection, or the requested option is disabled. | Check isMultiple() and the option’s disabled state; choose an enabled option and only deselect a multiple select. |
| The command succeeds but the application does not update | The application’s change handler or dependent request is still running, or the wrong visual widget was targeted. | Wait for and assert the page-level result. For custom controls, interact with the actual widget rather than an unrelated hidden native element. |
| A test selects the wrong option after a page change | Index-based selection depends on option order. | Use a stable value or exact label where possible, and assert the selected value. |
| Driver or browser startup fails | Browser installation, version, permissions, or driver setup is unavailable. | Check the browser and Selenium setup guide, then confirm the driver can start in the test environment. |
9. Reliability, runtime, and test design
- Prefer stable IDs, names, labels, or application-owned test attributes over long positional or styling-dependent selectors.
- Wait for observable conditions around asynchronous menus and dependent updates; avoid arbitrary sleeps.
- Keep selection and verification together so failures identify the unexpected state.
- Use a fresh, controlled browser state where prior selections or cookies could affect the page.
- For flaky custom widgets, inspect whether the menu is rendered in a portal elsewhere in the DOM, whether it scrolls independently, and whether a loading state appears before options.
- Browser automation has setup and execution costs: browser startup and page loading often take more time than the selection command itself. Reuse a driver within a test where appropriate, and always quit it in cleanup.
10. Or skip the browser setup
If your task is to capture a page for visual review rather than interact with its dropdown, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF. See the API documentation for parameters and response details.
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,
)
r.raise_for_status()
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}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free for 1,000 screenshots a month, no card required.
11. FAQ
Can Selenium select an option that is not visible in the open list?
For a native select, use the matching option through Select and verify the result. A custom widget may require opening, scrolling, or searching before its option can be clicked.
Should I use JavaScript to set the select value?
Usually, test through the user-facing WebDriver interaction. Direct script changes can bypass the behavior the test is intended to cover, including event handling.
How do I tell whether a dropdown is native?
Inspect the element in browser developer tools. A native control has a select tag and child option elements; visual appearance alone does not identify its implementation.
Can a multiple select have no selected options?
Yes. Use getAllSelectedOptions() to inspect its current selection rather than assuming there is always a selected item.


