DOM: element created with cloneNode(true) missing element when added to DOM - javascript

I'm creating a tree control and I'm attempting to use a parent element as a template for its children. To this end I'm using the element.cloneNode(true) method to deep clone the parent element. However when I insert the cloned element into the DOM it is missing certain inner elements despite having an outerHTML value identical to its parent. Surprisingly I observe the same behavior is in IE, Firefox, and Chrome leading me to believe that it is by design.
This is the HTML for the node I'm attempting to clone.
<SPAN class=node><A class=nodeLink href="/SparklerRestService2.aspx?q={0}" name=http://dbpedia.org/data/Taylor_Swift.rdf>
<IMG class=nodeIcon alt="Taylor Swift" src="images/node.png"><SPAN class=nodeText>Taylor Swift</SPAN></A><SPAN class=nodeDescription>Taylor Swift is a swell gall who is realy great.</SPAN></SPAN>
Once I've cloned the node using cloneNode(true) I examine the outerHTML property and find that it is indeed identical to the original.
<SPAN class=node><A class=nodeLink href="/SparklerRestService2.aspx?q={0}" name=http://dbpedia.org/data/Taylor_Swift.rdf><IMG class=nodeIcon alt="Taylor Swift" src="images/node.png"><SPAN class=nodeText>Taylor Swift</SPAN></A><SPAN class=nodeDescription>Taylor Swift is a swell gall who is realy great.</SPAN></SPAN>
However when I insert it into the DOM and inspect the result using FireBug I find that the element has been transformed:
<span class="node" style="top: 0px; left: 0px;"<a class=nodeLink href="/SparklerRestService2.aspx?q={0}" name=http://dbpedia.org/data/Taylor_Swift.rdf>Taylor Swift</a><span class="nodeDescription">Taylor Swift is a swell gall who is realy great.</span></span>
Notice that the grandchildren of the node (the image tag and the span tag surrounding "Taylor Swift") are missing, although strangely the great grandchild "Taylor Swift" text node has made it into the tree.
Can anyone shed some light on this behavior? Why would nodes disappear after insertion into the DOM, and why am I seeing the same result in all three major browser engines?

I tried it and it works fine. I notice you say that the text changed to say "It's great" which wasn't even in the original. Sorry but I'm not buying that.

Related

.firstElementChild, .lastElementChild... what about those in between?

I'm trying to update an existing markup element in React rather than replacing an entire fragment. When I assign a div container element to a variable with .getElement(); I can select the first element within the div with firstElementChild. Then I can find its ".style.color" for example.
Now there's a popover that's the before-last element that I also want to get to it (and its backgroundColor). Not the lastElementChild, the one before that. How do I "get to it"? Is there an array of children I can use?
Not the lastElementChild, the one before that. How do I "get to it"?
You can go from the lastElementChild to its previousSibling
Is there an array of children I can use?
There is a live HTMLCollection (which is array-like) called children
DOM_Selector_Image
As can be shown in the attached image. Which presents a basic website with the "HTML Tree Generator" Add on.
typing the following in the chrome console:-
document.firstElementChild.lastElementChild.lastElementChild.previousElementSibling;
Will give you the following results:
<p>​ The DOM ​</p>​
This is because
firstElementChild
This is the HTML document
lastElementChild
This is the Body
lastElementChild
This is the SCRIPT
previousElementSibling
This is the element before the SCRIPT which is the text "The DOM"

How do I get reference to the ::before or ::after node in JS?

As far as I know, standard JavaScript has no way to get at the ::before or ::after pseudo-elements. Element.children doesn't let you get to it.
I know there has to be a way, at least in Chrome-privileged Firefox add-on code, since it lists every ::before element in the page (and apparently getComputedStyle() works on it too, as you can list all styles of it in inspector, which is written in JavaScript).
Where is this API documented, and is it something that's different and privileged-only in say Firefox and Chrome browser, or something that is on track to be standard soon?
The CSS generated content is not part of the DOM, and you wouldn't be able to do much with the ::before/::after pseudo-elements, even if you get at them. The only use-cases I can think of are:
Access the CSS computed values on the pseudo-elements. window.getComputedStyle() supports this via an optional 2nd parameter.
Enumerate the generated content. You can accomplish this:
by using a browser-specific API. In Firefox, the DevTools inspector uses a special interface - inIDeepTreeWalker.
or by walking the DOM and checking (for each element) if it has content in its computed style for :before / :after. For example:
window.getComputedStyle(elt, ':before').content
Get the "live" value of a counter defined in CSS, like in How to access CSS generated content with JavaScript - see that question for details.
At least to me, your question is unclear as to exactly what you are attempting to do, or get.
The most direct equivalent to ::before and ::after:
If you are wanting to actually insert content, which is what the ::before and ::after CSS selectors do, then the most direct equivalent is Element.insertAdjacentHTML(position, text). In that case:
The equivalent of ::before would be:
Element.insertAdjacentHTML("beforebegin", "<p>Additional HTML content before element.</p>");
The equivalent of ::after would be:
Element.insertAdjacentHTML("afterend", "<p>Additional HTML content after element.</p>");
Element.insertAdjacentHTML() also has options of afterbegin and beforeend which insert the HTML text just after the beginning, or just before the end, of the referenced Element.
Alternately:
You could insert nodes using Node.insertBefore(newNode, referenceNode).
For ::before it would be (insert newNode before myNode):
myNode.parentNode.insertBefore(newNode, myNode);
For ::after it would be (insert newNode after myNode):
myNode.parentNode.insertBefore(newNode, myNode.nextSibling);
Obtaining references:
If you are attempting to get a reference to the element that is earlier in the DOM, then it sounds like you are looking for Node.previousSibling. If you are looking for a reference to the element that is later in the DOM, then you are looking for Node.nextSibling.
In DOM walk order:
It is also possible that you are looking for the elements that are just before and just after the reference Node in DOM walk order. However, that is not really what the CSS selectors ::before and ::after do. However, from your mention of Page Inspector, it kind of sounds like this is what you want. If so, then you will can use a TreeWalker to walk the DOM tree.
The following should do what you want (Note: Currently untested, so might be missing something.):
//referenceNode is the node for which we want to find the elements
// before and after in DOM walk order.
//Create the TreeWalker
let treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT,
{acceptNode: function(node) {
return NodeFilter.FILTER_ACCEPT;
}
},
false );
//Point the TreeWalker at the referenceNode.
treeWalker.currentNode = referenceNode;
//Get the node immediately prior to the referenceNode in DOM walk order
let thePreviousNode = treeWalker.previousNode();
//Point the TreeWalker back at the referenceNode.
treeWalker.currentNode = referenceNode;
//Get the node immediately after to the referenceNode in DOM walk order
let theNextNode = treeWalker.nextNode();
As mentioned by Nickolay, if you want the full detail that Page Inspector, or the DOM Inspector (documentation), provides then you will need to use an inIDeepTreeWalker. However, it is unlikely that you want, or need, the detail which using that Firefox specific non-standard interface provides. You only need it if you want to walk through how something like how an XUL <toolbarbutton> is constructed (not the attributes/properties, but the XBL which makes up a XUL elements like a <toolbarbutton>). For the vast majority of what you are potentially thinking about, a standard TreeWalker should be just fine.
With the exception of inIDeepTreeWalker, all of the above are standard parts of JavaScript and do not require elevated privileges (i.e do not require it to be in an add-on).
You can use iniDOMUtils - selectorMatchesElement() function.
You can read more about it here - https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/inIDOMUtils#selectorMatchesElement%28%29

Is it normal that JavaScript can create otherwise invalid DOM?

Somewhat by accident, I found out that a span inserted directly inside a tbody stays in place when done with JavaScript (insertBefore), where such invalid DOM would if created with literal HTML lead to the span being placed before the entire table.
I expected either the same behaviour as with literal HTML or some DOM Exception being thrown.
E.g. this HTML
<table>
<thead><tr><th>Table Header</th></td></thead>
<tbody>
<span>from HTML → goes up</span>
<tr><td>Table Contents</td></tr>
</tbody>
</table>
with this JavaScript:
var span = document.createElement('span'),
tbody = document.querySelector('tbody');
span.innerHTML = 'Created with JS → stays in place';
tbody.insertBefore(span, tbody.querySelector('tr'));
renders "Created with JS → stays in place" between the header and the first row; the original, literal, span moves outside of the table.
Is this normal, and can/should I count on this? (It behaves the same in FF, Chrome, Opera, IE >= 9 (not tested below)).
Also, is there a way to query the DOM whether content of a certain type would (under normal circumstances) be valid at a certain point in the DOM? This is actually what I wanted to do when I found out about this quirk (which it is, imho).
The fiddle is here: http://jsfiddle.net/xr37g9kw/2/
As for "is this normal, and can/should I count on this?" Sadly, yes. But mostly you should be aware of the node types you are working with. NB, in case of table, there are a handful of not so well known DOM methods (HTMLTableElement.rows. InsertRow() and so on).
As for "is there a way to query the DOM whether content of a certain type would (under normal circumstances) be valid at a certain point in the DOM?" nothing built-in for this exact purpose, but you could exploit one native feature of JavaScript -> DOM API: you can let browser to re-parse HTML chunk in the "literal way". Yes, I am speaking about innerHTML.
In your fiddle, adding**tbody.outerHTML = tbody.outerHTML** "fixes" the structure, so you could hypothetically take some DOM node, look at its DOM tree, clone, "re-eval" it and compare with original.
Yes, that is the default behavior, as you saw. It's not valid HTML of course as you can check here http://www.freeformatter.com/html-validator.html if you input the HTML.

Can I select an element by its array position in nightwatch.js?

I am looking to automate some of my testing processes and I am relatively new to Nightwatch.js and javascript. Is there a way that I can click an element based on it's class and position in the subsequent array that will be returned if there are multiple elements with the same class.
For example take the following HTML: -
<div class="clickable-button"><p>Some Text</p></div>
<div class="clickable-button"><p>Some Text 2</p></div>
<div class="clickable-button"><p>Some Text 3</P></div>
If I use chrome development tools and run the following command in the console: -
$('.clickable-button')
It returns an array of the three elements listed above.
I would like to click the first <div> element and want to know if there is a way I can do this using a CSS selector? I cannot select via the text that appears within the <p> tag as this is dynamic data.
I have tried the following commands in Nightwatch: -
browser.click('.clickable-button'[0])
browser.click('clickable-button[0]')
Neither of these options work. Any help or advice would be appreciated.
You could probably use :nth-of-type
browser.click('.clickable-button:nth-of-type(1)');
BTW :nth-of-type is part of CSS3 so it is not supported by older browsers.
Besides using CSS selector, XPath is another option, you can do
browser
.useXpath() //ignore this line if you already selected xpath as strategy
.click('(//div[#class="clickable-button"])[1]')
to locate the first button. Reference

Detecting attributes of elements in the HTML DOM with javascript

So much confusion, so few answers. I'm trying to loop through the DOM, looking for a specific node by id, however, this code has several problems for which I have no explanation. First, the length of the childNodes list comes up as '5'. Two "ul"'s, two "id"'s, if those count...and one for luck?
Second, it dies at if(y[i].hasAttribute('id')===true). Firebug says this is not a function. I have no reason to not believe it, but am not sure why it isn't.
Thank you for any help.
<div id="list">
<ul id="first"></ul>
<ul id="second"></ul>
</div>
<script>
var comments=document.getElementById('list')
var y=comments.childNodes;
var count=y.length
for(i=0;i<count;i++)
{
document.write(y.length);
if(y[i].hasAttribute('id')===true)
{ document.write('here!');}
}
</script>
the childNodes attribute contains all nodes in the DOM, which specifically means, it includes text nodes. you have 3 of them - the newline/linefeed characters inside your div.
you can test for element children using the nodeType attribute ( see eg. here; 1represents ELEMENT_NODE, 3 stands for TEXT_NODE).
If you use a tool like Firebug and inspect the DOM itself, you would see all the children of an element and the difference between .children and .childNodes .
It's by hunting around in the DOM that I discovered why there are so many things that at first appear to be duplicates of each other, but are definitely not. The Mozilla developers site developer.mozilla.org is also a wealth of information.

Categories