I have two divs looks like:
<div id="responseframe">
<div id="oldframe">
</div>
</div>
I thought the #oldframe DIV is the only child of #responseframe. However, when I write this in javascript,
var old=document.getElementById("responseframe");
var nodesnumber=old.childNodes.length;
console.log("-------------Here is the nodes number of reponseframe---------: "+nodesnumber);
var nodesname=old.childNodes[i].nodeName;
console.log("-------------Here is the nodes name of reponseframe's child---------: "+nodesname);
console told me #responseframe has 3 child nodes and,
childNode[0] is #text;
childNode[1] is DIV ;
childNode[2] is #text
Why there are 2 #text? Thank you for any idea.
Because of you added new line after <div id="responseframe"> and after first </div>.
If you put this in one line will be there is one node: div.
Html:
<div id="responseframe"><div id="oldframe"></div></div>
Output:
-------------Here is the nodes number of reponseframe---------: 1
-------------Here is the nodes name of reponseframe's child---------: DIV
Here is fiddle: http://jsfiddle.net/cassln/t7kec97u/2/
Node.childNodes property returns all direct child elementNodes of parent element including textNodes, commentNodes.
So in your case you have:
<div id="responseframe"><!-- this whole space area is considered by html as single space so you got your first #text Node
--><div id="oldframe"><!-- this area is ignored because this is not direct child area of the responseframe
--></div><!-- this whole space area is considered by html as single space so you got your second #text Node
--></div>
So finally we got direct children: #text0 #DIV(oldframe) #text1.
If you want to get only direct elementNode (without textNodes and commentNodes) you need Node.children.
var old=document.getElementById("responseframe");
var nodesnumber=old.children.length;
console.log("-------------Here is the nodes number of reponseframe---------: "+nodesnumber);
var nodesname=old.children[i].nodeName;
console.log("-------------Here is the nodes name of reponseframe's child---------: "+nodesname);
Related
(NOTE: This question isn't the same as the similar one above, as it is about differences between attached and detached DOM trees.)
A simple bit of HTML containing a DIV with no whitespace between its elements:
<!DOCTYPE html>
<html>
<body>
<div><h1>The Title</h1><p>A paragraph.</p><p>A second paragraph.</p></div>
</body>
<script type="text/javascript">
const div = document.querySelector("div");
console.log(div.innerText);
const clone = div.cloneNode(true);
console.log(clone.innerText);
document.body.appendChild(clone);
console.log(clone.innerText);
</script>
</html>
I output innerText to the console three times.
The first time is that of the original DIV:
The Title
A paragraph.
A second paragraph.
The second is that of the cloned DIV, which I would expect to be the same, but is:
The TitleA paragraph.A second paragraph.
The third is again that of the cloned DIV, but after it has been added to the document, now what I would expect it to be:
The Title
A paragraph.
A second paragraph.
Why is the spacing different when it is not part of the document?
This is a quirk of innerText for detached DOM nodes:
If the element itself is not being rendered (for example, is detached from the document or is hidden from view), the returned value is the same as the Node.textContent property.
This is because innerText takes CSS into account (in this case, the display: block properties of the tags to insert new lines (\n)).
From the algorithm for computing innerText, step 9 says:
If node's used value of 'display' is block-level or 'table-caption', then append 1 (a required line break count) at the beginning and end of items.
As you see, after you insert the cloned node into the DOM, then innerText returns what the original node did because it is able to compute those CSS properties:
const div = document.querySelector("div");
console.log("Original, innerText:", JSON.stringify(div.innerText));
console.log("Original, textContent:", JSON.stringify(div.textContent));
const clone = div.cloneNode(true);
console.log("Detached clone, innerText:", JSON.stringify(clone.innerText));
console.log("Detached clone, textContent:", JSON.stringify(clone.textContent));
document.body.appendChild(clone);
console.log("Attached clone, innerText:", JSON.stringify(clone.innerText));
console.log("Attached clone, textContent:", JSON.stringify(clone.textContent));
<div><h1>The Title</h1><p>A paragraph.</p><p>A second paragraph.</p></div>
For example, if I have
<div>
<h2>Name</h2>
<h3>age</h3>
<span class='fa fa-trash'></span>
</div>
and when clicking the span (icon) I say
this.parentNode.childNodes[1].innerText
it would target the Name but if I say
this.parentNode.childNodes[2].innerText
it would not target Age. why is this? Is there a resource that explains this well? I know the indexing doesn't start at 0 but I don't understand how the indexing work.
DOM is not just the elements you see in your HTML code. In-between each element is a "text node" where text can be displayed. An example of this would be having text in a div below a a header. So...
<div>
<!-- Index 0: Text node -->
<h1>Header Text</h1> <!-- Index 1: Element -->
Description Text <!-- Index 2: Text node -->
</div>
As you can see, you are allowed to insert text in the div without wrapping it in an element. These are called text nodes which you see when you put the text in normal text elements (such as span or p) or buttons (<button>TEXT</button>). So to get around this in you JavaScript code, you could either do it the lazy way;
document.getElementById("ELEMENT_ID").childNodes[INDEX*2 + 1]
or by using the children property;
document.getElementById("ELEMENT_ID").children[INDEX]
The problem with this method is that it only returns 'element' children within the div, so the description text in the above HTML example will not be accessible. ([H1] instead of [Text, H1, Text]), but I suppose that is what you're looking for anyway. :)
Nodes are 'automagically' added to elements by Chrome. More experienced front end devs probably deal with this frequently.
It was surprising for me to find in Chrome that the nested div e2 <div id="e1"><div id="e2"></div></div> has NodeList [] well ok, that is expected, but div e1 has
NodeList(3) [text, div#e2, text] !!
So an empty div has no child nodes while the outer of two nested divs has 3 child nodes. I can see that text could be added between the divs, so the two text nodes maybe act as placeholders.
But I'm wondering where can I find documentation for this behaviour? What's it called anyway?
And by the same auto add logic shouldn't the inner div have a placeholder text node too??
It is how the method treats whitespace.
childNodes: includes all child nodes—including non-element nodes like text and comment nodes.
const test = elem => console.log(elem.id, elem.childNodes.length, elem.children.length);
test(document.querySelector("#test1"));
test(document.querySelector("#test2"));
test(document.querySelector("#test3"));
test(document.querySelector("#test4"));
<div id="test1"></div>
<div id="test2"> </div>
<div id="test3"><div></div></div>
<div id="test4">
<div></div>
</div>
I have some non-static content on my webpage and I need all unwrapped plain text to be wrapped inside an anchor element with a class, however, I need to maintain the placement of that text.
I've search around on this site and found questions like these which are asking the same question except the text is always in the beginning or the end and the answers always prepend/append the content back into the <div>.
To make the question even more complicated, there are scenarios where the content will be only unwrapped plain text and I'll need to wrap that as well.
My HTML:
<div>
<a>1</a>
<a>2</a>
3
<a>4</a>
</div>
Sometimes:
<div>
1
</div>
I've tried all the answers on this page but they all reorder the text.
Any ideas?
Iterate over all text nodes of the element:
$('div').contents().filter(function() {
return this.nodeType === 3;
}).wrap('<a class="awesomeClass"></a>');
DEMO
.contents() retrieves all child nodes of the element, not only element nodes.
The .filter callback discards all nodes that are not text nodes. It works as follows:
Each DOM node has the property nodeType which specifies its type (what surprise). This value is a constant. Element nodes have the value 1 and text nodes have the value 3. .filter will remove all elements from the set for which the callback returns false.
Then each of the text nodes is wrapped in a new element.
I'm having a whitespace problem.
If your HTML looks like
<div>
1
</div>
then the element has one child node, a text node. The value of the text node does not only consist of the visible character(s), but also of all the whitespace characters, e.g. the line break following the opening tag. Here they are made visible (⋅ is a space, ¬ is a line break):
<div>¬
⋅⋅1¬
</div>
The value of the text node is therefore ¬⋅⋅1¬.
A way to solve this would be to trim the node values, removing trailing and preceding whitespace character:
$('div').contents().filter(function() {
return this.nodeType === 3;
}).each(function() {
this.nodeValue = $.trim(this.nodeValue);
}).wrap('<a class="awesomeClass"></a>');
DEMO
I've tried
d3.select(".cell:first")
d3.selectAll(".cell").filter(":first")
d3.selectAll(".cell").select(":first")
but neither work
d3.select(".cell") already selects the first matched element:
Selects the first element that matches the specified selector string, returning a single-element selection. If no elements in the current document match the specified selector, returns the empty selection. If multiple elements match the selector, only the first matching element (in document traversal order) will be selected.
Source: https://github.com/mbostock/d3/wiki/Selections#wiki-d3_select
"How would I get the last item?"
D3 appears to return the results of d3.selectAll() in a collection, positioned in an array. For instance, requesting all paragraphs on the d3 homepage results in:
[ Array[32] ] // An array with a single array child. Child has 32 paragraphs.
So if we wanted to get the last paragraph from that collection, we could do the following:
var paragraphs = d3.selectAll("p");
var lastParag = paragraphs[0].pop();
Or more concisely:
var obj = d3.select( d3.selectAll("p")[0].pop() );
"What about :last-child?"
The :last-child selector isn't the same as getting the last element on a page. This selector will give you the elements that are the last child of their parent container. Consider the following markup:
<div id="foo">
<p>Hello</p>
<p>World</p>
<div>English</div>
</div>
<div id="bar">
<p>Oie</p>
<p>Mundo</p>
<div>Portuguese</div>
</div>
In this example, running d3.select("p:last-child") won't return any of your paragraphs. Even d3.selectAll("p:last-child") won't. Neither of those containers have a last child that is a paragraph (they are <div> elements: <div>English</div> and <div>Portuguese</div>).
If you want to get the first DOM element from the D3's selection, use .node() method:
var sel = d3.selectAll('p'); // all <P>, wrapped with D3.selection
var el = sel.node(); // the first <P> element