Replace element contents with document fragment javascript
Asked Answered
C

3

20

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.

Coprolalia answered 9/11, 2012 at 15:19 Comment(7)
Just curious, why the aversion to libraries?Starnes
Project spec beyond my control. Also, kind of fun to try to do things "old school".Coprolalia
@pimvdb essentially, yes. But I'd like to do it in one swoop, to avoid page re-draws (I could innerHTML = "" then appendChild, but that's 2 re-draws).Coprolalia
@Randy Hall: Hm, well, since a fragment only exists at one place at a time, you could replace each child node again and again, so that they are implicitly removed: jsfiddle.net/tMGFM. But I'm not sure if that's what you want - just element.innerHTML = ""; and then appending should not do a redraw in between.Rima
@Rima that should be an answer, not a comment. It totally works. However, there's two issues still: 1) I'd like an answer without the loop (if possible) and 2) theoretically, the browser redraws each time there's a node change. This will perform a node change for each element I'm replacing. Right direction thought!Coprolalia
Maybe appending the fragment to a detached element (to avoid a redraw) and then transfering the innerHTML is what you want? jsfiddle.net/tMGFM/1 Still, I've never seen redraws when manipulating multiple times in a row.Rima
pimvdb, you are way too fast, but I have the same solution :)Crossarm
C
25

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/

Crossarm answered 9/11, 2012 at 15:23 Comment(3)
Not quite, sorry. This replaces the element with the fragment, I need to replace the element's content with the fragment.Coprolalia
Better! Ideally, I could do it in one go, so as to avoid a browser redraw on element.innerHTML = "" - however, this is probably the best solution so far (also suggested by @pimvdb). Still hoping someone has something magic!Coprolalia
See answer below, very similar to your element clone method but with ranges.Coprolalia
O
7

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)
Oxyacid answered 15/8, 2017 at 11:24 Comment(1)
update answer . you can delete now your extra comments .for cleaner wallOxyacid
C
1

EDIT (cause my original answer was just plain dumb):

var rep = document.createElement("div");
rep.appendChild(frag);
element.innerHTML = rep.innerHTML;
Coprolalia answered 9/11, 2012 at 15:45 Comment(5)
Looks like this strips all elements and only sets the textual contents. Personally, I think all "magical" solutions are a bit... odd. removeChild/appendChild(frag) should work fine.Rima
In old IE this will only grab text I believe. However, I'm not worried about anything but the most recent browsers (and mostly just mobile). I'll edit to reflect this in my answer.Coprolalia
Well, it fails for me on Chrome 23. Just so you know :)Rima
Nevermind, you're completely correct. It is destroying html elements. -1 to my own answer =/Coprolalia
if you're going to go with ranges, I'd suggest taking a look at code.google.com/p/rangy (I know, no libraries). It normalizes alot of inconsistencies across browsersCrossarm

© 2022 - 2024 — McMap. All rights reserved.