4

In Flipkart page, I searched for Apple ipads and clicked on first search result. Here, I wanted to click on 'BUY NOW'.

Typical page looks like this:

Typical Facebook Apple iPad page

I used the following xpath and it worked:

//button[text()='BUY NOW']

Then I used the xpath with 'contains' but it didn't work:

//button[contains(text(),'BUY NOW')]

Also this:

//button[contains(text(),'BUY')]

But none worked. I really surprised as to why 'contains' is not identifying the elements. I found the same behavior with 'Add to Cart' button as well.

Then I tried to identify another element - 'Wi-Fi+4G' with contains and it worked:

//a[contains(text(),'Wi-Fi+4G')]
//a[text()='Wi-Fi+4G']

Both the above xpath worked for me.

So, can anyone please explain why 'contains' is not working here?

2
  • Can you share html of the elements?
    – Alexey R.
    Dec 18, 2018 at 13:45
  • <button class="_2AkmmA _2Npkh4 _2kuvG8 _7UHT_c" type="button"><span class="_279WdV"></span> <!-- -->BUY NOW</button>. This is the html of the button. Dec 18, 2018 at 13:55

3 Answers 3

7

It is an interesting question.

Let me share my findings. First of all //button[contains(text(),'BUY NOW')] doesn't really look up the element in Chrome dev tools. But //button[contains(.,'BUY NOW')] does the trick.

Check this thing:

enter image description here

This is comment element.

If to copypaste form to this site and try to apply this xpath, it will show up the following error

Unable to perform XPath operation. A sequence of more than one item is not allowed as the first argument of contains() ("", "BUY NOW")

So let's check what text() function returns for the element, having noticed that //button[contains(.,'BUY NOW')] works, lets try to process the following: //button[contains(.,'BUY NOW')]/text(). It turns out that text() returns two text elements which is not the correct input for contains() function.

Unlike this button, [Wi-Fi+4G] does not have such the property (text() function will return the only single element for that). This is why contains works perfectly for that button.

Summing up (mostly from this discussion on SO):

  • text() function returns all the text nodes under the specified element. Comment tag breaks inner text into several nodes
  • There is a function that ignores the node structure but just converts inner text nodes into a single string, It is called string()
  • You can omit using string() function when use contains() but just use contains(., 'your text') since . is automatically taken as string(.)
1

In XPath 1.0, contains(NS, 'string') where NS is a node-set tests whether the first node in NS contains the supplied string. In your case, text() is selecting several text nodes (one before the comment and one after), and only the first is considered.

This is why we advise against using text() except in very special circumstances. It's nearly always better to use ., for example //button[contains(., 'label')]. This will return true if any of the text nodes contains 'label', or even if the string is split across several text nodes.

The rule for '=' is different: NS = 'string' returns true if ANY node in the node-set is equal to the supplied string.

In XPath 2.0, contains() and similar functions throw an error if you supply a node-set containing multiple nodes.

0

enter image description here

Use this xpath: .//button[contains(.,'BUY NOW')] CSS path: form>button

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.