The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →For an HTML element with a known ID, use //*[@id='element-id']. It tests the literal id attribute and works in HTML and in many automation contexts. XPath also defines id('element-id'), but that function works only when the XPath processor knows which attribute is typed as an ID. In Selenium, use By.ID for a straightforward lookup and By.XPATH when you need XPath predicates, relationships, or text conditions.
The three XPath forms you need
These expressions can all refer to an element whose ID is login, but they do not have identical portability or behavior.
| Expression | What it tests | Best use | Important limitation |
|---|---|---|---|
id('login') |
The XPath processor’s ID index | Documents and processors with reliable ID typing | Returns nothing when the processor does not know that the relevant attribute is an ID |
//*[@id='login'] |
The literal id attribute on any element |
HTML automation and scraping where portability matters | Can match more than one node if the document contains duplicate IDs |
//input[@id='login'] |
An input element whose literal ID is login |
When the element type is part of the requirement | It will not match the same ID on another element type |
For most HTML pages, start with //*[@id='login'], or qualify it with the expected element name. Use id() only when you know the document and XPath implementation expose the attribute as an ID.
How the XPath id() function works
id() is a function, not a shorthand for an attribute predicate. In XPath 1.0, the document’s type information determines which attributes are IDs. A DTD can declare an attribute as type ID, and XML vocabularies can define an ID attribute whose name is not literally id. If the processor has no such typing information, id('login') may return an empty node-set even though an element visibly has id='login'.
#1 Best Overall
That distinction explains many “XPath id() does not find my HTML element” reports. HTML exposes an id attribute, but an XPath engine is not required to treat every attribute named id as a typed XML ID in every context. The explicit form, //*[@id='login'], asks the engine to test the attribute itself and therefore avoids that dependency.
HTML IDs, XML IDs, case, and uniqueness
HTML
HTML ID values are case-sensitive: login and Login are different values. An ID is intended to be unique in the document. Valid markup should contain one element for a given value, but real pages sometimes repeat IDs because of templates, duplicated components, or invalid generated markup.
XML
XML applications determine ID typing through their document language and declarations. The attribute may have a different name, and an XPath implementation that lacks the relevant type information cannot resolve it through id(). When the typing rules are unknown, use an explicit attribute test such as //*[@data-key='login'] for that vocabulary, or the literal id predicate when that is the attribute you need.
Duplicate IDs
//*[@id='login'] returns every matching node. A DOM convenience method such as getElementById() returns the first match, so it can conceal a duplicate that an XPath query exposes. If your test requires one element, validate uniqueness rather than silently accepting the first result.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsRank #2
- Used Book in Good Condition
Selecting an ID in Selenium
Selenium provides separate locator strategies for IDs and XPath. Choose the direct ID strategy when the value is known and no additional logic is required:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://example.com')
login = driver.find_element(By.ID, 'login')
login.click()
driver.quit()
The equivalent XPath locator is:
from selenium.webdriver.common.by import By
element = driver.find_element(By.XPATH, "//*[@id='login']")
Selenium’s JavaScript By.id implementation uses a CSS selector of the form *[id="$ID"], while By.xpath evaluates the XPath expression. That distinction is normally invisible to your test, but it is useful when diagnosing a locator that behaves differently from a hand-written XPath.
When XPath is better than a direct ID lookup
A direct ID lookup is the simplest choice when the ID is stable. XPath becomes useful when the ID is only one part of the condition.
Restrict the element type
//input[@id='login']
//button[@id='login']
This protects a test from matching an unexpected element that happens to reuse the same ID.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use an ancestor or descendant relationship
//form[@id='account']//input[@name='email']
//*[@id='settings']//button[@type='submit']
These expressions first anchor the search to a stable ID, then express the required relationship. They are more resilient than an absolute path such as /html/body/div[2]/form/input, which depends on the exact layout and position of every ancestor.
Add a text or attribute predicate
//*[@id='account']//button[contains(normalize-space(.), 'Continue')]
//*[@id='results']//a[@aria-current='page']
Use predicates only for conditions that are part of the requirement. If the ID alone identifies the target, adding unnecessary text or position tests makes the locator harder to maintain.
Building XPath values safely at runtime
Host-language quoting can change the XPath before the browser or parser evaluates it. Keep the XPath string separate from the data used to build it, and escape a value according to both the host language and XPath rules. For a fixed value, this Python expression is unambiguous:
element_id = 'login'
xpath = "//*[@id=" + "'" + element_id + "'" + "]"
element = driver.find_element(By.XPATH, xpath)
If an externally supplied ID can contain quote characters, do not concatenate it blindly. Construct an XPath string literal with the XPath concat() function when necessary, or use the locator API’s direct ID strategy when you do not need XPath logic. The important check is that the final XPath sent to Selenium is the expression you intended, not a string altered by host-language quoting.
Recommended Free Tools
A practical selection workflow
- Confirm the exact value and case. Inspect the element and copy the ID exactly;
loginis notLogin. - Check uniqueness. Search the document for the same ID. If multiple nodes exist, decide which element type or ancestor should qualify the XPath.
- Start with the simplest locator. In Selenium, use
By.IDfor a plain ID lookup. In a general XPath context, use//*[@id='value']when ID typing is uncertain. - Add only necessary structure. Add an element name, ancestor, descendant, or predicate when the requirement demands it.
- Test the expression in the same environment as the automation. Different XPath processors can expose different ID-typing information, so a query that works in one tool is not proof that
id()will work everywhere.
Troubleshooting ID-based XPath
id('x') returns no result
The processor probably does not know that the relevant attribute is typed as an ID. Replace it with //*[@id='x'], or use an element-qualified form such as //input[@id='x'].
The expression matches nothing, but the element is visible
Check spelling and case first. ID values are case-sensitive. Then verify that the automation is evaluating the XPath in the document or frame that actually contains the element.
The expression returns several nodes
The page contains duplicate IDs, or your predicate is broader than intended. Qualify the element name or anchor the query to a stable ancestor. Do not rely on “the first” result unless that behavior is explicitly acceptable.
An absolute XPath broke after a redesign
Paths beginning with /html/body and positional steps such as div[2] encode layout details. Replace them with a relative expression anchored to a stable ID and, where needed, a semantic relationship.
Best Value
Selenium rejects the locator string
Inspect the host-language quoting. A quote intended for the XPath may have ended the Python, JavaScript, or other string early. Log the final XPath value and test that exact value in the same browser session.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance and reliability considerations
There is no useful universal speed number for these expressions: the result depends on the browser, XPath engine, document size, and page state. Reliability matters more than micro-optimizing a locator. A direct ID lookup communicates intent clearly; an XPath predicate is justified when it expresses a real relationship or disambiguates duplicate markup.
Prefer stable, semantic anchors over generated class names and absolute positions. If a page’s framework generates a different ID on every render, an ID-based locator is inherently unstable; identify a durable ancestor, attribute, or relationship instead. Conversely, if the ID is stable and unique, adding extra axes and predicates creates more places for a future markup change to break the test.
Or skip the browser setup
If your goal is to obtain a rendered image of a page for documentation, review, or an automated workflow rather than interact with it, ScreenshotNeo can return a screenshot or PDF through one request. Its CSS-selector element capture can target a rendered element, while XPath remains the right choice for XPath-based browser interaction. See the ScreenshotNeo API documentation for parameters and response details.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →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}`);
Before capture, ScreenshotNeo accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether the request was billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for the free plan.
Frequently Asked Questions
Can id() identify more than one element?
Yes. The function can resolve one or more IDs when the processor has ID-typing information. If a document violates the uniqueness convention, inspect all returned nodes rather than assuming one target.
Is //*[@id='x'] limited to HTML?
No. It tests an attribute named id, so it can be used in XML or other documents that use that attribute name. It does not, however, automatically find an XML vocabulary’s differently named ID attribute.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

