ScreenshotNeo

BlogHow-to

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.

By the ScreenshotNeo team30 September 20263 min read

How to Get the Name of an HTML Element in XPath

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.

The same XPath selection can expose a qualified name, a local name, or a namespace URI.
The same XPath selection can expose a qualified name, a local name, or a namespace URI.

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:

Namespace-aware matching keeps working when a document changes its prefix aliases.
Namespace-aware matching keeps working when a document changes its prefix aliases.
//*[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 . A predicate comparing 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

  1. Inspect the context node. Confirm that the evaluator is running against the element you think it is.
  2. Run name(). This reveals the QName, including any prefix.
  3. Run local-name(). Compare the prefix-free value with your expected local name.
  4. Run namespace-uri(). If it is non-empty, include that URI in your predicate or configure a resolver.
  5. Check the result type. A string expression needs STRING_TYPE; a collection needs a snapshot or iterator result type.
  6. 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.