How to get text of element but excluding the sub-elements text - selenium-webdriver

I want to get the text of the element without including the text of its elements. I have tried getText(), but it returns text that includes all the child elements text.
In the following example: When I retrieved text from the first div, it returns text that includes all its subelements.
<div class="row”>
<div class="col-lg-4 section”>
<div class="col-md-12”>
inseam 28 30 32
</div>
</div>
<div class="col-lg-5 section”>
<div class="col-md-13”>
inseam 28 34 36
</div>
</div>
</div>
Please let me know how to do this using webdriver in java.
Thanks
sean

I've been searching for the same thing for a while, here's my solution for those who can specify a WebElement or a list of WebElements:
def remove_child_text_from_webelement(webelement):
# Declaring the current text for this webelement
current_text = webelement.text
# Getting its childs elements in a list
childs_list = webelement.find_elements_by_xpath('./*')
# Manipulating text to remove child text from parents
childrens_text_list = [child.text for child in childs_list]
#return (childrens_text_list,type(childrens_text_list))
for children_text in childrens_text_list:
match_index = current_text.find(children_text)
if match_index != -1:
match_length = len(children_text)
current_text = current_text[0:match_index] + current_text[match_index+match_length:]
return current_text
Now you can do something like:
[remove_child_text_from_webelement(e) for e in browser.find_elements_by_xpath('//div[contains(#class,"person")]')]

When I retrieved text from the first div with class 'row', it returns text that includes all its subelements.
This happened because you retrieved text from the parent div and hence all the innerHTML/text of the child divs were retrieved along with them.
Below is the way to retrieve the necessary innerHTML/text only:
1- for 'inseam 28 30 32':
String text = driver.findElement(By.xpath("//div[#class='col-md-12']")).getText();
OR
String text = driver.findElement(By.className("col-md-12")).getText();
2- for 'inseam 28 34 36':
String text = driver.findElement(By.xpath("//div[#class='col-md-13']")).getText();
OR
String text = driver.findElement(By.className("col-md-13")).getText();

Not tried it specifically with Selenium, but with jQuery you can use contents() to get all elements including raw text nodes, filter by nodeType 3 (text nodes) and then take the first, in your example:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/p33gcfk2/1/
var text = $('.row').contents().filter(function () {
return this.nodeType == 3;
}).first();
alert(text.text());

This is happening because you are trying to get the text of parent tag. If you want to get tag of particular child, you have to reach all the way there. You can take use of "nth-child" or "nth-of-type". For e.g in this case, if you want to have return this text "inseam 28 34 36".
The CSS selector will be "div.row div:nth-of-type(3)" or you can directly specify the div class "div.col-md-13"
You can refer to this article on more on selectors https://saucelabs.com/resources/selenium/css-selectors

Related

angular 10 in html template array variable don't match type declaration

I am using a global variable and type defined as:
global.ts
export type ReservationCurrentPC = Array<{StartDateTime: string, EndDateTime: string, Who: string}>;
export class GlobalVariables {
public static ReservationsCurrentPC: ReservationCurrentPC = [];
}
then imported it in component .ts
import { GlobalVariables , ReservationCurrentPC } from '../GlobalVariables';
then did a getter at component.ts for using it in its html template.
get GetReservationsMyPC(): ReservationCurrentPC { return GlobalVariables.ReservationsCurrentPC; };
then it executes a single push as:
case 'RSV':
// stemp[i].Param1 = PCNameID.
// stemp[i].Param2 = StartDate.
// stemp[i].Param3 = EndDate.
// stemp[i].Param4 = ReserveByAccountName.
GlobalVariables.ReservationsCurrentPC.push (stemp[i].Param2, stemp[i].Param3, stemp[i].Param4);
and then try to use it in the html template.
<div *ngFor="let b of GetReservationsMyPC">
<li>
Desde [{{b.StartDateTime}}] hasta [{{b.EndDateTime}}] reservado para: {{b.Who}}
</li>
<button (click)="RemReserve(b.StartDateTime)">Remover esta reserva</button>
</div>
the problem is, the html loops 3 times with the variable 'b' , and it is not recognizing the type structure, like 3 fields per item, and like b.StartDateTime , b.EndDateTime, b.Who returns nothing, only if print just 'b' variable it will get the 3 strings pushed, as if it were a single plain array.
How to handle that structured variable properly at html template? I saw several examples but, using local-component variable, and that works, but I need to use this global variable because it is required to be filled from another component (which handled a map view where the user clicks, and the object clicked in the map triggers several events, and that is where is requested info from the API about the object clicked, and as response it returns the data for the push), so, it must be a global variable as it is handled in several components.
fixed:
GlobalVariables.ReservationsCurrentPC.push ({StartDateTime: stemp[i].Param2, EndDateTime: stemp[i].Param3, Who: stemp[i].Param4 });
What an adventure for me!.

How do I get the text from the li tag

How do I get the text from the li tag? I want to find the text "Password is required." only, not the text inside strong tag.
<li><strong>Error:</strong> Password is required.</li>
You need to show your code for somebody to give a complete answer. I guess that you already know how to do something like the following
WebElement something = driver.FindElement(By.CssSelector(?))
string s = something.Text;
The next bit seems to be where you are stuck. There you need to parse the string s. That is nothing to do with Selenium-Webdriver. You could do something like
string[] s2 = s.split(new string[] {">","<"});
were the last element in s2 would be your answer here. This would be totally non generic though. Is this a situation in which you always want to purge html?
Here is the method developed in python.
def get_text_exclude_children(element):
return driver.execute_script(
"""
var parent = arguments[0];
var child = parent.firstChild;
var textValue = "";
while(child) {
if (child.nodeType === Node.TEXT_NODE)
textValue += child.textContent;
child = child.nextSibling;
}
return textValue;""",
element).strip()
How to use in this:
liElement = driver.find_element_by_xpath("//li")
liOnlyText = get_text_exclude_children(liElement)
print(liOnlyText)
Please use your possible strategy to get the element, this method need an element from which you need the text (without children text).

How to select p and q tag in jsoup such that if same element is under the both the tags,it should be selected once?

How to select p tag and b tag both and if p and b have same element then only one should be selected.
<html>
<div>
<p><b>This is first line</b></p>
<b>This is second line</b>
<span style="color:blue">This is third line</span>
</div>
</html>
How do I select all the three lines only once?
If I use html.select("p,b");, <p><b>This is first line</b></p> gets selected twice.
You need the pseudo-selector :not to exclude the child b tag.
for (Element e : doc.select("p :not(b),b"))
System.out.println(e.ownText());
Output
This is first line
This is second line
To include the third line, add span to your selector.
for (Element e : doc.select("p :not(b),b,span"))
System.out.println(e.ownText());
Output
This is first line
This is second line
This is third line
How do I select all the three lines only once?
Use the parent with a selector for child nodes div>* (see CSS reference):
Update: use selector for elements, that are not <div> but a have a <div> parent .select("div>:not(div)")
String htmlString = "<html><div><p><b>This is first line</b></p><b>This is second line</b><span style=\"color:blue\">This is third line</span></div></html>";
Document doc = Jsoup.parse(htmlString);
Elements elements = doc.select("div>:not(div)");
for (Element element : elements) {
System.out.println(element.toString());
}
This prints out:
<p><b>This is first line</b></p>
<b>This is second line</b>
<span style="color:blue">This is third line</span>
If you will only use the text inside the nodes simply use element.text()

Selenium java code: How to read text from hidden element that contain many tags

1.<svg blablabla style:overflow: hidden > <aaa id=aaa>
2.+
3.<g>
4.<g>
5.<g>
6.<g>
The tags are inside the svg.
1 is the hidden tag, i want to get the 5th line text(), this is what i do.
WebElement hiddenDiv = driver.findElement(By.id("aaa"));
String n = hiddenDiv.getText();
String script = "return arguments[0].innerHTML";
n = (String) ((JavascriptExecutor) driver).executeScript(script, hiddenDiv);
System.out.println(n);
how can i get g[2], i have tried by direct xpath and it got an error because the svg is hidden.
You just need to grab an array of the g tags and pick the one you want. If you want the 5th, you would use the code below.
List<WebElement> gs = driver.findElements(By.cssSelector("#aaa g"));
System.out.println(gs.get(4).getAttribute("innerHTML"));

How do I set a class based on a child element's state with Angular?

I am trying to display a results 'table' (built with DIVs as it happens) using Angular. Data looks somethingof it like this:
[['sydney','hotel','2','5','1'],
['sydney','bar','6','5','2'],
['sydney','stand','2','7','3'],
['melbourne','hotel','2','5','1'],
['melbourne','bar','8','0','1']]
What I want firstly is to suppress the repeating city name so that the first row says 'sydney' at the start but the second row and third row don't. Then the fourth says 'melbourne' and the fifth says nothing.
I've achieved this using markup like this:
<div class="row-container"
ng-repeat="row in resultsRows"
<div
ng-repeat="cell in row track by $index"
ng-bind="showValue( cell )">
</div>
</div>
The showValue() function in the controller looks like this:
$scope.currentCityName = '';
function showValue( val ) {
var outValue = '';
if (this.$index === 0) {
if (val === $scope.currentCityName) {
outValue = '';
} else {
$scope.currentCityName = val;
outValue = val;
}
} else {
outValue = val;
}
return outValue;
}
Maybe that's a bit clunky but it works and I get:
sydney hotel 2 5 1
bar 6 5 2
stand 2 7 3
melbourne hotel 2 5 1
bar 8 0 1
Now, though, I want rows that have the city name in them to have a different background colour.
What I think I want is for any 'TR' DIV (I call it that because it contains the left-floated 'TD' DIVs with the data points in them) to check if its first child DIV is not empty (because it has the city name in it) and, if so, to colour its background.
My question is: how do I do that with Angular? Or am I missing another trick..?
How do I get an item in an ng-repeat loop to interrogate a child element?
You are using ng-repeat, which has built-in values like $even and $odd:
$even boolean true if the iterator position $index is even (otherwise false).
$odd boolean true if the iterator position $index is odd (otherwise false).
Use ng-class to give different classed depending on $even and $odd.

Resources