118

Instead of tediously search for workarounds for each type of attribute and event when using the following syntax:

elem = document.createElement("div");
elem.id = 'myID';
elem.innerHTML = ' my Text '
document.body.insertBefore(elem,document.body.childNodes[0]);

Is there a way where I can just declare the entire HTML element as a string? like:

elem = document.createElement("<div id='myID'> my Text </div>");
document.body.insertBefore(elem,document.body.childNodes[0]);
YakovL
  • 7,557
  • 12
  • 62
  • 102
Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607

7 Answers7

136

Instead of directly messing with innerHTML it might be better to create a fragment and then insert that:

function create(htmlStr) {
    var frag = document.createDocumentFragment(),
        temp = document.createElement('div');
    temp.innerHTML = htmlStr;
    while (temp.firstChild) {
        frag.appendChild(temp.firstChild);
    }
    return frag;
}

var fragment = create('<div>Hello!</div><p>...</p>');
// You can use native DOM methods to insert the fragment:
document.body.insertBefore(fragment, document.body.childNodes[0]);

Benefits:

  1. You can use native DOM methods for insertion such as insertBefore, appendChild etc.
  2. You have access to the actual DOM nodes before they're inserted; you can access the fragment's childNodes object.
  3. Using document fragments is very quick; faster than creating elements outside of the DOM and in certain situations faster than innerHTML.

Even though innerHTML is used within the function, it's all happening outside of the DOM so it's much faster than you'd think...

James
  • 109,676
  • 31
  • 162
  • 175
  • 3
    I bet this createDocumentFragment() wouldn't be as good as the "legacy" innerHTML. – Robin Rodricks May 02 '09 at 14:12
  • I really would like backward compatibility, so how could I use this alongside innerHTML, like I check if this method is not null, maybe? – Robin Rodricks May 02 '09 at 14:12
  • 2
    This method works in all modern browsers. For IE 5.5 and below you could perform a check like:: if (document.createDocumentFragment) { create('...'); } else { /* Use innerHTML perhaps */ } – James May 02 '09 at 14:41
  • Exactly my point, thanks for the code snippet! I like your method since its quick, so making this the default behavior on all modern browsers is preferable. – Robin Rodricks May 02 '09 at 15:49
  • crescentfresh - anomalies... In 99% of cases it won't fail; where it does you'll have to venture another abstraction. – James May 02 '09 at 18:59
  • 39
    crescentfresh, I help on SO because I enjoy helping people; I'm sorry if a solution is not 100% operable in all situations. If you're so concerned why not offer your own solution instead of being unnecessarily critical of already-offered answers? With any abstraction there will always be edge-cases! This is a learning environment - we're not here to give people a solution on a plate - we're here to help each other become better at what we do! And sorry, but where did anyone mention "AJAX"? – James May 02 '09 at 20:50
  • 1
    Fair enough. My apologies. I just feel that there're so many abstractions on here with zero caveats mentioned. – Crescent Fresh May 03 '09 at 13:12
97

You want this

document.body.insertAdjacentHTML( 'afterbegin', '<div id="myID">...</div>' );
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • @JonnyLeeds this doesn't seem to work on `body` element but works fine on any other element. – Phill Feb 19 '15 at 09:17
  • 1
    @Phill Of course it works on ``. As a matter of fact, running it on `` is my standard method of injecting stuff like style sheets into web pages via the console. What issue are you having? – Šime Vidas Feb 19 '15 at 16:22
  • 1
    @ŠimeVidas probably because the body element wasn't defined at execution time ... i paired your line with window.onload = function() { … } to prevent that problem – GDY Oct 19 '16 at 08:23
  • @Grandy Phill wrote that it works on other elements. If `` were undefined, so would be all other elements, so this is likely not Phill’s issue. – Šime Vidas Oct 19 '16 at 17:36
  • @ŠimeVidas Hmm yeah you right ... if it works on anything it should work on body too – GDY Oct 20 '16 at 04:58
  • this is great! Unfortunately, maybe because I am in a table, somehow all HTML tags are stripped from what I insert... – xeruf Jul 03 '23 at 08:40
39

Have a look at insertAdjacentHTML

var element = document.getElementById("one");
var newElement = '<div id="two">two</div>'
element.insertAdjacentHTML( 'afterend', newElement )
// new DOM structure: <div id="one">one</div><div id="two">two</div>

position is the position relative to the element you are inserting adjacent to:

'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

svnm
  • 22,878
  • 21
  • 90
  • 105
17

In old school JavaScript, you could do this:

document.body.innerHTML = '<p id="foo">Some HTML</p>' + document.body.innerHTML;

In response to your comment:

[...] I was interested in declaring the source of a new element's attributes and events, not the innerHTML of an element.

You need to inject the new HTML into the DOM, though; that's why innerHTML is used in the old school JavaScript example. The innerHTML of the BODY element is prepended with the new HTML. We're not really touching the existing HTML inside the BODY.

I'll rewrite the abovementioned example to clarify this:

var newElement = '<p id="foo">This is some dynamically added HTML. Yay!</p>';
var bodyElement = document.body;
bodyElement.innerHTML = newElement + bodyElement.innerHTML;
// note that += cannot be used here; this would result in 'NaN'

Using a JavaScript framework would make this code much less verbose and improve readability. For example, jQuery allows you to do the following:

$('body').prepend('<p id="foo">Some HTML</p>');
Mathias Bynens
  • 144,855
  • 52
  • 216
  • 248
  • Pretty good. But I was interested in declaring the source of a new element's attributes and events, not the innerHTML of an element. – Robin Rodricks May 02 '09 at 10:11
  • Can I use "document.body" instead of "document.getElementsByTagName" ?? Wouldn't it be more backward compatible? – Robin Rodricks May 02 '09 at 10:28
  • 1
    `document.getElementsByTagName('body')[0]` can indeed be replaced by `document.body`, good point. However, if you want to prepend or append the new HTML to another existing element instead of the BODY, you'll have to use `document.getElementById()` and/or `document.getElementsByTagName()`; that's why I used it in the example. – Mathias Bynens May 02 '09 at 10:37
  • $('body').insertBefore('

    Some HTML

    ') won't work. You're not inserting the before the

    !!???

    – James May 02 '09 at 10:59
  • 1
    Also, your code can get very messy very quickly if you're directly adding/inserting "innerHTML" – James May 02 '09 at 11:09
  • I must've overlooked that one, JimmyP. Thanks! It's fixed now. – Mathias Bynens May 02 '09 at 11:34
  • 1
    @JimmyP: What rubbish! The very purpose of this question is to be able to directly insert HTML as strings instead of playing around with functions to construct it, attrib by attrib, node by node, element by element. – Robin Rodricks May 02 '09 at 14:11
  • jeremy, it's not rubbish; check out my abstraction below. All this needs is some polishing - I am not saying don't use innerHTML. – James May 02 '09 at 14:43
  • @JimmyP: I meant your point about "gets messy" is rubbish, since I *want* to edit/see the "raw" HTML!. – Robin Rodricks May 02 '09 at 15:44
  • 1
    this detaches all event handlers and may cause memory leaks big time. – adardesign Aug 22 '10 at 20:56
  • @adardesign Of course, but that is true for every possible solution to OP’s problem (i.e. setting the HTML of the entire document based on a string). Why the downvote? – Mathias Bynens Jun 29 '11 at 11:08
  • @MathiasBynens what if I want to add attribute without value in this context, like tooltip `
    ..
    `
    – k11k2 Aug 22 '21 at 05:18
1

To my knowledge, which, to be fair, is fairly new and limited, the only potential issue with this technique is the fact that you are prevented from dynamically creating some table elements.

I use a form to templating by adding "template" elements to a hidden DIV and then using cloneNode(true) to create a clone and appending it as required. Bear in ind that you do need to ensure you re-assign id's as required to prevent duplication.

Rpc
  • 11
  • 1
0

As others said the convenient jQuery prepend functionality can be emulated:

var html = '<div>Hello prepended</div>';
document.body.innerHTML = html + document.body.innerHTML;

While some say it is better not to "mess" with innerHTML, it is reliable in many use cases, if you know this:

If a <div>, <span>, or <noembed> node has a child text node that includes the characters (&), (<), or (>), innerHTML returns these characters as &amp, &lt and &gt respectively. Use Node.textContent to get a correct copy of these text nodes' contents.

https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML

Or:

var html = '<div>Hello prepended</div>';
document.body.insertAdjacentHTML('afterbegin', html)

insertAdjacentHTML is probably a good alternative: https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML

taseenb
  • 1,378
  • 1
  • 16
  • 31
  • upvoted because you pointed me at `textContent`, the solution to copying the full HTMl from one node to another :) – xeruf Jul 03 '23 at 08:44
-2

If you want to insert HTML code inside existing page's tag use Jnerator. This tool was created specially for this goal.

Instead of writing next code

    var htmlCode = '<ul class=\'menu-countries\'><li
        class=\'item\'><img src=\'au.png\'></img><span>Australia </span></li><li
        class=\'item\'><img src=\'br.png\'> </img><span>Brazil</span></li><li
        class=\'item\'> <img src=\'ca.png\'></img><span>Canada</span></li></ul>';
    var element = document.getElementById('myTag');
    element.innerHTML = htmlCode;

You can write more understandable structure

    var jtag = $j.ul({
        class: 'menu-countries',
        child: [
            $j.li({ class: 'item', child: [
                $j.img({ src: 'au.png' }),
                $j.span({ child: 'Australia' })
            ]}),
            $j.li({ class: 'item', child: [
                $j.img({ src: 'br.png' }),
                $j.span({ child: 'Brazil' })
            ]}),
            $j.li({ class: 'item', child: [
                $j.img({ src: 'ca.png' }),
                $j.span({ child: 'Canada' })
            ]})
        ]
    });
    var htmlCode = jtag.html();
    var element = document.getElementById('myTag');
    element.innerHTML = htmlCode;
Berezh
  • 942
  • 9
  • 12