I'm trying to use XPath to find an element containing a piece of text, but I can't get it to work....
WebElement searchItemByText = driver.findElement(By.xpath("//*[#id='popover-search']/div/div/ul/li[1]/a/span[contains(text()='Some text')]"));
If I remove the last bit with the "contains" thing, it locates my span element, but I need to select it based on the text contents. It's not going to be a perfect match of 'Some text' either, because it might contain truncated strings as well.
What is the issue?
I think the problem is here:
[contains(text()='Some text')]
To break this down,
The [] are a conditional that operates on each individual node in
that node set -- each span node in your case. It matches if any of the individual nodes it operates
on match the conditions inside the brackets.
text() is a selector
that matches all of the text nodes that are children of the context
node -- it returns a node set.
contains is a function that operates
on a string. If it is passed a node set, the node set is converted
into a string by returning the string-value of the node in the
node-set that is first in document order.
You should try to change this to
[text()[contains(.,'Some text')]]
The outer [] are a conditional that operates on each individual node
in that node set text() is a selector that matches all of the text
nodes that are children of the context node -- it returns a node
set.
The inner [] are a conditional that operates on each node in that
node set.
contains is a function that operates on a string. Here it is passed
an individual text node (.).
Use this
//*[#id='popover-search']/div/div/ul/li[1]/a/span[contains(text(),'Some text')]
OR
//*[#id='popover-search']/div/div/ul/li[1]/a/span[contains(.,'Some text')]
#FindBy(xpath = "//button[#class='btn btn-primary' and contains(text(), 'Submit')]") private WebElementFacade submitButton;
public void clickOnSubmitButton() {
submitButton.click();
}
Use:
#FindBy(xpath = "//span[#class='y2' and contains(text(), 'Your Text')] ")
private WebElementFacade emailLinkToVerifyAccount;
Related
I'm trying to use XPath to find an element containing a piece of text, but I can't get it to work....
WebElement searchItemByText = driver.findElement(By.xpath("//*[#id='popover-search']/div/div/ul/li[1]/a/span[contains(text()='Some text')]"));
If I remove the last bit with the "contains" thing, it locates my span element, but I need to select it based on the text contents. It's not going to be a perfect match of 'Some text' either, because it might contain truncated strings as well.
What is the issue?
I think the problem is here:
[contains(text()='Some text')]
To break this down,
The [] are a conditional that operates on each individual node in
that node set -- each span node in your case. It matches if any of the individual nodes it operates
on match the conditions inside the brackets.
text() is a selector
that matches all of the text nodes that are children of the context
node -- it returns a node set.
contains is a function that operates
on a string. If it is passed a node set, the node set is converted
into a string by returning the string-value of the node in the
node-set that is first in document order.
You should try to change this to
[text()[contains(.,'Some text')]]
The outer [] are a conditional that operates on each individual node
in that node set text() is a selector that matches all of the text
nodes that are children of the context node -- it returns a node
set.
The inner [] are a conditional that operates on each node in that
node set.
contains is a function that operates on a string. Here it is passed
an individual text node (.).
Use this
//*[#id='popover-search']/div/div/ul/li[1]/a/span[contains(text(),'Some text')]
OR
//*[#id='popover-search']/div/div/ul/li[1]/a/span[contains(.,'Some text')]
#FindBy(xpath = "//button[#class='btn btn-primary' and contains(text(), 'Submit')]") private WebElementFacade submitButton;
public void clickOnSubmitButton() {
submitButton.click();
}
Use:
#FindBy(xpath = "//span[#class='y2' and contains(text(), 'Your Text')] ")
private WebElementFacade emailLinkToVerifyAccount;
<a>
This is <var>Me</var> and That is <var> You</var>
</a>
I can find an element "a" which contains "This is" by following code:
//a[contains(text(),'This is')]
But I am not able to find element "a" which contains "This is Me and That is You".
//a[contains(text(),'This is Me and That is You')]
Is there a way to find an element with children text as well?
I am not sure if this what you need but you can use string() to get the result as required,
//a[string()='This is Me and That is You']
The caveat however will be that you need to have precised information about the String being used.
See working example here.
This also can be find using normalize-space() function of xpath which strips leading and trailing white-space from a string, replaces sequences of whitespace characters by a single space, and returns the resulting string as below :-
//a[normalize-space()='This is Me and That is You']
My question is about specifics of using dot and text() in XPath. For example, following find_element lines returns same element:
driver.get('http://stackoverflow.com/')
driver.find_element_by_xpath('//a[text()="Ask Question"]')
driver.find_element_by_xpath('//a[.="Ask Question"]')
So what is the difference? What are the benefits and drawbacks of using . and text()?
There is a difference between . and text(), but this difference might not surface because of your input document.
If your input document looked like (the simplest document one can imagine given your XPath expressions)
Example 1
<html>
<a>Ask Question</a>
</html>
Then //a[text()="Ask Question"] and //a[.="Ask Question"] indeed return exactly the same result. But consider a different input document that looks like
Example 2
<html>
<a>Ask Question<other/>
</a>
</html>
where the a element also has a child element other that follows immediately after "Ask Question". Given this second input document, //a[text()="Ask Question"] still returns the a element, while //a[.="Ask Question"] does not return anything!
This is because the meaning of the two predicates (everything between [ and ]) is different. [text()="Ask Question"] actually means: return true if any of the text nodes of an element contains exactly the text "Ask Question". On the other hand, [.="Ask Question"] means: return true if the string value of an element is identical to "Ask Question".
In the XPath model, text inside XML elements can be partitioned into a number of text nodes if other elements interfere with the text, as in Example 2 above. There, the other element is between "Ask Question" and a newline character that also counts as text content.
To make an even clearer example, consider as an input document:
Example 3
<a>Ask Question<other/>more text</a>
Here, the a element actually contains two text nodes, "Ask Question" and "more text", since both are direct children of a. You can test this by running //a/text() on this document, which will return (individual results separated by ----):
Ask Question
-----------------------
more text
So, in such a scenario, text() returns a set of individual nodes, while . in a predicate evaluates to the string concatenation of all text nodes. Again, you can test this claim with the path expression //a[.='Ask Questionmore text'] which will successfully return the a element.
Finally, keep in mind that some XPath functions can only take one single string as an input. As LarsH has pointed out in the comments, if such an XPath function (e.g. contains()) is given a sequence of nodes, it will only process the first node and silently ignore the rest.
There is big difference between dot (".") and text() :-
The dot (".") in XPath is called the "context item expression" because it refers to the context item. This could be match with a node (such as an element, attribute, or text node) or an atomic value (such as a string, number, or boolean). While text() refers to match only element text which is in string form.
The dot (".") notation is the current node in the DOM. This is going to be an object of type Node while Using the XPath function text() to get the text for an element only gets the text up to the first inner element. If the text you are looking for is after the inner element you must use the current node to search for the string and not the XPath text() function.
For an example :-
<a href="something.html">
<img src="filename.gif">
link
</a>
Here if you want to find anchor a element by using text link, you need to use dot ("."). Because if you use //a[contains(.,'link')] it finds the anchor a element but if you use //a[contains(text(),'link')] the text() function does not seem to find it.
Hope it will help you..:)
enter image description here
The XPath text() function locates elements within a text node while dot (.) locate elements inside or outside a text node. In the image description screenshot, the XPath text() function will only locate Success in DOM Example 2. It will not find success in DOM Example 1 because it's located between the tags.
In addition, the text() function will not find success in DOM Example 3 because success does not have a direct relationship to the element . Here's a video demo explaining the difference between text() and dot (.) https://youtu.be/oi2Q7-0ZIBg
I am trying to build an XPath for a property that is constantly changing. The number prefix is bound to change sometimes.
Original:
//*[#id="MainContent_DXEditor3_I"]
which I can query using
$x('//*[#id="MainContent_DXEditor3_I"]
Intended use: I would like to build the string to handle any number in the sub-string. Example: if the property changes to 'MainContent_DXEditor33_I' or 'MainContent_DXEditor8_IXYZ' - I still want to be able to find the element without having to rebuild
You can try to relax the predicate by using starts-with() :
//*[#starts-with(#id, "MainContent_DXEditor")]
You should try to identify a unique parent of the element or save xpath as a string that contains a variable.
These are the 2 possible solutions.
A general selector will return multiple elements, if you identify a unique parent then you are closer and after that you can select any first, second.. last if you have a list.
How to count the number of elements are matching with for the given xpath expression
xpath: driver.findElement(By.xpath("//div[contains(#id,'richedittext_instance')]"))
all i need is the count.
Try this code:
//Assume driver is intialized properly.
int iCount = 0;
iCount = driver.findElements(By.xpath("Xpath Value")).size());
The iCount has the number of elements having the same xpath value.
Another option
If you are basing your requirements strictly on the need to use Selenium, you might be able to do something like this using WebElements and getting the size of the returned list:
List<WebElement> myListToCheck=currentDriver.findElements(By.xpath("somePath"));
if(myListToCheck.size()>0){
//do this
}else{
//do something else
}
Or just simply returning the size of the returned list; if that's all you really want to get from it...
int mySize=myListToCheck.size()
I believe once you have an established WebElements list, you can also use iterators to go over that list.
Helpful, I dunno... just providing another way to get to the same end-game.
Not working in Selenium, which only allows to return nodes from XPath, not primitives like the number returned by count(...). Kept for reference and is valid for most other tools offering a more complete XPath API.
You should only return least possible amount of data from the query. count(//div[contains(#id,'richedittext_instance')]) counts the number of results within XPath and thus is faster as all the elements do not have to be passed from the XPath engine to Selenium.
I can't help you with how to fetch this as n int out of selenium, but this should be easy stuff.
Do the following:
from selenium.webdriver.common.by import By
elements = driver.find_elements(By.XPATH, "Your_XPath")
This outputs a list of selenium.webdriver.firefox.webelement.FirefoxWebElements (in my Firefox browser).
Finally, find out the length of the list:
len(elements)
NB.: Please note that I have written find_elements() (plural) and NOT find_element(). Both of them are different. find_element() only returns the first matched web element, but to find the list of all the matched web elements, we have to use find_elements().