How to Get the Name of an HTML Element in XPath
Learn when to use name(), local-name(), and namespace-uri() in XPath, with browser JavaScript, Python, troubleshooting, and practical examples.

Use name() when you need the qualified name (QName) of an element. Use local-name() when you need only the local part, without a namespace prefix. When namespaces matter, combine local-name() with namespace-uri().
For the current context node:
name()
For the first node returned by an XPath expression:
name(//body)
To select elements by local name and namespace independently of the document prefix:
//*[local-name() = 'p' and namespace-uri() = 'http://www.w3.org/1999/xhtml']
This distinction prevents common mistakes with SVG, XHTML, XML feeds, and any document that uses a default namespace.
1. What each XPath function returns
| Expression | Result | Use it when |
|---|---|---|
name() |
QName of the first node, such as div or svg:path |
You need the element name as represented with its prefix |
local-name() |
Local part only, such as div or path |
You want a prefix-independent comparison |
namespace-uri() |
Namespace URI, or an empty string for an unnamespaced node | You need to identify the namespace precisely |
Both name() and local-name() accept an optional node-set argument. XPath uses the first node in document order when the argument contains several nodes. With no argument, the current context node is used. MDN documents these XPath 1.0 semantics for name() and local-name().
Qualified names and prefixes
A qualified name can contain a prefix, a local name, and a namespace binding. The prefix is an alias, so two documents can use different prefixes for the same namespace. The W3C XPath specification describes an expanded QName as a prefix, local name, and namespace URI. Consequently, comparing the literal result of name() is not always portable across documents.
2. Basic expressions you can copy
Get the current element’s name
name()
If the context node is a <section>, the result is the string section. If the context node is an element represented with a prefix, the returned string can include that prefix.

Get the name of the document element
name(/*)
/* selects the document element. Passing that node to name() returns its qualified name.
Get the local name of the document element
local-name(/*)
Get the first matching element’s name
name(//article)
This returns the name of the first article element selected in document order. It does not return a list of every matching name. To process every node, iterate over the node-set in your host language.
Select elements by their name
//*[name() = 'div']
This selects elements whose returned QName is exactly div. It is suitable for an unnamespaced HTML document. For namespace-aware documents, prefer the local-name and namespace-uri form shown below.
3. The namespace-safe form
In XML and XHTML, an element’s identity includes its namespace. A local name such as p can occur in more than one namespace. Match both values:

//*[local-name() = 'p' and namespace-uri() = 'http://www.w3.org/1999/xhtml']
The URI must match the document you are querying. namespace-uri() returns an empty string for an element with no namespace and for non-element or non-attribute nodes.
Why an unprefixed test can fail
In browser XPath, an unprefixed element test such as //p does not automatically mean “the p element in the document’s default namespace.” The XPath evaluator needs a namespace resolver, or you can use explicit local-name() and namespace-uri() predicates. This is a frequent cause of an empty result when querying XHTML or SVG.
When name() is the wrong comparison
Suppose one document writes an element as h:table and another writes the same namespace as name() to h:table depends on the prefix spelling. Comparing the local name and namespace URI avoids that dependency.
4. Browser JavaScript with document.evaluate()
Browsers expose XPath through document.evaluate(). You provide the expression, a context node, a namespace resolver, a result type, and an optional result object. For a string result, request XPathResult.STRING_TYPE:
const result = document.evaluate(
"name(/*)",
document,
null,
XPathResult.STRING_TYPE,
null
);
console.log(result.stringValue);
To get the local name of the element under a particular node:
const heading = document.querySelector('h1');
const result = document.evaluate(
'local-name()',
heading,
null,
XPathResult.STRING_TYPE,
null
);
console.log(result.stringValue);
Iterate over every matching element
const snapshot = document.evaluate(
'//*',
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
for (let i = 0; i < snapshot.snapshotLength; i++) {
const node = snapshot.snapshotItem(i);
console.log(node.tagName, node.localName, node.namespaceURI);
}
name() returns a string for one node. Use a snapshot or iterator when you need to inspect many elements and read each node’s localName or evaluate a function relative to that node.
Namespace-aware browser evaluation
If you know the namespace prefix used in your XPath expression, pass a resolver:
const resolver = prefix => ({
xhtml: 'http://www.w3.org/1999/xhtml',
svg: 'http://www.w3.org/2000/svg'
}[prefix] || null);
const result = document.evaluate(
'//xhtml:p',
document,
resolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
console.log(result.snapshotLength);
When prefixes in the source are unpredictable, use explicit namespace URI predicates instead:
const result = document.evaluate(
"//*[local-name()='path' and namespace-uri()='http://www.w3.org/2000/svg']",
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
5. Python examples
Python’s standard library can parse XML and evaluate basic XPath through ElementTree. The function-call expressions are easiest to evaluate by reading the element properties directly after selecting nodes:
import xml.etree.ElementTree as ET
xml = '''<root xmlns="urn:example">
<item>One</item>
<item>Two</item>
</root>'''
root = ET.fromstring(xml)
print(root.tag) # {urn:example}root
print(root.tag.split('}', 1)[-1]) # root
for item in root.findall('{urn:example}item'):
print(item.tag.split('}', 1)[-1])
For an XPath engine that implements name(), local-name(), and namespace-uri() directly, use a library that supports those XPath functions and check its version-specific documentation. XPath host APIs differ; do not assume browser result types or namespace behavior carry over unchanged.
6. Practical patterns and edge cases
Attributes are not elements
name(@class) returns the qualified name of the attribute node, while name(//div) returns an element name. Text nodes and comments do not have an element namespace; namespace-uri() returns an empty string for non-element and non-attribute nodes.
Empty node-sets
If the argument selects no nodes, the function returns an empty string. Guard against that result when converting it to a required value:
string-length(name(//does-not-exist)) > 0
Case in HTML
HTML element names are conventionally written in lowercase in XPath expressions. XML is case-sensitive, so DIV and div are different names in an XML document. Use the spelling and casing defined by the document and parser.
Comments, text, and processing instructions
name() is primarily useful for element and attribute nodes. If you select comments or text nodes, the result is not an element tag name. Filter by node type when necessary, for example //*[self::*] for elements.
7. A repeatable debugging workflow
- Inspect the context node. Confirm that the evaluator is running against the element you think it is.
- Run
name(). This reveals the QName, including any prefix. - Run
local-name(). Compare the prefix-free value with your expected local name. - Run
namespace-uri(). If it is non-empty, include that URI in your predicate or configure a resolver. - Check the result type. A string expression needs
STRING_TYPE; a collection needs a snapshot or iterator result type. - Test the smallest expression first. Start with
/*, then add predicates and descendant steps one at a time.
8. Troubleshooting common errors
| Symptom | Likely cause | Fix |
|---|---|---|
//p returns nothing |
The document uses a default namespace | Use a namespace resolver or match local-name() and namespace-uri() |
name() returns an unexpected prefix |
The source document chose a different prefix alias | Use local-name() plus namespace-uri() for prefix-independent matching |
| The result is empty | Your node-set contains no nodes or the context node is wrong | Evaluate count(...), name(/*), and a simpler path |
| JavaScript throws a result-type error | The requested XPathResult type does not match the expression |
Use STRING_TYPE for a string and a snapshot type for multiple nodes |
| Only the first name appears | name(node-set) is defined for the first node |
Iterate through a node snapshot and process each node |
| Python syntax differs from browser XPath | XPath support depends on the Python parser/library | Check that library’s supported XPath version and namespace API |
9. Performance, reliability, and maintainability
For a single known node, name() and local-name() are inexpensive. The cost usually comes from selecting a large document with //*, repeatedly evaluating expressions, or transferring a full DOM into a remote process. Narrow the path early, reuse a compiled expression when your library supports compilation, and choose a snapshot when you need a stable collection.
Namespace-aware predicates are more reliable than comparing serialized tag strings. They continue to work when a producer changes prefix aliases. Keep namespace URIs in constants, document why each URI is expected, and add a regression case for documents with a default namespace.
Browser DOMs can change after JavaScript runs. If the page is dynamic, evaluate XPath after the target element exists and after any relevant content has rendered. For automation, wait for a selector or a meaningful state rather than relying only on a fixed delay.
10. Or skip the browser setup
If your goal is to capture a page while inspecting or documenting its elements, ScreenshotNeo provides a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Before capture, it can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled.
Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for all options. A direct call looks like this:
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}`);
Options include full-page capture with lazy images loaded, element capture by CSS selector, dark mode, device presets, custom viewports, retina scale, custom CSS and JavaScript, click and wait actions, hidden selectors, blocked ads or resource types, custom headers and cookies, user agent, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and PDF controls.
An 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 each month with no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
11. FAQ
Does name() return a string?
Yes. It returns the QName of the first node in the supplied node-set, or the context node when no argument is supplied.
Should I use name() or local-name()?
Use name() when the prefix is meaningful to your output. Use local-name() when prefixes can vary or you only need the local component.
How do I match an element in a specific namespace?
Use local-name() together with namespace-uri(), or configure a namespace resolver and use a prefixed XPath expression.
Why does my expression return only one name?
Functions such as name(//item) operate on the first node in the node-set. Select a collection and iterate when you need every name.
Can I use these functions on ordinary HTML?
Yes. In an unnamespaced HTML DOM, name() and local-name() commonly produce the familiar tag name. Still check namespaces when querying XHTML, SVG, or XML-derived content.


