When and how does firstChild select #text nodes? - javascript

Bit new to JS here, so I apologize if this is something obvious. I've read through the relevant documentation, and I'm a bit perplexed about how and when exactly firstChild selects text nodes.
I have a span and an input like so:
<span class="checkbox">
<input class="inputs" value="1">
</span>
On page load, if I call:
$(".checkbox").firstChild
I'll get back that input html element. Now, if I make an ajax call that replaces the entire span and its input with identical code, and then call:
$(".checkbox").firstChild
I get a #text node element back. Why? It may be that a more pertinent question is when are #text nodes inserted into whitespaces?
Please let me know if you need some more context and I appreciate you taking the time to help a beginner out.

Whitespace between nodes creates text nodes, so depending on whether there's any space/newline/tab between the closing > of the parent and the opening < of the child you may or may not get text nodes.
Use firstElementChild instead. Similar equivalents exist for sibling traversal.
Other options are to adjust your CSS selector to get the first child or use jquery's traversal methods.

Related

How do I find parents of pseudo element using selenium webdriver?

I would like to retrieve parent web element of pseudo element (If it could be named parent), but as far as I know selenium's methods of searching for web elements are not suitable for searching pseudo elements.
However, JavaScript manipulates pseudo elements freely, so I would like to ask, if there is a method that could return css selector/xpath of parent web element for pseudo element (To be more precise "::after") in JavaScript.
In other words, I've got this:
<html>
<body>
<label id="parent">
<span> Some text </span>
::after
</label>
</body>
</html>
And I would like to get that:"#parent"
Thank you in advance.
If you need to select parent based on text of span, then you can use something like this :
driver.findElement(By.xpath("//label[.//span[text()='Some Text']]"));
The above code will help you find that label which is followed by a span with text = "Some Text".
If you are not sure of the tag and you are sure of getting the results using javascript. Try executing the JavaScript Code in selenium using the below syntax: (Python)
driver.execute_script("document.querySelector('#item'), ':after'")
Update the script as per your requirement in your case to find the parent also.
Let me know in comments below if this doesnt help you.
Find child element first and add context node to the child element, like this:
WebElement child = driver.findElement(By.xpath("xpath of child"));
WebElement parent = child.findElement(By.xpath("./.."));

Angularjs: How to check if element's children contains certain element (by ID)

<div id="divWrapper">
<input id="firstInput"/>
<div id="insideDiv">
<input id="secondInput"/>
</div>
</div>
This is the basic structure of my HTML. So, given an access to "divWrapper" element how do I check if it contains "secondInput".
NOTE: This is to be done without jQuery. Only using Angularjs utility functions.
UPDATE: The solution should be dynamic. Meaning it should find any child, how much ever down the level it is in the DOM tree.
UPDATE 2: Please don't get misguided by "input" element. I don't want to track the text inside it. I want to a logic which will check if me or any of my child is clicked. If NOT then hide myself and my children.
var secondInput = angular.element(document.querySelector("#divWrapper #insideDiv #secondInput"));
This will return the element you're looking for or empty if it doesn't find anything.
angular.element("#someId").find("someOtherSelector") already does exactly what you want.
So for your question you could just do...
angular.element("#divWrapper").find("#secondInput"); // Empty array OR a match

Merging Text Nodes Together After Inserting Span

I have an extension where I am storing/retrieving a section of the DOM structure (always a selection of text on the screen) the user has selected. When I am storing a selection, I enclose the section in a SPAN tag, and highlight the text in yellow. This causes the DOM structure around the selected text to split up into various text nodes. This causes a problem for me as when I try to restore this selection (without refreshing the page) it causes problems as the DOM structure has been modified.
My question is how do I prevent the DOM structure from splitting up after inserting the SPAN? If this cannot be achieved, how would I reassemble the DOM structure after removing the SPAN tag to its original state?
//Insert the span
var sel = restoreSelection(mootsOnPage[i].startXPath);
var range = sel.getRangeAt(0).cloneRange();
var newNode = document.createElement('span');
newNode.className = 'highlightYellow';
range.surroundContents(newNode);
//Original DOM structure
<p>Hello there, how are you today</p>
//What the DOM looks like after insertion of SPAN
<p>
"Hello there, "
<span class="highlightYellow">how</span
" are you today"
</p>
Use element.normalize().
After you remove the span you inserted, you can use the element.normalize() method to merge the extra text nodes that were created as a result of the insertion/removal of the span. The normalize() method puts the specified element and all of its subtree into a "normalized" form (i.e. no text nodes in the subtree are empty and there are no adjacent text nodes). Found, thanks to #tcovo's comment.
Text nodes inside of an element are broken apart if you insert nodes and then remove them. Unfortunately they don't automatically re-merge once the extra node is removed. To answer peoples' questions as to "why" this matters, it usually causes issues when working with text highlighting in your UI.
The very act of inserting a <span> tag will alter the DOM. That's, somewhat by definition, what you're doing when you call surroundContents(). You can't add a span tag without altering the DOM which includes splitting text nodes and adding new elements for the span.
Further, unless the selected text includes only whole text nodes and the selection never starts/stops in the middle of a text node, you will have to split text nodes to put the span in the right place. When you later remove the span tags, you will have extra text nodes. That shouldn't really matter to anything, but if you really think you have to get the split text nodes back to the way they were, I can think of a couple options:
1) Save the original parentNode before the span is inserted into it. Clone it, add your span to the clone, replace the original node with the clone and save the original. When you want to restore, put the original back and remove the cloned one.
2) When you remove the span, run a function that looks for neighboring text nodes and combine them.
3) Figure out why it matters that there are more text nodes afterwards than there were before because this should not matter to any code or display.
When using normalize() pay attention!
It will strip away nodes like <br/> and will alter the text and its visualisation.
normalize() is good, but it has its drawbacks.
So <p>"this is an "<br/>"example"</p> will turn into <p>this is an example</p>
Is there a way to use normalize() but keeping the <br/>s?
You can concatenate and then remove the second node
node1.textContent += node2.textContent;
node2.remove();
You can use this to unwrap your content.
$(".highlightYellow").contents().unwrap();
Demo: http://jsfiddle.net/R4hfa/

Can't understand this line of JavaScript

Thanks for your attention and time.
I'm modifying an existing JavaScript but can't understand a line of code. Please help me understanding this line:
rowArray[i].value = rows[i].getElementsByTagName('td')[sortOn].firstChild.nodeValue;
I am clear till .getElementsByTagName('td'), sortOn is being passed in this function as a parameter. But I couldn't understand [sortOn].firstChild.nodeValue;
Please guide me,
thanks
.getElementsByTagName('td') - returns a list of TD elements.
.getElementsByTagName('td')[sortOn] - fetches a single element from that list
.firstChild - returns the first element that is positioned inside this TD.
.nodeValue: see here - https://developer.mozilla.org/En/DOM/Node.nodeValue
[sortOn]
is array notation. It works in exactly the same way as rows[i]. Let's say sortOn is equal to 5, and that there are seven elements in rows[i].getElementsByTagName('td'), which is an array of <td> elements. Then you will get the sixth one (JavaScript arrays are 0 based), and this will be a <td> element.
firstChild means the first element beneath that td, so in this case
<td><em>emphasis</em><strong>some text</strong></td>
the <em> element is the first child
nodeValue is in this case the contents of that element, so "emphasis" will be returned.
You may well find the gecko DOM reference useful
rows[i].getElementsByTagName('td') will get all td elements that are children of rows[i]. The [sortOn] part selects the td whose index is specified by the sortOn parameter. The .firstChild.nodeValue gets the text contained in the first element within that td.
Update: In the DOM, elements such as <td> can only contain other child elements, but they don't have any text property. The text itself is contained in a special "text node" that is a child of the <td> node. This is why you use .firstChild to obtain the text node, then use .nodeValue to get the text contained in that node.
getelementsByTagName returns you an array of element with same tag, then by using sortOn variable you select specified one form collection and take his first child and look on it.

Complex jquery selection that also involves custom xml tags

I want to write a select something like...
#window_4 > content > p:eq(0)
I think I have this correct, but I have a few selectors that are all similar but I can't test them all at once.
Am I right in saying this is selecting an element, who is the fist p tag child of a content tag that is a child of a tag with id 'window_4'
If I have gotten this wrong, can you give me some pointers. Would love to be able to simplify this code, I have more code selecting the tag I am after then actually doing stuff with them.
Looks good to me, although you can make it a bit more readable by substituting p:eq(0) for p:first.
Edit for comment:
jQuery always returns an array of elements, no matter whether 0, 1 or many elements were found. On these elements, yes, you can perform JS functions, such as innerHTML. You can access each element returned by jQuery just as if you would any other array:
$(".red")[0].innerHTML = "Glen Crawford";
More info: http://groups.google.com/group/jquery-ui/browse_thread/thread/34551a757f139ae1/20111f82c2596426

Categories

Resources