SVG, Namespaces and local-name()
Why //svg and //path return nothing in a browser, how namespaces work in the HTML DOM, and the local-name() idiom for icons, charts and icon buttons.
SVG, Namespaces and local-name()
Inline SVG is everywhere: icon buttons, charts, loaders, checkmarks. And //svg returns nothing. Neither does //path, //circle or //svg//*[@class='icon']. This is not a bug in your locator; it is XML namespaces.
What the HTML parser does
When the browser parses <svg> inside an HTML document, it creates elements in the SVG namespace (http://www.w3.org/2000/svg) rather than the HTML namespace. XPath name tests like svg or path match elements with no namespace, so they miss.
The same applies to MathML (<math>), and to any XML document with a default namespace.
The fix: local-name()
local-name() returns the element name without any namespace, so you can test it in a predicate:
//*[local-name()='svg'] every inline svg
//*[local-name()='path'] every path
//*[local-name()='svg'][@aria-label='Close'] an svg with a label
//*[local-name()='svg']//*[local-name()='circle'] circles inside svgs
name() also works for elements without a prefix, but local-name() is the safer habit because it ignores prefixes like svg:path in XML documents.
Icon buttons
The element you click is the button, not the svg. Anchor on the svg and go up, or use the button’s accessible name:
<button aria-label="Delete">
<svg viewBox="0 0 24 24"><path d="..."/></svg>
</button>
//button[@aria-label='Delete'] best
//button[.//*[local-name()='svg'][@data-icon='trash']] by icon data attribute
//*[local-name()='svg'][@data-icon='trash']/ancestor::button[1] from the icon upward
//button[*[local-name()='svg']] buttons that contain any svg
Icon libraries usually leave a stable hook: Font Awesome sets data-icon, Material adds a class or data-testid, Lucide sets class="lucide lucide-trash". Match on that rather than the path data.
Inside the svg
Attributes on SVG elements are not namespaced (unless prefixed), so @class, @id, @fill and @data-* work as normal once you have matched the element by local name:
//*[local-name()='svg'][@class='chart']//*[local-name()='rect'][@fill='#00D9FF']
//*[local-name()='g'][@data-series='revenue']/*[local-name()='rect']
//*[local-name()='text'][normalize-space()='Q3']
class on an SVG element is a plain attribute string, so contains(@class, ...) works like HTML.
Star ratings and repeated icons
<div class="rating" aria-label="4 out of 5 stars">
<svg class="star filled"/><svg class="star filled"/><svg class="star filled"/><svg class="star filled"/><svg class="star"/>
</div>
count(//div[@class='rating']/*[local-name()='svg'][contains(@class, 'filled')]) 4
(//div[@class='rating']/*[local-name()='svg'])[4] the fourth star
//div[@class='rating'][@aria-label='4 out of 5 stars'] better: assert the label
SVG used as a map or diagram
Clickable regions inside an SVG can be located like any element:
//*[local-name()='path'][@data-region='EU']
//*[local-name()='a'][contains(@href, '/region/eu')] svg links are also namespaced
XML documents with namespaces
When scraping XML feeds or SOAP responses, the same idiom applies, and you can also test the namespace itself:
//*[local-name()='item']
//*[local-name()='price' and namespace-uri()='http://example.com/ns']
Tools like lxml let you register prefixes so you can write //ns:item instead. Browsers do not offer that through document.evaluate without a resolver function, which is why local-name() is the portable choice.
Playwright and CSS alternatives
CSS selectors ignore namespaces, so page.locator('button svg.icon') works where XPath needs local-name(). Inside a mostly-XPath codebase, local-name() keeps everything in one language; in Playwright you may prefer a role locator (getByRole('button', { name: 'Delete' })) and never touch the svg at all.
Try It Yourself
Open in Playground →
Compare with //svg on the same sample and confirm it returns zero matches.
Next Steps
- Node Types and Wildcards - The
*wildcard this pattern relies on - Shadow DOM and iframes - The other two things XPath cannot see through
- XPath in Playwright - When to hand off to role locators