433

In my webpage, there's a div with a class named Test.

How can I find it with XPath?

John Slegers
  • 45,213
  • 22
  • 199
  • 169
Strawberry
  • 66,024
  • 56
  • 149
  • 197
  • 1
    Related http://stackoverflow.com/questions/8808921/selecting-a-css-class-with-xpath and http://stackoverflow.com/questions/1390568/how-to-match-attributes-that-contain-a-certain-string – Timo Huovinen Mar 31 '14 at 12:39
  • 1
    The more general related XPath,CSS, DOM and Selenium Solutions can be found in document [XPath, CSS, DOM and Selenium: The Rosetta Stone](https://www.simple-talk.com/dotnet/.net-framework/xpath,-css,-dom-and-selenium-the-rosetta-stone/). Specifically, your answer can be found in the item *Id & Name*. – Terence Xie Jun 18 '15 at 06:29

8 Answers8

663

This selector should work but will be more efficient if you replace it with your suited markup:

//*[contains(@class, 'Test')]

Or, since we know the sought element is a div:

//div[contains(@class, 'Test')]

But since this will also match cases like class="Testvalue" or class="newTest", @Tomalak's version provided in the comments is better:

//div[contains(concat(' ', @class, ' '), ' Test ')]

If you wished to be really certain that it will match correctly, you could also use the normalize-space function to clean up stray whitespace characters around the class name (as mentioned by @Terry):

//div[contains(concat(' ', normalize-space(@class), ' '), ' Test ')]

Note that in all these versions, the * should best be replaced by whatever element name you actually wish to match, unless you wish to search each and every element in the document for the given condition.

Teemu Leisti
  • 3,750
  • 2
  • 30
  • 39
meder omuraliev
  • 183,342
  • 71
  • 393
  • 434
  • with my suited markup? Sorry, I'm not sure what did that part mean. – Strawberry Oct 22 '09 at 05:50
  • It means that I gave you a generic non-efficient answer that should work, but because you provided no xml whatsoever I couldn't make it efficient so you would have to suite it towards your markup. And did it work? – meder omuraliev Oct 22 '09 at 05:52
  • 45
    @meder: More like `//div[contains(concat(' ', @class, ' '), ' Test ')]` - Yours will turn up partial matches as well. – Tomalak Oct 22 '09 at 16:32
  • 10
    Why don't you just do //div[@class='Test'] – Jessica Oct 11 '13 at 14:56
  • 18
    Because classes can contain more than one value – meder omuraliev Oct 11 '13 at 16:33
  • 13
    I'm surprised xpath doesn't have a shortcut/more efficient way to locate a token in a space-separated token list. Anything in later versions of xpath? – thomasrutter May 10 '16 at 04:19
  • I updated the answer to specify the that the element is a `div`; that is, instead of `//*[contains...`, it now specifies `//div[contains...`. – Teemu Leisti Sep 06 '16 at 11:48
  • // div[contains(concat(' ', @class, ' '), ' Test ')] is really a good idea, it will escape the case when you only want class 'Test' and class 'Testable' founded – NGloom Sep 12 '16 at 08:36
  • Only the final suggestion with `normalize-space` worked for me. – myol Nov 16 '16 at 12:10
  • Another option I discovered that works in XPath 2.0+ (didn't test 1) is: `//div[tokenize(@class, "\s+") = "Test"]` – Sweet Sheep Feb 13 '19 at 01:31
  • 1
    @thomasrutter why the surprise - this is just a language made for XML, not the more specific HTML, and who's to say it's casual to use space-separated lists as any node value in XML. Tomalak's solution is a very viable one. – bitoolean Aug 04 '19 at 13:26
  • Fair enough. I think Tomalek's solution is the most elegant. Since my comment from 4 years ago I've done that kind of thing a number of times. – thomasrutter Aug 06 '19 at 06:42
  • "the * should best be replaced by whatever element name you actually wish to match, unless (...)" - it should be noted that this advice is very context-dependent. It can be a *really bad idea* to use anything other than * in contexts where the CSS classes are specifically meant to identify elements, while the concrete structure (`
    `? ``? Whatever?) is subject to vary, think automated UI tests and the like.
    – O. R. Mapper Oct 11 '22 at 09:58
219

Most easy way..

//div[@class="Test"]

Assuming you want to find <div class="Test"> as described.

Will Tate
  • 33,439
  • 9
  • 77
  • 71
Olli Puljula
  • 2,471
  • 1
  • 14
  • 8
  • 4
    The above syntax is a lot easier to use and is less error-prone. REMEMBER you need to have the DOUBLE QUOTES around the class to search. I would recommend using the listed above. //div[@class="Test"] – FlyingV Dec 30 '15 at 21:20
  • Does this work for the cases that div[class='Test'] lies in deeper level? – Jake0x32 Mar 06 '16 at 01:36
  • @Jake0x32 Yes, it does. – Olli Puljula Mar 07 '16 at 14:06
  • 1
    @Jake0x32, that's because it uses `//` not just `/`. – Solomon Ucko Jun 01 '16 at 20:59
  • 15
    Does it match `
    too?
    – Jugal Thakkar Sep 27 '16 at 14:26
  • 22
    @JugalThakkar No, it doesn't. It requires an exact match to work but you can try //div[contains(@class,"Test")] instead. – Olli Puljula Oct 31 '16 at 01:08
  • 12
    This answer may benefit from further clarification as it doesn't really answer the OP's question. OP says "a div with a class named Test", but at no point it is suggested that "Test" is the _only_ class in the div, which is what this answer assumes. The simplicity of this answer is appealing, which could lure readers into trouble. – quiram Jun 29 '20 at 10:23
  • @OlliPuljula why in case "@class=..." and in another cases "@class, ..." . What differences in "=" and ","? – Mikhail Barinov Aug 16 '21 at 08:53
65

The ONLY right way to do it with XPath :

//div[contains(concat(" ", normalize-space(@class), " "), " Test ")]

The function normalize-space strips leading and trailing whitespace, and also replaces sequences of whitespace characters by a single space.


Note

If you need many of these Xpath queries, you might want to use a library that converts CSS selectors to XPath, as CSS selectors are usually a lot easier to both read and write than XPath queries. For example, in this case, you could use the selector div.Test to get the exact same result.

Some libraries I've been able to find :

John Slegers
  • 45,213
  • 22
  • 199
  • 169
  • 1
    this is REALLY the only way to avoid issues e.g. if you have more than one class assigned! – Don Diego Jan 28 '21 at 10:56
  • How about `//div[@class[contains(.,'Test')]]`? – user31782 Dec 30 '21 at 17:23
  • @user31782: That would also match `class="NotATest"`. – O. R. Mapper Oct 11 '22 at 10:04
  • "The function normalize-space strips leading and trailing whitespace, and also replaces sequences of whitespace characters by a single space." - none of which should be a problem in the case at hand, as long as we're talking about "normal" whitespace characters (`U+0020`). Removal of other whitespace-like characters that are valid CSS class separators seems to be the actual benefit `normailze-space` brings. – O. R. Mapper Oct 11 '22 at 10:11
  • XPath would be superior to CSS, which STILL doesn't have wide spread ":has()" support, if it weren't for making something as simple as a "element has class x" query THIS complicated... Why the hell doesn't XPath offer a function that does this padding with spaces just to split it up again and then compare the entries? – BloodyRain2k Jan 08 '23 at 18:22
27

I'm just providing this as an answer, as Tomalak provided as a comment to meder's answer a long time ago

//div[contains(concat(' ', @class, ' '), ' Test ')]
Alex Lyman
  • 15,637
  • 3
  • 38
  • 42
  • 3
    Sorry to bring this up from such a time ago but what about `concat(' ', normalize-space(@class), ' ')` to account for all sorts of white-space characters as well? – Terry May 30 '13 at 08:10
  • For a sake of curiosity - Why `//div[contains(concat(' ', @class, ' '), ' Test ')]/chid` does not select children? – Fusion Jul 09 '19 at 09:02
  • @Fusion if you post that as a question, you might get an answer. – bitoolean Aug 04 '19 at 13:28
  • @bitoolean being Captain Cbvious is hard these days – Fusion Aug 12 '19 at 14:30
  • 1
    @Fusion I was just trying to help. XPath is not an HTML-aware language. It's more generic, XML-only. I don't have any experience in it, but I think you're assuming you can just put the id instead of the tag. You need to select on the "id" attribute's value. So you need to think of the HTML document as XML. Off-topic discussions don't help people find solutions though. – bitoolean Aug 14 '19 at 12:07
10

XPath has a contains-token function, specifically designed for this situation:

//div[contains-token(@class, 'Test')]

It's only supported in the latest version of XPath (3.1) so you'll need an up-to-date implementation.

Bennett McElwee
  • 24,740
  • 6
  • 54
  • 63
5

Since XPath 2.0 there is a tokenize-function you can use:

//div[tokenize(@class,'\s+')='Test']

Here it will tokenize on white-space and then compares the resulting strings with 'Test'.

It's an alternative of the XPath 3.1 function contains-token()

But at this moment (2021-04-30) no browser support XPath 2.0 or more.

Siebe Jongebloed
  • 3,906
  • 2
  • 14
  • 19
2
//div[@class[contains(.,'Test')]]

This is what I am using in my current project and it works smooth as.

The dot . in the expression represents the value of class attribute of any div element. So you don't need to use normalize-space and concat. Note this might also select divs with classnames XXXTestXXX. I happen to have my searchable class as infobox-header and the page doesn't have anything like XXinfobox-headerXXXX.

user31782
  • 7,087
  • 14
  • 68
  • 143
1

Match against one class that has whitespace.

<div class="hello "></div>
//div[normalize-space(@class)="hello"]
Philip
  • 6,827
  • 13
  • 75
  • 104