Find last element in dynamically changing xpath - xpath

//div[text()='abc']
It gives sometime 10 count, next time 20 and so on.
How can I find the last element in count even if the count changes dynamically?

Welcome to SO. You have to use last() xpath function to return a number equal to context size of the expression evaluation context, meaning it will get the size of the matching elements and you can access the last item by using the below standard xpath notation.
Rather specifying [1] or [5] you are passing the last item number.
(//yourxpath)[last()]
In your case the xpath will be
(//div[text()='abc'])[last()]
Here is the link with more explation
https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms256083(v%3Dvs.100)

Related

Within robot framework find an xpath within an element variable

I have currently got a situation where I need to get all elements on a page, iterate through the elements 1 by 1 and within each element, I want to see if a certain other element exists.
To make it more clear, this is my loop:
${elements} = Get WebElements //li[#class='product-item--row js_item_root ']
FOR ${item} IN #{elements}
This is the part where I need to check within ${item} if the xpath exists:
//*[contains(text(), '${companyname}')]
END
So basically, I have 24 elements on my page which have the xpath
//li[#class='product-item--row js_item_root ']
I have 1 element on my page which can be located by xpath
//li[#class='product-item--row js_item_root ']//*[contains(text(), '${companyname}')]
And I want to know, within the 24 elements, which place the element is located which contains
//*[contains(text(), '${companyname}')]
Hoping someone can help!
Edit
This does not work:
Element should be visible ${item}//*[contains(text(), 'BargainsKing')]
And that's because:
Element with locator '<selenium.webdriver.remote.webelement.WebElement (session="4b40581d8835aac628e3a2032e355ee5", element="663438f7-76eb-4801-b255-021a865035dd")>//*[contains(text(), 'BargainsKing')]' not found.
Edit
I found the
${item.get_attribute('innerHTML')}
Now my next/final question is, can I look up an xpath within this innerHTML?
A solution is to count the number of elements and then get a FOR to cycle all elements
${count} Get Element Count XmlLocatorForAllElements
FOR ${i} IN RANGE 1 ${count}
${tmpElement} Get Element Count XmlLocator[${i}]/WithCompany
IF ${tmpElement} > ${0}
#element found
END
END
Could you please provide more detail about the selectors?
For now, I think you should try this:
You can check in the selector if there are any incremental values
if not then add a counter for each row
for selecting a specific column you can either go with the counter or the company name in Xpath

XPATH Last element with certain class

I have a date picker on my website,
It contains a list of elements for each week
and then those include 7 elements for each day
2930311234
Now I'm trying to xpath find the last button with class "is-selected"
And I'd also like to go trough each week since I want the last possible date ( "is-selected" means avaible)
I've tried
.//div[#'available-dates-calendar']//table/?[last()='True']//button[last()='True']
But that gave me the first element...
For the last() condition to work, I think that you would have to use the spy to create a XPath query that finds all buttons.
When such a query is found, then you can use the last() condition to find the last in the list of items found.
Hope this helps.
I believe you can use the [-1] identifier to grab the last element. So, instead of [last()='True'], use [-1].

finding the current position of xpath match

I'm trying to get the current position of a xpath match. Here is a real world example
on this page http://newyork.backpage.com/homes-for-sale/
running the following xpath matches the 8th listing counting from top
//div[contains(#class, 'cat 93893742')]
I want to somehow get the ad position using xpath which at the time of posting this question is "8". I tried using prececeding-sibling::div but I am getting unexpected results.
Anyway to achieve this with xpath?
I'm not sure wether current version of htmlunit supports XPath 2.0, but if so you can use below expression:
index-of(//div[starts-with(#class, "cat")], //div[#class='cat 93893742'])
This will return 10 - position in common list
If you want to get position in list for specific date (Thu. May. 11) you can try:
index-of(//div[normalize-space()="Thu. May. 11"]/following::div[starts-with(#class, "cat")],//div[normalize-space()="Thu. May. 11"]/following::div[#class='cat 93893742'])
which returns 8
Some description added in #har07 answer
I think this is what you required
count(//div[contains(#class, 'cat 93893742')]/preceding-sibling::div[starts-with(#class,'cat')])+1
Lets breakdown the whole
//div[contains(#class, 'cat 93893742')]
will match the required context node which have classname = cat 93893742
/preceding-sibling::div[starts-with(#class,'cat')]
Will match the all div element starts with classname=cat just before your context node
So if we keep all those in count() it will count all div tag just before the context node So add 1 to include count of context node as well
If you want to point that element by using index like calculated above then add this
//div[starts-with(#class,'cat')][count(//div[contains(#class, 'cat 93893742')]/preceding-sibling::div[starts-with(#class,'cat')])+1]
Equal to
//div[starts-with(#class,'cat')][10] // 10 in index number
Based on this and your previous question, maybe the following XPath is what you're looking for :
count(
//div[contains(#class, 'cat 93893742')]/preceding-sibling::div[contains(#class, 'cat ')]
)+1

Take the length of the xpath

I have a list which I can take their values using the following xpaths:
.//*[#id='LayoutWrapper']/div[6]/div[3]/main/section[3]/div/div/div[1]/xpl-result/div/div[1]/div[1]/div[1]/span
.//*[#id='LayoutWrapper']/div[6]/div[3]/main/section[3]/div/div/div[2]/xpl-result/div/div[1]/div[1]/div[1]/span
.//*[#id='LayoutWrapper']/div[6]/div[3]/main/section[3]/div/div/div[3]/xpl-result/div/div[1]/div[1]/div[1]/span
.//*[#id='LayoutWrapper']/div[6]/div[3]/main/section[3]/div/div/div[4]/xpl-result/div/div[1]/div[1]/div[1]/span
is it possible to use a xpath command to take the total number of list which is in the previous case in 4?
The number of items selected by an XPath expression, xp, is given by count(xp).

BaseX XQuery error: root(): no context value bound

I am trying to run the following XQuery expression in BaseX to extract elements between two succeeding headings. (as an article section).
xquery for $x in doc("test.xq")//h2,
$y in $x/following-sibling::h2[1]
return //*[$x/following::* and $y/preceding::*]
But it gives the error
Error:
Stopped at D:/Program Files/BaseX/data/test.xq, 1/74:
[XPDY0002] root(): no context value bound.
By the expression I mean if $x is heading and $y is the first heading following $x, then select the common text for $x/following::* and $y/preceding::*
However I am not sure my expression works, but my question here is how can execute my intended query without error?
If you have also an expression which works for my need, that is welcomed.
[...] to extract elements between two succeeding headings [...]
You need something more like:
for $x in doc("test.xq")//h2
return $x/following-sibling::*[preceding-sibling::h2[1] is $x]
but on its own it won't give you anything useful because the XPath and XQuery data model only has flat sequences, not "multi-dimensional arrays". When you have a for that returns a sequence of values for each "iteration", the overall result of the for expression is the concatenation of all the result sequences, so as written above this expression will simply return you all the elements in every "section" in a single flat list. If you want to group the elements by section then you'd need to construct a new XML element for each group
for $x in doc("test.xq")//h2
return
<section>{$x/following-sibling::*[preceding-sibling::h2[1] is $x]}</section>
The error (as documented here) comes from this expression:
//*[$x/following::* and $y/preceding::*]
which begins with //. The abbreviation // stands for /descendant-or-self::node()/, which of course begins with /. The XPath standard says:
A / by itself selects the root node of the document containing the
context node. If it is followed by a relative location path, then the
location path selects the set of nodes that would be selected by the
relative location path relative to the root node of the document
containing the context node.
But from what you've shown us, there is nothing indicating that you've established a context node. So XPath doesn't have any way to know what document contains the context node. That's what the error message is referring to when it says
root(): no context value bound
To fix the error, you could precede the // with an explicit doc(...) or any other explicit way to set the context:
doc("test.xq")//*[$x/following::* and $y/preceding::*]
or
root($x)//*[$x/following::* and $y/preceding::*]
This should get rid of the error, but as Ian Roberts has written, it won't give you the result you want. See his answer for that.

Resources