I got an issue reading XPath. Need some help/advise from experts.
Part of my HTML is below:
<div class = "input required_field">
<div class="rounded_corner_error">
<input id="FnameInput" class="ideField" type="text" value="" name="first_name>
<div class ="help-tooltip">LOGIN BACK TO MAIN</div>
<div class="error-tooltip">
I need to find the XPath of the text message (LOGIN BACK TO MAIN)
Using Firebug I find the XPath
("//html/body/div/div[5]/div/div/form/fieldset/div/div[2]/div[2]/div/div");
But using above XPath I can read only class = help-tooltip but I need to read LOGIN BACK TO MAIN.
Try adding /text() on the end of the xpath you have.
It does not really look like your XPath matches your XHTML element.
You should try something simpler and more generic, such as:
//div[#class="help-tooltip"]/text()
See Selecting a css class with xpath.
I would use:
# Selecting the div element
//input[#id="FnameInput"]/following-sibling::div[#class="help-tooltip"]
# Selecting the text content of the div
//input[#id="FnameInput"]/following-sibling::div[#class="help-tooltip"]/text()
…since a syntactically-valid HTML document will have a unique id attribute, and as such that's a pretty strong anchor point.
Note that the latter expression will select the text node, not the text string content of that node; you need to extract the value of the text node if you want the string. How you do that depends on what tools you are using:
In JavaScript/DOM that would be the .nodeValue property of the text node.
For Nokogiri that would be the .content method.
…but I have no idea what technology you are using your XPath with.
Related
A document has several <div class="ok"> tags. I am able to select all of them with
"//*[#class="ok"]" (i don't have to specify div, because only div tags have this class). I get a list of 6 nodes matching this.
Now, i need
either to test each node in order to see if it includes the tag <a href="soft://an.id/">. This inclusion is not direct. I mean, the <div> includes a <table> with many <tr> and <td> and <span>, and the <a..> (only one, or none) somewhere before </div>.
or to directly select only (div) nodes of class="ok" that include this <a> tag.
I have tried many things, that all fail. Including protecting the "/" in the href detection (is it required?).
I am quite familiar with regular expressions, but i must confess that i find XPath syntax even harder to understand.. And the W3C reference documents are so hard, without examples..
Any hints are welcome.
In order to select only <div class="ok"> element containing <a href="soft://an.id/"> child element you can use the following XPath locator:
"//div[#class='ok' and .//a[#href='soft://an.id/']]"
If I understand you correctly, you have a nested somewhere under the div with class "ok", right?
So in xpath, the a / is meant for a direct locator under/above the current tag. If you are looking for the somewhere under the found div, you need to use:
//div[#class="ok"]//a[#href="soft://an.id/"]
Then you need to check if it exists or not by using some kind of an assertion.
This was the html code earlier for label 'Home Page' which style attribute
<label style="background: rgb(204, 136, 136); border: 2px solid red;">
<i class="fa fa-info-circle info"></i> Home Page</label>
I had written this XPath earlier which worked well
//*[contains(text(),'Home Page')]
Now I noticed html is changed for label Home Page, style attribute is removed from label as shown below
<label>
<i class="fa fa-info-circle info"></i> Home Page</label>
Because of this changes my existing XPath is not working now, but when I change XPath as shown below, it works
//label[contains(.,'Home Page')]//removed * with label, text() with dot(.)
Also when I use the previous XPath in XPath checker with * none of element is selected now but when I use second XPath with label it select the Home Page label I want.
I think both XPath should have worked, addition or removal of style attribute in label should not have any impact. Can anyone please explain why it is happening so, why my first XPath is not working now, does addition or removal of style attribute in html impact on existing XPath?
Please check the attached screenshot to view the html structure
It's impossible to give the definite answer without seeing the whole HTML document, but probably you have the following problem:
Your initial XPath expression was:
//*[contains(text(),'Home Page')]
Which, in plain English, means:
Select element nodes with any name, if they have at least one text node as a child, and if the first text node in them contains the string "Home Page".
I am emphasizing first because it it not obvious to many that a function like contains() will use only the first node in a sequence, and silently ignore the rest - this is only true for XPath 1.0.
The expression text() does not return a single node, it returns a sequence of nodes if an element has more than one child text node. This happens if there are interfering child element nodes, for example.
There are several ways to confirm this yourself. On the one hand, you can modify the expression to
//*[contains(text()[2], "Home Page")]
which explicitly selects the second text node as the argument for contains() and you will find this label element as a result.
Or, evaluating an expression on only the HTML snippet you show,
/label/text()
will return (individual results separated by ---):
[result that only has whitespace]
-----------------------
Home Page
which indicates that the i element as a child of label leads to an additional text node in front of i that only has whitespace in it.
A good solution to your problem with the correct semantics is
//*[text()[contains(.,'Home Page')]]
it means:
Select element nodes with any name, if they have at least one text node as a child, and if any text node in them contains the string "Home Page".
I need to find a certain text in a nested div that has no class or id.
This is a structure of the html.
<div class="active_row">
<div class="outcomes">
<div class="event_outcome" onclick="doSomething">
<div>Target Text</div>
</div>
</div>
</div>
I tried accessing the text directly using the example I got from here.
driver.find_elements_by_xpath("//div[contains(., 'Target Text')]")
This returns a list of elements that contain the target text but nothing happens when I run the click method on them.
What's the best way to set this query to find the text and then click on the div with event_outcome class?
To select the div with event_outcome class, you can add a predicate in your XPath to check class attribute value :
//div[contains(., 'Target Text') and #class='event_outcome']
or add a predicate to check existence of onclick attribute :
//div[contains(., 'Target Text') and #onclick]
What's the best way to set this query to find the text and then click on the div with event_outcome class?
You should try using below xpath which would returns <div class="event_outcome" onclick="doSomething"> with text Target Text which would fullfil all your needs as below :-
element = driver.find_element_by_xpath(".//div[contains(., 'Target Text') and #class='event_outcome']")
print(element.text)
element.click()
Or you can also get the same with exact match of the inner text using normalize-space() function of the xpath as below :-
element = driver.find_element_by_xpath(".//div[normalize-space()='Target Text' and #class='event_outcome']")
print(element.text)
element.click()
The element is similar to:
<input type="text" class="information">
There is no id for the element.
There is only one text type element inside the information class. I want to be able to enter text into this html element by using casperjs which works on top of phantomjs.
The XPath obtained from chrome developer tools is similar to:
//*[#id="abcid"]/div/div[1]/input
abcdid is the id of the div element which comprises of the text box and a few other elements. But I need a more reliable XPath. I'm not very experienced with finding XPaths so forgive me if the answer is too obvious.
If you want to use XPath selectors for nearly all CasperJS functions, you need to provide it as an object. If the selector is provided as a string it will be automatically assumed that it is a CSS selector.
You can build the XPath selector object yourself:
{
type: 'xpath',
path: '//input[#class="information"]'
}
or just use a XPath utility by first requiring it at the beginning of your script and then using it:
var x = require('casper').selectXPath;
// later ...
var text = casper.fetchText(x('//input[#class="information"]'));
Regarding your selector:
If there is only one input with the information class then you can use the XPath
//input[#class="information"]
or the CSS selector
input.information[type='text']
If the input has other classes too, the CSS selector will work as is, but the XPath selector must be changed to
//input[contains(#class,"information")]
I'm trying to automate the Google Translate web interface with Selenium (but it's not necessary to understand Selenium to understand this question, just know that it finds elements and clicks them). I'm stuck on selecting the language to translate from.
I can't get to the point where the drop-down menu opens, as seen in the screenshot below.
Now, I want to select 'Japanese'.
This xpath expression works: $b.find_element(:xpath,"//*[#id=':13']/div").click But I would rather have one where I can just input the name of the language.
This xpath expression also works: $b.find_element(:xpath,"//*[contains(text(),'Japanese')]").click But only as long as there is no other 'Japanese' text on the page.
So I'm trying to narrow down the scope of my xpath, but when I try to specify the path to take to find the 'Japanese' text, the expression no longer works, I can't find the element: $b.find_element(:xpath,"//*div[#id='gt-sl-gms']/*[contains(text(),'Japanese')]").click
It also no longer works for the original xpath either: $b.find_element(:xpath,"//*div[#id='gt-sl-gms']/*[#id=':13']/div").click
Which is weird, because to bring down the drop-down menu, I use this xpath $b.find_element(:xpath,"//*[#id='gt-sl-gms']/*[contains(text(),'From:')]").click.
So it's not that I have two wildcards in my expression and it's not that my expression is too specific. There's something else that I'm missing and I'm sure it's really simple.
Any suggestions are appreciated.
Edit Other things I have tried unsuccessfully:
$b.find_element(:xpath,"//*/div[#id='gt-sl-gms']/*[#id=':13']/div").click
$b.find_element(:xpath,"//*[#id='gt-sl-gms']/*[#id=':13']/div").click
$b.find_element(:xpath,"//*[#id='gt-sl-gms']//*[#id=':13']/div").click
If the div with "#id=':13'" is an descendant of the div with "#id='gt-sl-gms" your xpaht "//*[#id='gt-sl-gms']//*[#id=':13']/div" would work.
The above xpaht expect that the html looks somehow like:
<div id="gt-sl-gms">
<div>
<div id=":13">
<div></div>
</div>
</div>
</div>
If <div id="gt-sl-gms"> in not an ancestor (as I expect) you have to look for an "real" ancestor, or you may use following (for nodes later in the document) or following-sibling (for nodes later in the document at the same level as the previous.
*div is incorrect, it should be just div. Also, depending on he structure of the HTML, you may need // instead of /.
Try selecting descendants (//) instead of (/*) which is really grandchildren or deeper.