Changing HTML in JavaScript without innerHTML - javascript

Let's say my code was something pretty simple like this:
let content = "";
for(let i=0; i<array.length; i++){
content+='<h1>array[i]</h1>';
}
document.getElementById('some_id').innerHTML = content;
I don't like the idea of putting HTML in my JavaScript code, but I don't know any other way of inserting elements into the DOM without using innerHTML, JQuery's html() method, or simply creating new DOM elements programmatically.
In the industry or for best practices, what's the best way to insert HTML elements from JavaScript?
Thanks in advance!

You can use a DOMParser and ES6 string literals:
const template = text => (
`
<div class="myClass">
<h1>${text}</h1>
</div>
`);
You can create a in memory Fragment:
const fragment = document.createDocumentFragment();
const parser = new DOMParser();
const newNode = parser.parseFromString(template('Hello'), 'text/html');
const els = newNode.documentElement.querySelectorAll('div');
for (let index = 0; index < els.length; index++) {
fragment.appendChild(els[index]);
}
parent.appendChild(fragment);
Since the document fragment is in memory and not part of the main DOM tree, appending children to it does not cause page reflow (computation of element's position and geometry). Historically, using document fragments could result in better performance.
Source: https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment
Basically you can use whatever template you want because it's just a function that return a string that you can feed into the parser.
Hope it helps

You can use the createElement() method
In an HTML document, the document.createElement() method creates the HTML element specified by tagName, or an HTMLUnknownElement if tagName isn't recognized.
Here is an example,
document.body.onload = addElement;
function addElement () {
// create a new div element
var newDiv = document.createElement("div");
// and give it some content
var newContent = document.createTextNode("Hi there and greetings!");
// add the text node to the newly created div
newDiv.appendChild(newContent);
// add the newly created element and its content into the DOM
var currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
<!DOCTYPE html>
<html>
<head>
<title>||Working with elements||</title>
</head>
<body>
<div id="div1">The text above has been created dynamically.</div>
</body>
</html>

A flexible and more faster (efficient) way to insert HTML elements using JavaScript's insertAdjacentHTML method. It allows you to specify exactly where to place the element. Possible position values are:
'beforebegin'
'afterbegin'
'beforeend'
'afterend'
Like this:
document.getElementById("some_id").insertAdjacentElement("afterbegin", content);
Here's a Fiddle example

Creating the element programmatically instead of via HTML should have the desired effect.
const parent = document.getElementById('some_id');
// clear the parent (borrowed from https://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript)
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
// loop through array and create new elements programmatically
for(let i=0; i<array.length; i++){
const newElem = document.createElement('h1');
newElem.innerText = array[i];
parentElement.appendChild(newElem);
}

Related

make a DOM element's content be the one of a document fragment

I am trying to insert into a DOM element the content of a document fragment (in pure javascript). The working principle is this:
var a = document.getElementById("a"),
b = document.getElementById("b");
now i place the content of "a" into a document fragment, using a template element
var content = document.createElement("template");
content.innerHTML = a.innerHTML;
content = content.content.cloneNode(true);
now i would like to replace the content of b with the content of content. I tried with a simple b.innerHTML = content.innerHTML;, but it seems like if document fragments doesn't have innerHTML property.
Is this possible to do?
Note: i know this is totally an ineffective way to do the task of making b.innerHTML = a.innerHTML, but obviously this is just a simplification of a bigger task i am managing to do.
You need to create "clones" and by using the template content property you can then use the innerHTML of a and b into your fragments.
Example:
const a = document.getElementById("a"),
b = document.getElementById("b");
// note template will only be parsed, to render it use js...
function initTemplate() {
const container = document.getElementById("container");
const template = document.getElementById("t");
// create a first clone with the innerHTML of a...
const firstClone = template.content.cloneNode(true);
firstClone.textContent = a.innerHTML;
// create a second clone with the innerHTML of b...
const secondClone = template.content.cloneNode(true);
secondClone.textContent = b.innerHTML;
// append to the document
container.appendChild(firstClone);
container.appendChild(secondClone);
}
<p id="a">Paragraph A</p>
<p id="b">Paragraph B</p>
<button onclick="initTemplate()">Init Template</button>
<br>
<div id="container"></div>
<template id="t">
</template>
If you want to check if your browser supports the HTML template element, do something like this:
if ("content" in document.createElement("template") {
// clone the elements as above and append them to the document
}
Mozilla Docs and Mozilla Docs

InnerHTML creates new nodes (new references) each time

I'm creating a document object via DOMImplementation.createHTMLDocument() and i'm inserting some html with the innerHTML property.
after that i'm appending the nodes to the realdom like this:
function appenderController (nodes,target,uid){
for(let i = 0 ; i < nodes.children.length ; i++) {
if(nodes.children[i].children.length > 0){
if(nodes.nodeType === "9"){
appenderController(nodes.children[i],target);
continue;
}
let realnode = appender(target, nodes.children[i]);
appenderController(nodes.children[i],realnode);
}
if (uid && nodes.children[i].nodeName.toLowerCase() === uid) {
nodes.children[i].remove();
return;
}
appender(target, nodes.children[i]);
}
}
in some cases i'm adding more html to our document and because innerhtml creates the entire document again so i'm also adding special attribute to each node to keep track on which nodes i've already appended.
the problem arises when i'm calling appenderController recursively with nodes = fakedocument.body but inside another function i'm updating the innerhtml for our fakedocument and on the next iteration of appenderController it will continue iterating on the old fakedocument.body , how can i keep the reference to my fake document even if i use innerhtml and update the nodes again
EXAMPLE:
var html='
<html>
<head>
</head>
<body>
<div id="somediv1"></div>
</body>
</html>'
;
var dom = document.implementation.createHTMLDocument();
dom.documentElement.innerHTML = html;
var body = dom.body;
var html2='
<html>
<head>
</head>
<body>
<div id="somediv1"></div>
<div id="somediv2"></div>
</body>
</html>'
;
dom.documentElement.innerHTML = html2;
now if we look at "body" var we will not have "somediv2" , that's exactly what's happening in my function , in some point i'm updating the innerhtml of my fake document but the "appenderController" continues to iterate with the old nodes
Assign each element a unique ID. Then access elements by ID, both on the real DOM, and the temporary one.
Do not store references to elements, store their IDs instead.
—
You can also import nodes from one DOM into another instead of replacing the entire tree using innerHTML. That approach will not interfer with iterating parent elements.
https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode

Append method not appending to all elements

I have created my own JS library. In that i am trying to define append method like this:
append: function (els) {
var elChild = document.createElement('span');
elChild.innerHTML = els;
for(i = 0; i < this.length; i++){
this[i].appendChild(elChild);
}
}
Now i am calling this append method in my script tag of HTML page like this:
<body>
<h1 class="first_heading">hello</h1>
<h1 class="second_heading">hi</h1>
<button>Test Me</button>
</body>
<script>
dome.get('h1').append('<p>some text</p>');
</script>
But the problem is all h1 tags not appending the paragraph text. Only last h1 is appending paragraph text. Any solution?
https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild:
The Node.appendChild() method adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position
In other words, the same node can't appear in multiple places in a document. You have to call document.createElement('span') separately for each child you want to create.
As mentioned this this corresponds to the object in scope. In this scenario, it is the window object. Here is a simple way to append the string to all headers
function append(tagname, text) {
// get all tag names
all_t = document.getElementsByTagName(tagname);
// loop through and append the string to the inner html of each tag
for (var x = 0; x < all_t.length; ++x) {
all_t[x].innerHTML += text
}
}
append('h1', '<p>some text</p>')
<h1 class="first_heading">hello</h1>
<h1 class="second_heading">hi</h1>
<button id="b">Test Me</button>

Can I convert an HTML comment to actual HTML using JavaScript?

If I insert a comment node into a document fragment, can I convert it to HTML later? Example:
var fragment = document.createDocumentFragment();
var comment = document.createComment('<div>Testing</div>');
fragment.appendChild(comment);
// Convert comment into actual HTML?
What this would look like inside of a fragment:
<!--<div>Testing</div>-->
And how it should be when done (actual DOM):
<div>Testing</div>
Background:
There are many limitations for inserting HTML into a fragment. Fragments don't support innerHTML or insertAdjacentHTML etc. There are other methods for getting HTML into fragments, but they drop certain elements and leave only the inner text. For example, creating a fragment with the createRange API drops those types of nodes and I will be left with a fragment with just the text node "Data"
var fragment = document.createRange().createContextualFragment('<td>Data</td>');
The hope is that if I can convert a comment into actual DOM, that it will work as expected.
For the entire body you can use that snippet:
var body = document.getElementsByTagName('body')[0],
bodyHtml = body.innerHTML;
while (bodyHtml.indexOf("<!--") !== -1) { // will replace all the comments with empty string
bodyHtml = bodyHtml.replace("<!--", "").replace("-->", "");
}
body.innerHTML = bodyHtml;
Hello
<h1>hi</h1>
<!--<div>Testing</div>-->
<!--<div>Testing</div>-->
It sounds like your underlying problem is that you need to stuff HTML text into a DocumentFragment, so here is a helper function to do so for you.
var container = document.createElement('div')
function createFragmentWithHTML (html, doc) {
var result = (doc || document).createDocumentFragment()
container.innerHTML = html
while (container.firstChild) result.appendChild(container.firstChild)
return result
}
var fragment = createFragmentWithHTML('<div>Testing</div><p><strong>More text</strong></p>')
// do whatever you want with `fragment`
document.body.appendChild(fragment)
Something like this would do the trick
var element = document.getElementById("whatever"); // get the parent element
var comment = element.innerHTML; // get the thml
var html = comment.replace("<!--", "").replace("-->", ""); // remove the comment
element.innerHTML = html;
Text content of an HTML comment can be accessed via the nodeValue property of the Node interface or the data property of the CharacterData interface that the HTML Comment interface inherits from:
<!--<div>Example</div>-->
var code = comment.data; /* Now contains `<div>Example</div>` text */
var code = comment.nodeValue; // Same result.
Fwiw, I have a blog post about using HTML comment as data container.
You can get your comments with element.childNodes method, then use textContent method to get the content of comment, then change it into HTML Element with innerHTML method, then remove the comment node and replace it with Element Node.
parseComments('container');
function parseComments(getContainerId){
var container = document.getElementById(getContainerId);
var nodes = container.childNodes;
for(var i=0;i<nodes.length;i++){
if(nodes[i].nodeType===8){
var virtualCont = document.createElement('DIV');
var getContent = nodes[i].textContent;
virtualCont.innerHTML = getContent;
container.removeChild(nodes[i]);
if(nodes[i+1]){
container.insertBefore(virtualCont.children[0],nodes[i+1]);
} else {
container.appendChild(virtualCont.children[0]);
}
}
}
}
<div id="container">
<h1>some header A</h1>
<!--<p>some hidden content A</p>-->
<p>some content</p>
<!--<p>some hidden content B</p>-->
<p>another content</p>
<!--<h1>some hidden header B</h1>-->
<!--<p>another hidden content C</p>-->
</div>

How can I implement prepend and append with regular JavaScript?

How can I implement prepend and append with regular JavaScript without using jQuery?
Here's a snippet to get you going:
theParent = document.getElementById("theParent");
theKid = document.createElement("div");
theKid.innerHTML = 'Are we there yet?';
// append theKid to the end of theParent
theParent.appendChild(theKid);
// prepend theKid to the beginning of theParent
theParent.insertBefore(theKid, theParent.firstChild);
theParent.firstChild will give us a reference to the first element within theParent and put theKid before it.
Perhaps you're asking about the DOM methods appendChild and insertBefore.
parentNode.insertBefore(newChild, refChild)
Inserts the node newChild as a child of parentNode before the
existing child node refChild. (Returns newChild.)
If refChild is null, newChild is added at the end of the list of
children. Equivalently, and more readably, use
parentNode.appendChild(newChild).
You didn't give us much to go on here, but I think you're just asking how to add content to the beginning or end of an element?
If so here's how you can do it pretty easily:
//get the target div you want to append/prepend to
var someDiv = document.getElementById("targetDiv");
//append text
someDiv.innerHTML += "Add this text to the end";
//prepend text
someDiv.innerHTML = "Add this text to the beginning" + someDiv.innerHTML;
Pretty easy.
If you want to insert a raw HTML string no matter how complex, you can use:
insertAdjacentHTML, with appropriate first argument:
'beforebegin'
Before the element itself.
'afterbegin'
Just inside the element, before its first child.
'beforeend'
Just inside the element, after its last child.
'afterend'
After the element itself.
Hint: you can always call Element.outerHTML to get the HTML string representing the element to be inserted.
An example of usage:
document.getElementById("foo").insertAdjacentHTML("beforeBegin",
"<div><h1>I</h1><h2>was</h2><h3>inserted</h3></div>");
DEMO
Caution: insertAdjacentHTML does not preserve listeners that where attached with .addEventLisntener.
I added this on my project and it seems to work:
HTMLElement.prototype.prependHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
this.insertBefore(div, this.firstChild);
};
HTMLElement.prototype.appendHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
while (div.children.length > 0) {
this.appendChild(div.children[0]);
}
};
Example:
document.body.prependHtml(`Hello World`);
document.body.appendHtml(`Hello World`);
Here's an example of using prepend to add a paragraph to the document.
var element = document.createElement("p");
var text = document.createTextNode("Example text");
element.appendChild(text);
document.body.prepend(element);
result:
<p>Example text</p>
In order to simplify your life you can extend the HTMLElement object. It might not work for older browsers, but definitely makes your life easier:
HTMLElement = typeof(HTMLElement) != 'undefined' ? HTMLElement : Element;
HTMLElement.prototype.prepend = function(element) {
if (this.firstChild) {
return this.insertBefore(element, this.firstChild);
} else {
return this.appendChild(element);
}
};
So next time you can do this:
document.getElementById('container').prepend(document.getElementById('block'));
// or
var element = document.getElementById('anotherElement');
document.body.prepend(div);
In 2017 I know for Edge 15 and IE 12, the prepend method isn't included as a property for Div elements, but if anyone needs a quick reference to polyfill a function I made this:
HTMLDivElement.prototype.prepend = (node, ele)=>{
try { node.insertBefore(ele ,node.children[0]);}
catch (e){ throw new Error(e.toString()) } }
Simple arrow function that's compatible with most modern browsers.
var insertedElement = parentElement.insertBefore(newElement, referenceElement);
If referenceElement is null, or undefined, newElement is inserted at the end of the list of child nodes.
insertedElement The node being inserted, that is newElement
parentElement The parent of the newly inserted node.
newElement The node to insert.
referenceElement The node before which newElement is inserted.
Examples can be found here: Node.insertBefore
You can also use unshift() to prepend to a list
document.write() is not a good practice, some browsers like Chrome give you a warning if you use it, and it may be a bad solution if you are providing it to a customer, they don't want to use your code and see warnings in the debug console!
Also jQuery may also be a bad thing if you are giving your code to a customer who already uses jQuery for other functionality on their site, there will be a conflict if there is already a different version of jQuery running.
If you want to insert content into an iframe, and do that with pure JS, and with no JQuery, and without document.write(), I have a solution.
You can use the following steps
1.Select your iframe:
var iframe = document.getElementById("adblock_iframe");
2.Create an element that you want to insert into the frame, let's say an image:
var img = document.createElement('img');
img.src = "https://server-name.com/upload/adblock" + id + ".jpg";
img.style.paddingLeft = "450px";
//scale down the image is we have a high resolution screen on the client side
if (retina_test_media == true && high_res_test == true) {
img.style.width = "200px";
img.style.height = "50px";
} else {
img.style.width = "400px";
img.style.height = "100px";
}
img.id = "image";
3.Insert the image element into the iframe:
iframe.contentWindow.document.body.appendChild(img);
This is not best way to do it but if anyone wants to insert an element before everything, here is a way.
var newElement = document.createElement("div");
var element = document.getElementById("targetelement");
element.innerHTML = '<div style="display:none !important;"></div>' + element.innerHTML;
var referanceElement = element.children[0];
element.insertBefore(newElement,referanceElement);
element.removeChild(referanceElement);

Categories

Resources