There's a whole ton of stuff XPath can do that (maybe forever) fall outside of what CSS selectors are designed to do. With CSS it's like you only ever have access to the descendant-or-self axis, to being able to select previous siblings, parents, ancestors, and navigating the DOM in any way other than down or to following siblings is a huge win.
But XPath also has functions which let you build selectors using logic. CSS has some selectors like this, like :nth-child() that have some kind of awareness of the placement of an element compared to its siblings, but XPath makes it so easy to:
- select elements with a partial tag name match
- select elements with a partial attribute name match
- select elements by matching their inner text content
- select elements by comparing their attribute values as numbers (like
>, <, >= and <= where CSS can only compare as strings)
- selecting elements by other elements it contains (like
:has() in CSS, except it's supported by browsers)
So I don't think anybody is going to replace writing CSS selectors with XPath for those selectors that can be written in CSS…but if your only option for targeting some elements in your document is via JS or XPath…sometimes XPath will be simpler of the two options.
Consider these examples. Suppose we have HTML with a custom tag name in it. CSS can select elements by their full tag name, but let's say we have a range of different custom elements that each begin with a certain part, but have different endings. There's absolutely no way CSS can help us here, so our options are: JS or XPath.
Here's some sample HTML
<custom-1>Custom 1 tag</custom-1>
<custom-2>Custom 2 tag</custom-2>
<custom-3>Custom 3 tag</custom-3>
In order to target that with JS we basically have to:
- find all tags in the document
- filter them by their
outerHTML
- make sure the first few letters of the
outerHTML of our tag match the partial tagname we're looking for:
Array.from(document.querySelectorAll('*'))
.filter(tag => tag.outerHTML.substring(0,8) === '<custom-')
Now that's not the worst thing ever, but here's the equivalent XPath:
//*[starts-with(name(), 'custom-')]
So I think there will be plenty of use cases where using XPath will simplify matching tags with JS!