Click Button by Text Using Python and Selenium (2026 Guide)

When I’m debugging flaky UI tests, the failure often boils down to one tiny detail: the test clicked the wrong element because the locator was too clever or too brittle. The simplest fix is usually the most obvious one—click the button by the text you can actually see. It’s the same reason you’d tell a teammate “click the Sign In button” rather than “click the third blue rectangle.” Your automation should speak the same language as the UI.

In this post I walk through how I click buttons by visible text in Python + Selenium in 2026, what has changed in Selenium 4, and when text-based locators are the right choice. I’ll show complete, runnable examples with explicit waits, explain why the old findelementbylinktext call is now legacy, and give you practical fallbacks for buttons that are not links. I’ll also cover the pitfalls—localization, dynamic labels, shadow DOM, and duplicate text—and how I keep these tests stable in real projects. If you want reliable UI automation that reads like a human instruction, this is the playbook I use.

Why click-by-text still matters in 2026

I still reach for text-based clicks because they map to what a user sees. When the UI says “Pay Invoice,” that phrase is the truth a user and a tester share. If I can anchor my automation to that text, I gain two big benefits: readability and resilience. Readability is obvious—your test is a sentence. Resilience is more subtle—text often survives layout refactors, class renames, and CSS rewrites.

That said, text is not a silver bullet. It’s best when:

  • The visible label is stable and user-facing.
  • The UI is not heavily personalized.
  • You want tests that double as documentation.

It’s a poor fit when:

  • The text changes based on locale or A/B testing.
  • The UI is built from icons only.
  • The button text is generated dynamically or hidden behind animation.

I think of it like finding someone in a crowd. If you know their name (text), you can find them quickly. If everyone is wearing the same outfit (classes), you’ll grab the wrong person.

Selenium 4 locator rules: modern APIs and legacy calls

Selenium 4 tightened the locator API. The old findelementbylinktext and friends are now legacy. They may still work in older environments, but I treat them as compatibility only. The modern style is explicit and consistent:

from selenium.webdriver.common.by import By

link = driver.findelement(By.LINKTEXT, "Sign In")

Why I prefer this style:

  • It’s the same API across languages, which matters if your team mixes Python with Java or JS.
  • It’s explicit about the locator strategy, which makes code review easier.
  • It aligns with Selenium’s future direction, so you won’t fight deprecation warnings.

If you’re working with older code, you can keep it running, but I recommend migrating as you touch tests. The migration is trivial, and it’s a low-effort way to keep your suite future-proof.

Here’s a quick comparison I use with teams:

Traditional approach

Modern approach

findelementbylinktext("Sign In")

findelement(By.LINKTEXT, "Sign In")

Implicit locator strategy

Explicit locator strategy

Quiet deprecations

Supported long-termThat table is not just style talk. When Selenium updates, the explicit By.* style is the first to get fixes. That’s where you want to be.

Setup that works on real machines (not just demos)

When I set up Selenium for a team in 2026, I try to reduce the number of moving parts. You still need a browser driver, but modern Selenium releases ship with Selenium Manager, which can download a compatible driver on the fly. If your environment blocks auto-downloads, install the driver manually and put it on PATH.

Minimal setup I recommend:

  • Python 3.10+ (3.12 or 3.13 is common in 2026)
  • selenium installed via pip
  • A consistent driver strategy (Selenium Manager or a pinned driver in CI)

Here’s a simple, runnable setup that relies on Selenium Manager and avoids hardcoded paths:

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

options = Options()

options.add_argument("–start-maximized")

# Selenium Manager resolves the driver if needed

driver = webdriver.Chrome(options=options)

If you must pin the driver explicitly, I still keep it simple and readable:

from selenium import webdriver

# Keep drivers in a predictable location or add them to PATH

driver = webdriver.Chrome(executable_path="./drivers/chromedriver")

In CI, I often add a headless option and a fixed window size so layout is predictable. That removes a class of flaky tests caused by responsive layouts.

Click a link by visible text (complete example with waits)

The cleanest case is a link or button that actually uses text. In that case, By.LINK_TEXT is exactly what you want. The main thing I add is an explicit wait. Explicit waits are the difference between a test that passes once and a test that passes all week.

Here’s my go-to pattern:

import time

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

URL = "https://example.com/login"

options = webdriver.ChromeOptions()

options.add_argument("–start-maximized")

driver = webdriver.Chrome(options=options)

wait = WebDriverWait(driver, 10)

driver.get(URL)

# Wait until the link text is visible and clickable

sign_in = wait.until(

EC.elementtobeclickable((By.LINKTEXT, "Sign In"))

)

sign_in.click()

# Optional: small pause when demoing locally

time.sleep(1)

driver.quit()

Why I like elementtobeclickable over presenceofelementlocated:

  • It waits for visibility and enabled state, which mirrors a real click.
  • It avoids clicks blocked by overlays.

Performance-wise, explicit waits usually add negligible overhead. On fast pages they return quickly, often in 10–30ms. On slower pages they prevent the “click too early” problem that is far more costly than a small wait.

Buttons that aren’t links: XPath text selectors that stay sane

Many modern UIs render buttons as

Scroll to Top