2

In the following HTML snippet:

<div><p class="a b c"></p> <p class="a c"></p> <p class="d"></p> </div>

I want to select only the second p element (with the a class and without the b class) using not selector as follows:

p.a:not(.b)

The xpath counterpart is

.//p[contains(concat(' ', normalize-space(@class), ' '), ' a ')][not(self::*[contains(concat(' ', normalize-space(@class), ' '), ' b ')])].

But when I use that xpath expression, it cannot locate the element.

Is there something else that I can use to achieve the effect of not selector?

Update: It seems I have made a mistake, because p.a:not(.b) and its xpath counterpart do seem to work perfectly for XML::LibXML.

It didn't work because I got one of the if condition wrong…

jonah_w
  • 972
  • 5
  • 11

1 Answers1

5

To get what you want just use this XPath:

//p[@class[not(contains(.,"b"))]]

In other words : select a p element with a @class attribute that doesn't contain a "b".

EDIT : To be more specific (class has to contain a but not b) :

//p[@class[contains(.,"a")] and not(@class[contains(.,"b")])]
E.Wiest
  • 5,425
  • 2
  • 7
  • 12
  • Thanks Wiest. I have updated the original post: In my current situation, I have to specify an xpath that contains one class and avoids one class. – jonah_w Apr 05 '20 at 02:58
  • OK. Post is edited. I hope I fully understand this time. – E.Wiest Apr 05 '20 at 03:22
  • As it turns out, `p.a:not(.b)` and its xpath counterpart work fine for `XML::LibXML` (I have re-edited the original post again). And I have tried your answer - it works perfectly too. Thanks Wiest. – jonah_w Apr 05 '20 at 04:41