Skip to main content
SeleniumDecoded

Select a Dropdown Option by Visible Text

Choose options in native select elements with the Select helper, and handle custom (non-select) dropdowns built from divs.

Stable Verified against Selenium 4.48.0

Problem

You need to pick “United Kingdom” from a country dropdown. Half the time it is a real <select>; the other half it is a styled <div> list from a component library, and Select throws UnexpectedTagNameException.

Solution

Detect the tag. Use the Select helper for native selects; for custom widgets, click the trigger and click the option by text.

Native select and custom dropdown
Selenium 3 & 4 Stable
import org.openqa.selenium.support.ui.Select;
public static void choose(WebDriver driver, By trigger, String visibleText) {
WebElement el = driver.findElement(trigger);
if ("select".equalsIgnoreCase(el.getTagName())) {
new Select(el).selectByVisibleText(visibleText);
return;
}
// Custom dropdown: open it, then click the matching option
el.click();
By option = By.xpath("//*[@role='option' or contains(@class,'option')][normalize-space()='" + visibleText + "']");
new WebDriverWait(driver, Duration.ofSeconds(5))
.until(ExpectedConditions.elementToBeClickable(option)).click();
}
choose(driver, By.id("country"), "United Kingdom");
from selenium.webdriver.support.select import Select
def choose(driver, trigger, visible_text):
el = driver.find_element(*trigger)
if el.tag_name.lower() == "select":
Select(el).select_by_visible_text(visible_text)
return
el.click()
option = (By.XPATH, f"//*[@role='option' or contains(@class,'option')][normalize-space()='{visible_text}']")
WebDriverWait(driver, 5).until(EC.element_to_be_clickable(option)).click()
choose(driver, (By.ID, "country"), "United Kingdom")
const { Select } = require('selenium-webdriver/lib/select');
async function choose(driver, trigger, visibleText) {
const el = await driver.findElement(trigger);
if ((await el.getTagName()).toLowerCase() === 'select') {
await new Select(el).selectByVisibleText(visibleText);
return;
}
await el.click();
const option = By.xpath(`//*[@role='option' or contains(@class,'option')][normalize-space()='${visibleText}']`);
const opt = await driver.wait(until.elementLocated(option), 5000);
await driver.wait(until.elementIsVisible(opt), 5000);
await opt.click();
}
await choose(driver, By.id('country'), 'United Kingdom');
using OpenQA.Selenium.Support.UI;
public static void Choose(IWebDriver driver, By trigger, string visibleText)
{
var el = driver.FindElement(trigger);
if (el.TagName.Equals("select", StringComparison.OrdinalIgnoreCase))
{
new SelectElement(el).SelectByText(visibleText);
return;
}
el.Click();
var option = By.XPath($"//*[@role='option' or contains(@class,'option')][normalize-space()='{visibleText}']");
new WebDriverWait(driver, TimeSpan.FromSeconds(5))
.Until(d => { var o = d.FindElement(option); return o.Displayed && o.Enabled ? o : null; }).Click();
}
Choose(driver, By.Id("country"), "United Kingdom");

Why It Works

Select manipulates the native element and fires change, which is what the app listens to. Custom dropdowns are just DOM: opening the trigger renders the options (often with role="option"), and clicking one runs the component’s handler. Matching by normalize-space() text tolerates padding whitespace.

Gotchas

  • Some custom dropdowns render options in a portal at the end of <body>, not inside the trigger; the document-wide XPath above handles that.
  • Searchable dropdowns need typing first: sendKeys(visibleText) into the trigger’s input, then click the option.
  • For native selects, selectByValue and selectByIndex exist too; visible text is the most readable in tests but couples to copy.
  • Verify the selection afterwards with Select.getFirstSelectedOption() or the trigger’s displayed text; some components swallow the click if the list is still animating.

Learn the theory