20

I have a node as follows:

<span class="portal-text-medium">Office Hours</span>

For the XPath I use

//span[text()='Office Hours']

which should work, but it never does. I can use *contains(text(),'Office Hours')]* but that won't find an exact match and I have to verify there is no "*". This is not the only time it hasn't worked for me. I have seen it work before so I don't know what is wrong. Any idea?

Yes I can, and do, use starts-with but it is not quite the same.

1
  • I have the page open in Chrome, and I do an inspect and then ctrl-f which opens a box in the lower left corner into which I can type xpaths. If the xpath matches, then the number of matches is shown and the first one is highlighted in yellow. Actually even if no match is found the number of matches is shown, and is shown as 0. When I use this xpath 0 matches are shown. When I use contains() or starts-with(), 14 or so matches are shown. Commented Jan 4, 2016 at 15:03

1 Answer 1

60

XPath text() = is different than XPath . =

(Matching text nodes is different than matching string values)

The following XPaths are not the same...

  1. //span[text() = 'Office Hours']

    Says:

    Select the span elements that have an immediate child text node equal to 'Office Hours`.

  2. //span[. = 'Office Hours']

    Says:

    Select the span elements whose string value is equal to 'Office Hours`.

In short, for element nodes:

The string-value of an element node is the concatenation of the string-values of all text node descendants of the element node in document order.

Examples

The following span elements would match only #1:

  • <span class="portal-text-medium">Office Hours<br/>8:00-10:00</span>
  • <span class="portal-text-medium">My<br/>Office Hours</span>

The following span elements would match only #2:

  • <span class="portal-text-medium"><b>Office</b> Hours</span>
  • <span class="portal-text-medium"><b><i>Office Hours</i></b></span>

The following span element would match both #1 and #2:

  • <span class="portal-text-medium">Office Hours</span>
Sign up to request clarification or add additional context in comments.

7 Comments

So for using a ".", parts of the string can be at different depths, as long as when appended together it comes out to the value you are searching? I think sometimes when I encountered that problem I should have tried I should have used a . because maybe there were some extra levels I did not know about.
You got it. More precisely per the XPath recommendation: The string-value of an element node is the concatenation of the string-values of all text node descendants of the element node in document order. Answer updated. Thanks.
A relevant quote from the XPath Specification: "If one object to be compared is a node-set and the other is a string, then the comparison will be true if and only if there is a node in the node-set such that the result of performing the comparison on the string-value of the node and the other string is true."
so how to only match<span>xxx<span>, not <span>xxx<br/>xxx</span>?
Clearly by testing the string value of the span because the string value of the <span>xxx<span> is xxx while the string value of <span>xxx<br/>xxx</span> is xxxxxx. If you wanted to select all span elements in the document with a string value of xxx, you'd use //span[.='xxx'].
Thank you, but [.='xxx'] also match <span>x<br/>xx</span> right? I just want exactly match <span>xxx</span>
Correct. To match <span>xxx</span> but not <span>x<br/>xx</span> (or <span><b>xxx</b></span> or <span><i>x</i><b>xx</b></span>, etc), use //span[.='xxx' and not(*)] to say, "Select all span elements that have a string value of xxx and no element children."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.