DOMParser parseFromString removing nodes only when iterating resulting body childNodes - javascript

I am writing an electronjs app. I want to parse a string to DOM nodes and try to use DOMParser parseFromString for that. Here is the code:
let str = '<div id="customerList" style="display: none;"><ul></ul></div><script type="text/javascript" src="../js/customerList.js"></script>';
let nodes = new DOMParser().parseFromString(str, 'text/html').body.childNodes;
console.log(nodes);
This returns a NodeList with 2 elements, the expected div and scriptl, in it. If I add the following part in the code, the first element, the div, disappears from the NodeList:
let str = '<div id="customerList" style="display: none;"><ul></ul></div><script type="text/javascript" src="../js/customerList.js"></script>';
let nodes = new DOMParser().parseFromString(str, 'text/html').body.childNodes;
console.log(nodes);
for (let node of nodes) {
contentDiv.appendChild(node);
}
The for loop is after the console.log and somehow alters the behavior of the code before. I can't seem to figure out, why the code behaves like it does though...Since I want to provide information about the first element in an ipcRenderer call, this is actually quite frustrating at the moment. Why does the code behave like it does?

Node.appendChild() moves a node to the new destination. That's why it disappears from your node list.
You can clone the node to avoid that like so:
let str = '<div id="customerList" style="display: none;"><ul></ul></div><script type="text/javascript" src="../js/customerList.js"></script>';
let nodes = new DOMParser().parseFromString(str, 'text/html').body.childNodes;
console.log(nodes);
for (let node of nodes) {
contentDiv.appendChild(node.cloneNode());
}
This will append clones of all(!) nodes from the list and keep your nodes list as is.
Reference: https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild

Related

How to get the DOM reference to an inserted document fragment

I am attempting to get the DOM reference of an inserted document fragment in vanilla Javascript. I'm currently using Node.appendChild() however the returned reference is the document fragment as outlined here: https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild#return_value
Is there any approach I could use to get the inserted DOM reference?
I did find the following Stack Overflow answer for a similar question but not related directly to document fragments, this solution uses either CSS animations or Mutation Observers but seems overkill for what I'm attempting. https://stackoverflow.com/a/38636244/13306195
const temp = document.createElement('template');
temp.innerHTML = '<span>Test</span>';
const neededReference = document.body.appendChild(temp.content);
The thing about document fragments is that you have to clone their content whenever appending them to the DOM in order to make them as DOM Nodes and not just the fragments.
const temp = document.createElement('template');
temp.innerHTML = '<span>Test</span>';
const neededReference =
document.body.appendChild(
temp.content.cloneNode(true).firstElementChild
);
console.log(neededReference); // should give the reference to span element
If the given child is a DocumentFragment, the entire contents of the DocumentFragment are moved into the child list of the specified parent node.
The return value of Node.appendChild() is the same thing with the document fragment which is empty.
I think you can put the children of document fragment into another container (e.g. an array). Those children are the real DOM refernces which are inserted.
const df = new DocumentFragment();
const t1 = document.createElement('template1');
t1.innerHTML = '<span>Test1</span>';
t1.id = "xxx"
const t2 = document.createElement('template2');
t2.innerHTML = '<span>Test2</span>';
df.appendChild(t1);
df.appendChild(t2);
document.body.appendChild(df);
window.alert(document.getElementById('xxx') === t1);

How to append more than 1 nested node for XML in javascript?

Good day everyone,
I am currently trying to append a metadata file. Sorry in advance if I did anything wrong, I am unfamiliar with editing XML codes in JS.. Thanks!
Currently, I am having difficulty getting the results that I expected. I am trying to insert 2 new nodes one nested over the other into the newParentTestNode.
I want to add a couple of nodes within the TestNode as seen in the results I want.. I can't seem to find a solution online. Please do help thanks!
I am currently getting this result:
<gmd:MTTEST><TESTNODE2/></gmd:MTTEST>
But the result I want is:
<gmd:MTTEST>
<gmd:TestNode>
<gmd:TestNode2>
</gmd:TestNode2>
</gmd:TestNode>
</gmd:MTTEST>
xmlTest: function (evt) {
if(this.item.metadata_standard_name == "Correct Data"){
xmlString = this.item.sys_xml_clob;
var metadataXmlString = jQuery.parseXML(xmlString);
let newParentTestNode = metadataXmlString.getElementsByTagName("gmd:MTTEST")
newNode = metadataXmlString.createElement("TestNode")
newNode2 = metadataXmlString.createElement("TestNode2")
let addMe = newNode.appendChild(newNode2)
newParentTestNode[0].appendChild(addMe)
xmlString = (new XMLSerializer()).serializeToString(metadataXmlString);
console.log(xmlString)
}
appendChild() returns the node that is appended not the parent node.
This means that newNode.appendChild(newNode2) returns newNode2, which you'll then append to your root node, effectively removing TestNode2 from TestNode and appending it directly to MTTEST.
You don't need to assign the result of appendChild to a new addMe variable because appendChild modifies the structure in-place, so you gain nothing from the return value (as you already have variables referencing both the parent and the child element). So in the end you just need to append newNode (which will already contain newNode2) to newParentTestNode.

Looping on Document nodes (Including childrens )

I'm trying to replace "document.write" inside an iframe that contains an ad.
I'm currently parsing the html string via "DOMParser" and i'm getting a dom document in return, i would like to loop on this document and insert each node and any childrens to the real iframe dom.
But i've encountered two problems:
1.if the string from the original document.write doesn't contains <head><html><body> the "DOMParser" api adds them anyway.
2.i can loop on all nodes like this:
ParsedHtml.querySelectorAll("*").forEach(function(node) {
but i'm getting on the first iteration the <html> node with all the nodes as childrens and also the <body> node again with all children nodes, and when the iteration continues i'm getting to the same nodes again.
so what i really like to do is to parse the string from document.write and start inserting node after node including all childrens directly to the iframe dom in the correct order (head first the body ...)
Thanks !
Code example:
var RealDocumentWrite = document.write;
document.write = function(str){
//call our parser here instead of using document.write
}
//our parser:
function Parser(str){
var parser = new DOMParser();
ParsedHtml = parser.parseFromString(str, "text/html");
ParsedHtml.querySelectorAll("*").forEach(function(node) {
//in this for loop the problems that i specified above occur
}
Example execution:
if our input will be :
var samplehtml = '<html><head><script>console.log(1);</script></head><body><div id="111"></div></body></html>'
and we use console.log(node); inside the for loop the output will be :
https://ibb.co/hxzDc8
as you can see in the first iteration the node is the entire html tag including all childrens (head body...) the second is only the head than just the script inside the head than the entire body and than just the div inside the body .
so thats not good for me at all , i need to parse the string as a document and start appending the nodes in the correct order to the iframe thats it
New code:
ParsedHtml.querySelectorAll("head > *").forEach(function(node) {
IframeDoc.head.appendChild(node);
});
ParsedHtml.querySelectorAll("body > *").forEach(function(node) {
IframeDoc.body.appendChild(node);
});

Cannot replace nodes after using TreeWalker

I would like to replace the current text nodes with span nodes.
I have made this JS code but it does not work:
var EditorElement_Content;
var ContentElement_TreeWalker;
//
ContentElement_TreeWalker = document.createTreeWalker(EditorElement_Content, NodeFilter.SHOW_TEXT);
while (ContentElement_TreeWalker.nextNode()) {
var ReplacementNode = document.createElement("span");
ReplacementNode.appendChild(ContentElement_TreeWalker.currentNode);
ContentElement_TreeWalker.currentNode.parentNode.replaceChild(ReplacementNode, ContentElement_TreeWalker.currentNode);
}
Also, the Web explorer console returns this: HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy
What am I doing wrong and why?
Thanks in advance.

DOM - Replace node with an array of nodes (efficiently)

What would be an efficient way to replace a DOM node with an array of nodes
(which are a simple array of detached nodes and not HTMLCollection)
(please no jQuery answers)
Demo page
HTML
<body>
<header>
<div>foo</div>
<div>bar</div>
</header>
</body>
JS
// generate simple dummy array of detached DOM nodes
var link1 = document.createElement('a');
link1.innerHTML = 'xxx';
var link2 = document.createElement('a');
link2.innerHTML = 'yyy';
var nodesArr = [link1, link2];
// get the element to replace at some place in the DOM.
// in the case, the second <div> inside the <header> element
var nodeToReplace = document.querySelectorAll('header > div')[1];
// replace "nodeToReplace" with "nodesArr"
for(let node of nodesArr)
nodeToReplace.parentNode.insertBefore(node, nodeToReplace);
nodeToReplace.parentNode.removeChild(nodeToReplace);
You can use a DocumentFragment instead of the array:
var nodesFragment = document.createDocumentFragment();
nodesFragment.appendChild(link1);
nodesFragment.appendChild(link2);
nodeToReplace.replaceWith(nodesFragment); // experimental, no good browser support
nodeToReplace.parentNode.replaceChild(nodesFragment, nodeToReplace); // standard
However, just inserting multiple elements in a loop shouldn't be much different with regard to performance. Building a document fragment from an existing array might even be slower.
My initial solution was a straightforward iteration:
// iterate on the Array and insert each element before the one to be removed
for(let node of nodesArr)
nodeToReplace.parentNode.insertBefore(node, nodeToReplace);
// remove the chosen element
nodeToReplace.parentNode.removeChild(nodeToReplace);

Categories