Replace element contents with document fragment javascript - javascript

I'm trying to replace all contents of an element with a document fragment:
var frag = document.createDocumentFragment()
The document fragment is being created just fine. No problems there. I add elements to it just fine, no problems there either. I can append it using element.appendChild(frag). That works just fine too.
I'm trying to create a "replace" method similar to jQuery's HTML. I'm not worried about old-browser compatibility. Is there a magical function to replace all content of an element?
I have tried element.innerHTML = frag.cloneNode(true), (as per every 'replace element content' wiki I could find), that doesn't work. It gives me <div>[object DocumentFragment]</div>.
No libraries, please, not even a jQuery solution.
For clarity, I'm looking for a "magic" solution, I know how to remove all the existing elements one at a time and then append my fragment.

Have you tried replaceChild
something like this
element.parentNode.replaceChild(frag, element)
source: https://developer.mozilla.org/en-US/docs/DOM/Node.replaceChild
original jsFiddle: http://jsfiddle.net/tomprogramming/RxFZA/
EDIT: ahh, I didn't see replace contents. Well, just remove them first!
element.innerHTML = "";
element.appendChild(frag);
jsfiddle: http://jsfiddle.net/tomprogramming/RxFZA/1/
note that in the jsfiddle, I only use jquery to hook up the button, the entirety of the click handler is raw javascript.
Edit2: also suggested by pimvdb, but just append the new stuff to a detached element and replace.
var newElement = element.cloneNode();
newElement.innerHTML = "";
newElement.appendChild(frag);
element.parentNode.replaceChild(newElement, element);
http://jsfiddle.net/tomprogramming/RxFZA/3/

2017:
Try this Magic answer from ContentEditable field and Range
var range = document.createRange(); // create range selection
range.selectNodeContents($element); // select all content of the node
range.deleteContents() // maybe there is replace command but i'm not find it
range.insertNode(frag)

EDIT (cause my original answer was just plain dumb):
var rep = document.createElement("div");
rep.appendChild(frag);
element.innerHTML = rep.innerHTML;

Related

Can I create a self-closing element with createElement?

I'm trying to append a line of HTML before all the children of the body.
Right now I have this:
// Prepend vsr-toggle
var vsrToggle = document.createElement("div");
vsrToggle.innerHTML = "<input type='checkbox' name='sr-toggle' id='srToggle'><label for='srToggle' role='switch'>Screen reader</label>"
document.body.insertBefore(vsrToggle, pageContent);
It's working fine because the HTML is being added to the created div. However, I need to prepend this element without wrapping it in a div.
Is there a way to prepend the HTML without first creating an element? If not, can I create the input as a self-closing element and append the label to it?
Is there a better way to achieve this?
Cheers!
Use document.createDocumentFragment() to create a node, that isn't automatically added to the document. You can then add elements to this fragment and finally add it to the document.
This is a good link: Document fragment
How to use:
var fragment = document.createDocumentFragment();
fragment.innerHTML = '<input />';
document.body.appendChild(fragment);
I ended up using createRange and createContextualFragment to turn the string into a node that I could prepend using insertBefore.:
// Prepend vsr-toggle
var vsrToggle = document.createRange().createContextualFragment("<input
type='checkbox' name='sr-toggle' id='srToggle'><label for='srToggle'
role='switch'>Screen reader</label>");
document.body.insertBefore(vsrToggle, pageContent);
Edit: As Poul Bak showed, there is a very useful feature in the DOM API for that. Creating elements separately (instead of having them parsed as a string) allows more control over the elements added (for example you can outright attach an event listener without queryiing it from the DOM later), but for a larger amounts of elements it quickly becomes very verbose.
Create each element separately, and insert it before the body content using
document.body.insertBefore(newNode, document.body.firstChild);
const vsrToggle = document.createElement("input");
vsrToggle.name="sr-toggle";
vsrToggle.id="srToggle";
vsrToggle.type="checkbox";
const vsrToggleLabel = document.createElement("label");
vsrToggleLabel.setAttribute("for", vsrToggle.id);
vsrToggleLabel.setAttribute("role", "switch");
vsrToggleLabel.textContent = "Screen reader";
document.body.insertBefore(vsrToggle, document.body.firstChild);
document.body.insertBefore(vsrToggleLabel, document.body.firstChild);
<body>
<h1>Body headline</h1>
<p>Some random content</p>
</body>

Creating elements within elements with javascript

Noob here. I searched on the internet a bit to find an answer to a question and I can't seem to find any (and that brings my hope down). So here I am.
I was wondering if there is a way to create an HTML element with javascript, BUT inside the newly created HTML element to create also another HTML element with javascript. I guess you can call it elementception //wink
To be more specific, I would like to create a paragraph with text, but I would like to include links in that text (or possibly buttons?).
var para = document.createElement("P");
var t = document.createTextNode("This is a paragraph. Can I do this: <a href='blabla'>like so?</a>");
para.appendChild(t);
document.body.appendChild(para);
I tried writing HTML tags inside the strings of the TextNode, but even I can see that was stupid of me. Is there a noobish(simple) way to achieve this, or any way at all? If I'm asking the impossible, please be harsh and blunt about it, so that I never ask questions again.
Thanks.
The simplest way to do this would be:
para.innerHTML = 'This is a paragraph. Here is a link: like so?';
I would use the DOM API approach instead of using innerHTML for readability, maintainability and security reasons. Sure innerHTML has been around for a long time, but just because it is easy doesn't mean you should use it for everything.
As well, if you're going to be learning JavaScript you should get acquainted with the DOM API sooner than later. It will save you a lot of headaches down the road if you get the hang of the API now.
// Create the parent and cache it in a variable.
var para = document.createElement( "p" );
// Create a text node and append it to the child.
// We don't need to cache this one because we aren't accessing it again.
para.appendChild( document.createTextNode( "This is a paragraph. Can I do this: " ) );
// Create our link element and cache it in a variable.
var link = document.createElement( "a" );
// Set the link's href attribute.
link.setAttribute( 'href', 'blabla' );
// Create a text node and append it to the link
// We don't need to cache the text node.
link.appendChild( document.createTextNode( 'like so?' ));
// Append the link to the parent.
para.appendChild( link );
// Append the parent to the body.
document.body.appendChild( para );
DOM API methods used:
Document.createElement()
Document.createTextNode()
Node.appendChild()
Element.setAttribute()
Further reading:
Document Object Model (DOM)
Element.innerHTML Security Considerations
Advantages of createElement over innerHTML?
Simply use innerHTML attribute to put HTML inside your element instead of createTexteNode, here's what you need:
var para = document.createElement("P");
para.innerHTML = "This is a paragraph. Can I do this: <a \"blabla\">like so?</a>";
document.body.appendChild(para);
Because as its name says, document.createTextNode() will only create a text and can't create HTML elements.
var para = document.createElement("P");
para.innerHTML = "This is a paragraph. Can I do this: like so?";
document.body.appendChild(para);

document.write() and Ajax - Doesn't work, looking for an alternative

I recently asked a question here, and received a great response (which I will shortly be accepting the most active answer of, barring better alternatives arise) but unfortunately it seems the of the two options suggested, neither will be compatible with Ajax (or any dynamically added content that includes such "inline-relative jQuery")
Anyways, my question pertains to good ole' document.write().
While a page is still rendering, it works great; not so much when an appended snippet contains it. Are there any alternatives that won't destroy the existing page content, yet still append a string inline, as in where the call is occurring?
In other words, is there a way/alternative to document.write() that when called post-render, doesn't destroy existing page content? An Ajax friendly version so to speak?
This is where I'm going:
var _inline_relative_index = 0;
function $_inlineRelative(){
// i hate non-dedicated string concatenation operators
var inline_relative_id = ('_inline_relative_{index}').replace('{index}', (++_inline_relative_index).toString());
document.write(('<br id="{id}" />').replace('{id}', inline_relative_id));
return $(document.getElementById(inline_relative_id)).remove().prev('script');
}
And then:
<div>
<script type="text/javascript">
(function($script){
// the container <div> background is now red.
$script.parent().css({ 'background-color': '#f00' });
})($_inlineRelative());
</script>
</div>
you have access to the innerHTML property of each DOM node. If you set it straight out you might destroy elements, but if you append more HTML to it, it'll preserve the existing HTML.
document.body.innerHTML += '<div id="foo">bar baz</div>';
There are all sorts of nuances to the sledgehammer that is innerHTML, so I highly recommend using a library such as jQuery to normalize everything for you.
You can assign id to the script tag and replace it with the new node.
<p>Foo</p>
<script type="text/javascript" id="placeholder">
var newElement = document.createElement('div');
newElement.id='bar';
var oldElement = document.getElementById('placeholder');
oldElement.parentNode.replaceChild(newElement, oldElement);
</script>
<p>Baz</p>
And if you need to insert html from string, than you can do it like so:
var div = document.createElement('div');
div.innerHTML = '<div id="bar"></div>';
var placeholder = document.getElementById('placeholder'),
container = placeholder.parentNode,
elems = div.childNodes,
el;
while (el = elems[0]) {
div.removeChild(el);
container.insertBefore(el, placeholder);
}
container.removeChild(placeholder);

How to append text to a div element?

I’m using AJAX to append data to a <div> element, where I fill the <div> from JavaScript. How can I append new data to the <div> without losing the previous data found in it?
Try this:
var div = document.getElementById('divID');
div.innerHTML += 'Extra stuff';
Using appendChild:
var theDiv = document.getElementById("<ID_OF_THE_DIV>");
var content = document.createTextNode("<YOUR_CONTENT>");
theDiv.appendChild(content);
Using innerHTML:
This approach will remove all the listeners to the existing elements as mentioned by #BiAiB. So use caution if you are planning to use this version.
var theDiv = document.getElementById("<ID_OF_THE_DIV>");
theDiv.innerHTML += "<YOUR_CONTENT>";
Beware of innerHTML, you sort of lose something when you use it:
theDiv.innerHTML += 'content';
Is equivalent to:
theDiv.innerHTML = theDiv.innerHTML + 'content';
Which will destroy all nodes inside your div and recreate new ones. All references and listeners to elements inside it will be lost.
If you need to keep them (when you have attached a click handler, for example), you have to append the new contents with the DOM functions(appendChild,insertAfter,insertBefore):
var newNode = document.createElement('div');
newNode.innerHTML = data;
theDiv.appendChild(newNode);
If you want to do it fast and don't want to lose references and listeners use: .insertAdjacentHTML();
"It does not reparse the element it is being used on and thus it does not corrupt the existing elements inside the element. This, and avoiding the extra step of serialization make it much faster than direct innerHTML manipulation."
Supported on all mainline browsers (IE6+, FF8+,All Others and Mobile): http://caniuse.com/#feat=insertadjacenthtml
Example from https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
// <div id="one">one</div>
var d1 = document.getElementById('one');
d1.insertAdjacentHTML('afterend', '<div id="two">two</div>');
// At this point, the new structure is:
// <div id="one">one</div><div id="two">two</div>
If you are using jQuery you can use $('#mydiv').append('html content') and it will keep the existing content.
http://api.jquery.com/append/
IE9+ (Vista+) solution, without creating new text nodes:
var div = document.getElementById("divID");
div.textContent += data + " ";
However, this didn't quite do the trick for me since I needed a new line after each message, so my DIV turned into a styled UL with this code:
var li = document.createElement("li");
var text = document.createTextNode(data);
li.appendChild(text);
ul.appendChild(li);
From https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent :
Differences from innerHTML
innerHTML returns the HTML as its name indicates. Quite often, in order to retrieve or write text within an element, people use innerHTML. textContent should be used instead. Because the text is not parsed as HTML, it's likely to have better performance. Moreover, this avoids an XSS attack vector.
Even this will work:
var div = document.getElementById('divID');
div.innerHTML += 'Text to append';
An option that I think is better than any of the ones mentioned so far is Element.insertAdjacentText().
// Example listener on a child element
// Included in this snippet to show that the listener does not get corrupted
document.querySelector('button').addEventListener('click', () => {
console.log('click');
});
// to actually insert the text:
document.querySelector('div').insertAdjacentText('beforeend', 'more text');
<div>
<button>click</button>
</div>
Advantages to this approach include:
Does not modify the existing nodes in the DOM; does not corrupt event listeners
Inserts text, not HTML (Best to only use .insertAdjacentHTML when deliberately inserting HTML - using it unnecessarily is less semantically appropriate and can increase the risk of XSS)
Flexible; the first argument to .insertAdjacentText may be beforebegin, beforeend, afterbegin, afterend, depending on where you'd like the text to be inserted
you can use jQuery. which make it very simple.
just download the jQuery file add jQuery into your HTML
or you can user online link:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
and try this:
$("#divID").append(data);
The following method is less general than others however it's great when you are sure that your last child node of the div is already a text node. In this way you won't create a new text node using appendData MDN Reference AppendData
let mydiv = document.getElementById("divId");
let lastChild = mydiv.lastChild;
if(lastChild && lastChild.nodeType === Node.TEXT_NODE ) //test if there is at least a node and the last is a text node
lastChild.appendData("YOUR TEXT CONTENT");
java script
document.getElementById("divID").html("this text will be added to div");
jquery
$("#divID").html("this text will be added to div");
Use .html() without any arguments to see that you have entered.
You can use the browser console to quickly test these functions before using them in your code.
Why not just use setAttribute ?
thisDiv.setAttribute('attrName','data you wish to append');
Then you can get this data by :
thisDiv.attrName;

How to insert HTML string in between two DOM nodes?

Consider following DOM fragment:
<div id="div-1">foo</div>
<div id="div-2">bar</div>
Is it possible to insert HTML string (EDIT: that contains tags to render) between divs without wrapping it in another div (EDIT: or some other tag) created via document.createElement and setting its innerHTML property?
Most browsers support element#insertAdjacentHTML(), which finally became standard in the HTML5 specification. Unfortunately, Firefox 7 and lower don't support it, but I managed to find a workaround that uses ranges to insert the HTML. I've adapted it below to work for your scenario:
var el = document.getElementById("div-2"),
html = "<span>Some HTML <b>here</b></span>";
// Internet Explorer, Opera, Chrome, Firefox 8+ and Safari
if (el.insertAdjacentHTML)
el.insertAdjacentHTML ("beforebegin", html);
else {
var range = document.createRange();
var frag = range.createContextualFragment(html);
el.parentNode.insertBefore(frag, el);
}
Live example: http://jsfiddle.net/AndyE/jARTf/
This does it for straight text, which is how I read your original question (see below for an update for strings that include tags):
var div = document.getElementById('div-2');
var textNode = document.createTextNode('your text');
div.parentNode.insertBefore(textNode, div);
Live example
If you start with:
<div id="div-1">foo</div>div id="div-2">bar</div>
(note that there's no whitespace between them) then the result of the above is exactly what you would get with this HTML:
<div id="div-1">foo</div>your text<div id="div-2">bar</div>
If you really have that whitespace between the divs, you'll already have a text node there and the above will insert another one next to it. For virtually all intents and purposes, that doesn't matter, but if that bothers you, you can append to the existing text node instead if you like:
var text = 'your text';
var div = document.getElementById('div-2');
var prev = div.previousSibling;
if (prev && prev.nodeType == 3) { // 3 == TEXT_NODE
// Prev is a text node, append to it
prev.nodeValue = prev.nodeValue + text;
}
else {
// Prev isn't a text node, insert one
var textNode = document.createTextNode(text);
div.parentNode.insertBefore(textNode, div);
}
Live example
Links to W3C docs: insertBefore, createTextNode
Including HTML tags
In your revised question you've said you want to include tags to be interpreted in doing all this. It's possible, but it's roundabout. First you put the HTML string into an element, then you move the stuff over, like this:
var text, div, tempdiv, node, next, parent;
// The text
text = 'your text <em>with</em> <strong>tags</strong> in it';
// Get the element to insert in front of, and its parent
div = document.getElementById('div-2');
parent = div.parentNode;
// Create a temporary container and render the HTML to it
tempdiv = document.createElement('div');
tempdiv.innerHTML = text;
// Walk through the children of the container, moving them
// to the desired target. Note that we have to be sure to
// grab the node's next sibling *before* we move it, because
// these things are live and when we moev it, well, the next
// sibling will become div-2!
node = tempdiv.firstChild;
next = node.nextSibling;
while (node) {
parent.insertBefore(node, div);
node = next;
next = node ? node.nextSibling : undefined;
}
Live example
But here there be dragons, you have to select the container element as appropriate to the content you're inserting. For instance, we couldn't use a <tr> in your text with the code above because we're inserting it into a div, not a tbody, and so that's invalid and the results are implementation-specific. These sorts of complexities are why we have libraries to help us out. You've asked for a raw DOM answer and that's what the above is, but I really would check out jQuery, Closure, Prototype, YUI, or any of several others. They'll smooth a lot of stuff over for you.
var neuB = document.createElement("b");
var neuBText = document.createTextNode("mit fettem Text ");
neuB.appendChild(neuBText);
document.getElementById("derText").insertBefore(neuB, document.getElementById("derKursiveText"));
You search for: insertBefore
Using jquery it is very simple:
$("#div-1").after("Other tag here!!!");
See: jquery.after
It is obvious that javascript is not a pure javascript solution.

Categories