Looping on Document nodes (Including childrens ) - javascript

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);
});

Related

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.

How to find multiple different matches using regex in Javascript? [duplicate]

var html = '<p>sup</p>'
I want to run document.querySelectorAll('p') on that text without inserting it into the dom.
In jQuery you can do $(html).find('p')
If it's not possible, what's the cleanest way to to do a temporary insert making sure it doesn't interfere with anything. then only query that element. then remove it.
(I'm doing ajax requests and trying to parse the returned html)
With IE 10 and above, you can use the DOM Parser object to parse DOM directly from HTML.
var parser = new DOMParser();
var doc = parser.parseFromString(html, "text/html");
var paragraphs = doc.querySelectorAll('p');
You can create temporary element, append html to it and run querySelectorAll
var element = document.createElement('div');
element.insertAdjacentHTML('beforeend', '<p>sup</p>');
element.querySelectorAll('p')

Javascript: assign onclick inside class

I created a constructor that will handle a custom list control. I created a method in order to allow the user to add elements to the list, and I need to assign event handlers to the click events of the list elements (divs).
A simplified version of the code is here. The list elements are created using the innerHTML property and a string template upon which I substitute specific parts. Later I get the element by it's id and assign it a function in closure:
function prueba(){
var plantilla = '<div id="«id»">«texto»</div>';
var f = function(nombre){
return function(){console.log('mi nombre es ' + nombre)};
};
this.agregar = function(id, texto){
var tmp = plantilla.replace('«id»', id);
tmp = tmp.replace('«texto»', texto);
document.body.innerHTML += tmp;
document.getElementById(id).onclick = f(id);
};
};
The problem is that, apparently, the event handler is unasigned to previous created divs, so is only retained by the last one, as it can be tested with the following code:
var p = new prueba;
p.agregar('i1', 'texto1');
console.log(document.getElementById('i1').onclick.toString());//shows the function code
p.agregar('i2', 'texto2');
console.log(document.getElementById('i2').onclick.toString());//shows the function code
console.log(document.getElementById('i1').onclick.toString());//returns 'null' error
p.agregar('i3', 'texto3');
console.log(document.getElementById('i3').onclick.toString());//shows the function code
console.log(document.getElementById('i2').onclick.toString());//returns 'null' error
This happens in Iceweasel as well as in Chromium. It does NOT happen when I add 'onclick = f(«id»)' in the template (which I cannot do here because of the assigned function scope), and neither happens if I use document.createElement. What am I doing wrong?
You destroy elements previously created when you do this:
document.body.innerHTML += tmp;
Instead use insertAdjacentHMTL() if you want to append using HTML markup.
document.body.insertAdjacentHTML("beforeend", tmp);
Now instead of going through this destructive process...
serialize the existing DOM nodes to HTML
concatenate the new HTML fragment to the serialized nodes
destroy the old nodes
recreate the nodes with the new nodes
...it simply creates the new content and places it before the close of the body element.
Basically, remove element.innerHTML += ... from your coding practices. It's never necessary, it's inefficient and it causes problems like what you've described.
FYI, the .insertAdjacentHTML() method receives 4 different string possibilities as the first argument. Each one designates a position relative to the element on which you're calling it.
The strings are...
"beforebegin"
"afterbegin"
"beforeend"
"afterend"
The labels are pretty self-explanatory. They position the new content before the current element, inside the current element at the beginning, inside the current element at the end, or after the current element, respectively.
Your full code will look like this, which I shortened a bit too since the tmp really isn't needed here:
function prueba(){
var plantilla = '<div id="«id»">«texto»</div>';
var f = function(nombre){
return function(){console.log('mi nombre es ' + nombre)};
};
this.agregar = function(id, texto){
document.body.insertAdjacentHTML("beforeend",
plantilla.replace('«id»', id)
.replace('«texto»', texto));
document.getElementById(id).onclick = f(id);
};
};

Parse HTML string containing script tag and document.write

I have below string. It has nested document.write string statements. I want to add text contents of innermost script to document.
"document.write('<script>document.write(\"<script>document.write(\"Hello World\");<\/script>\");<\/script>')"
How can I parse this string so that Hello World gets added in document. For e.g. html output can be as below.(can be in body or div, anything is ok.)
<body>Hello World</body>
P.S. there can be any number of nested document.write statements. Need to parse this string which can handle n level of nesting.
Well I figured it out now.
var str = "document.write('<script>document.write(\"<script>document.write(\"Hello World\");<\/script>\");<\/script>')";
var aStr, scriptEle = document.createElement('script');
aStr = str.replace(/["']/g, '"');
aStr = aStr.replace(/"<script>document.write/g, "");
aStr = aStr.replace(/;<\/script\>"/g, "");
scriptEle.innerHTML = aStr;
// console.log(aStr);
document.body.appendChild(scriptEle);
This also handles n level of nesting.
You will basically have to tell the script to execute the script inside the <script> tags.
You can achieve this by doing this
var code = "<script>document.write(\"Hello World\");</scr"+"ipt>";
$('body').append($(code)[0]);
Which will happily display hello world in the body tags. You can use this approach to get your script executed by appending it on any tag. Here is the jsfiddle and an SO answer that can give you an idea as to how to be able to execute a js which gets appended dynamically
Hope that helps :)

Is there a way to convert HTML into normal text without actually write it to a selector with Jquery?

I understand so far that in Jquery, with html() function, we can convert HTML into text, for example,
$("#myDiv").html(result);
converts "result" (which is the html code) into normal text and display it in myDiv.
Now, my question is, is there a way I can simply convert the html and put it into a variable?
for example:
var temp;
temp = html(result);
something like this, of course this does not work, but how can I put the converted into a variable without write it to the screen? Since I'm checking the converted in a loop, thought it's quite and waste of resource if keep writing it to the screen for every single loop.
Edit:
Sorry for the confusion, for example, if result is " <p>abc</p> " then $(#mydiv).html(result) makes mydiv display "abc", which "converts" html into normal text by removing the <p> tags. So how can I put "abc" into a variable without doing something like var temp=$(#mydiv).text()?
Here is no-jQuery solution:
function htmlToText(html) {
var temp = document.createElement('div');
temp.innerHTML = html;
return temp.textContent; // Or return temp.innerText if you need to return only visible text. It's slower.
}
Works great in IE ≥9.
No, the html method doesn't turn HTML code into text, it turns HTML code into DOM elements. The browser will parse the HTML code and create elements from it.
You don't have to put the HTML code into the page to have it parsed into elements, you can do that in an independent element:
var d = $('<div>').html(result);
Now you have a jQuery object that contains a div element that has the elements from the parsed HTML code as children. Or:
var d = $(result);
Now you have a jQuery object that contains the elements from the parsed HTML code.
You could simply strip all HTML tags:
var text = html.replace(/(<([^>]+)>)/g, "");
Why not use .text()
$("#myDiv").html($(result).text());
you can try:
var tmp = $("<div>").attr("style","display:none");
var html_text = tmp.html(result).text();
tmp.remove();
But the way with modifying string with regular expression is simpler, because it doesn't use DOM traversal.
You may replace html to text string with regexp like in answer of user Crozin.
P.S.
Also you may like the way when <br> is replacing with newline-symbols:
var text = html.replace(/<\s*br[^>]?>/,'\n')
.replace(/(<([^>]+)>)/g, "");
var temp = $(your_selector).html();
the variable temp is a string containing the HTML
$("#myDiv").html(result); is not formatting text into html code. You can use .html() to do a couple of things.
if you say $("#myDiv").html(); where you are not passing in parameters to the `html()' function then you are "GETTING" the html that is currently in that div element.
so you could say,
var whatsInThisDiv = $("#myDiv").html();
console.log(whatsInThisDiv); //will print whatever is nested inside of <div id="myDiv"></div>
if you pass in a parameter with your .html() call you will be setting the html to what is stored inside the variable or string you pass. For instance
var htmlToReplaceCurrent = '<div id="childOfmyDiv">Hi! Im a child.</div>';
$("#myDiv").html(htmlToReplaceCurrent);
That will leave your dom looking like this...
<div id="myDiv">
<div id="childOfmyDiv">Hi! Im a child.</div>
</div>
Easiest, safe solution - use Dom Parser
For more advanced usage - I suggest you try Dompurify
It's cross-browser (and supports Node js). only 19kb gziped
Here is a fiddle I've created that converts HTML to text
const dirty = "Hello <script>in script<\/script> <b>world</b><p> Many other <br/>tags are stripped</p>";
const config = { ALLOWED_TAGS: [''], KEEP_CONTENT: true, USE_PROFILES: { html: true } };
// Clean HTML string and write into the div
const clean = DOMPurify.sanitize(dirty, config);
document.getElementById('sanitized').innerText = clean;
Input: Hello <script>in script<\/script> <b>world</b><p> Many other <br/>tags are stripped</p>
Output: Hello world Many other tags are stripped
Using the dom has several disadvantages. The one not mentioned in the other answers: Media will be loaded, causing network traffic.
I recommend using a regular expression to remove the tags after replacing certain tags like br, p, ol, ul, and headers into \n newlines.

Categories