390

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

How can I find it with XPath?

John Slegers
  • 41,615
  • 22
  • 193
  • 161
Strawberry
  • 62,326
  • 55
  • 143
  • 194
  • 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

610

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,711
  • 2
  • 29
  • 37
meder omuraliev
  • 177,923
  • 69
  • 381
  • 426
  • 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
  • 41
    @meder: More like `//div[contains(concat(' ', @class, ' '), ' Test ')]` - Yours will turn up partial matches as well. – Tomalak Oct 22 '09 at 16:32
  • 9
    Why don't you just do //div[@class='Test'] – Jessica Oct 11 '13 at 14:56
  • 14
    Because classes can contain more than one value – meder omuraliev Oct 11 '13 at 16:33
  • 11
    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
191

Most easy way..

//div[@class="Test"]

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

Will Tate
  • 33,169
  • 9
  • 76
  • 71
Olli Puljula
  • 2,157
  • 1
  • 12
  • 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
  • 11
    Does it match `
    too?
    – Jugal Thakkar Sep 27 '16 at 14:26
  • 18
    @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
  • 8
    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
50

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 not 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
  • 41,615
  • 22
  • 193
  • 161
26

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,277
  • 3
  • 37
  • 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
  • @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
6

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
  • 23,305
  • 6
  • 51
  • 61
3

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
  • 2,689
  • 2
  • 12
  • 16
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
  • 6,688
  • 11
  • 61
  • 127
1

Match against one class that has whitespace.

<div class="hello "></div>
//div[normalize-space(@class)="hello"]
Philip
  • 6,369
  • 12
  • 70
  • 102