David Mark's Javascript Daily - Volume #3 - Tip #6 - How to Get andSet HTML

D

David Mark

How to Get and Set HTML

Setting the inner HTML of an element is often useful. When making
complicated changes to the document tree, allowing the parser to do
most of the work is often the most efficient solution. The question
of whether to deal with a string or object representation of a DOM
structure comes up often in applications that update portions of a
document with XHR results.

Despite there being no standard behind the innerHTML property and the
somewhat awkward feel of dealing with serialized DOM structures, I
virtually always choose the string over the (XML) document object.
It's much simpler, faster, requires less code, less function calls
and, having debuted in IE 4, has enough successful history behind it
to be trusted.

You've probably heard there are bugs galore with this property, but
most implementation differences can hardly be considered bugs. This
is another Microsoft invention that has been copied by every other
browser vendor. Microsoft explicitly disallowed modifying table
structures with this property. For example, you can replace entire
TABLE elements, but you can't replace a row, column group, etc. There
are existing (and standard) objects to modify table structures:-

http://msdn.microsoft.com/en-us/library/ms537484(v=vs.85).aspx

Follow the link at the bottom to the DOM 1 recommendation to find all
of the information you need to modify table structures without
stooping to setting inner HTML.

Same goes for SELECT elements, which also have specialized objects to
modify their structure (e.g. add and remove options). As with most
form controls, they are very easy to manipulate with standard code
that works in virtually every script-enabled browser ever made (even
NN 4!) See the same DOM recommendation.

Keeping these facts in mind during design can save a world of time.
I've found that most designs can do just fine sticking to the
innerHTML properties of DIV elements (and perhaps the occasional TD or
TR, which won't change the structure of containing tables).

The simplest rendition of the setter with simplified feature
detection*:-

// NOTE: Don't use this rendition with anything but DIV's

// Degrades in IE 3 :)

if (document.documentElement && 'string' == typeof
document.documentElement.innerHTML) {
var setHTML = function(el, html) {
el.innerHTML = html;
};
}

Trying to move a complicated project with lots of developers over to
this way of setting HTML? During development, you may want to add
something like:-

if (el.tagName.toLowerCase() != 'div') {
throw new Error('Please only set inner HTML of DIV elements!');
}

....at the outset (and remove on deployment of course). All of the
trouble-making code will become apparent. Eventually, even the
developers who don't read documentation will get the message about not
modifying anything other than DIV's with the setHTML function.

Why would you even bother to wrap that functionality? For one,
because you need the feature detection. Never mind that virtually
every browser made to date has this property. Your applications
should degrade gracefully in those (past or future) browsers that are
the exceptions. Why even try to memorize which host objects and
properties are omnipresent at the moment the function is written?
Just include appropriate feature detection/testing in all function
renditions.

if (setHTML) {
// Put HTML setting application here
}

Also, however unlikely in this case, you might want to replace setHTML
with a more complicated rendition in some future application. You
might consider the debug version with the argument validation to be an
alternate rendition. I prefer to think of it as a debug version of
the same rendition.

But a better question is why you would want to try to modify table
structures by setting inner HTML. Sure, it can be be done in a
roundabout way. Ultimately it comes down to doing what you were
trying to avoid in the first place (i.e. manually appending nodes one
at a time). My Library (and the rest) all go through a lot of
rigamorale to try to make such operations "work" (as best they can)
with varied results. This is what GP libraries do. They don't help
developers avoid costly mistakes. They hide the costly mistakes
behind a "concise" API. If you could only see the mess that goes on
behind the scenes of these things, you'd be in a much better position
to make sound design decisions. Furthermore, you wouldn't be left
with code that can only be reasonably expected to "work" in five or
six of the latest versions of the "core" browsers in their default
configurations *today*.

Every time I hear "don't reinvent the wheel", I think of how many old
versions of jQuery, Dojo, Prototype, Mootools, etc. there are broken
down and rusting on the side of the highway. Also have to wonder what
the average Web developer wants with the "perfect" innerHTML wrapper.
Sure lots of library authors (typically excited and ambitious
beginners) are keen to provide such a thing (as best they can). But
who in their right mind would be in the market for it, particularly
when it is likely a clunky multi-browser facade as opposed to a clean
cross-browser solution? Which strategy do you think will save time in
the long run? Which strategy will the monolithic libraries/framework
always eschew for not having enough mass appeal?

See the problem with such libraries? They constantly strive to
overreach both practicality and the collective ability of their
authors because they are all in some sort of ridiculous competition to
be the most popular library. Does being the most popular lead to
better solutions? Of course not; jQuery is a prime example of a
script getting (much) worse as it grows more popular. It's not hard
to understand why: too many cooks with too many contexts, too many
suggestions, etc. You end up with one fuzzily-defined context (e.g.
works for me here, dude!) for hundreds of functions. Changing any one
function can alter the context in which the collection is expected to
work; it's impossible to pin such things down and no wonder that most
libraries provide little more than a set of shiny browser icons to
indicate their "supported" environments.

In contrast to setting, serializing (getting) HTML from DOM structures
is mostly useless and trying to normalize markup on the client side is
generally a waste of time. An example of an application that does
need to serialize HTML is an editor. But generally, HTML editors
don't *need* to normalize the resulting markup. That's a task for a
server side process. It's the same case as with form validation,
except that providing a client side first defense is a much more
complicated proposition. You have to write the server side validation
anyway.

* Use isHostObjectProperty to detect documentElement

http://www.cinsoft.net/
http://www.twitter.com/cinsoft
http://jsperf.com/browse/david-mark
 
A

Andreas Bergmaier

David said:
Despite there being no standard behind the innerHTML property and the
somewhat awkward feel of dealing with serialized DOM structures, I
virtually always choose the string over the (XML) document object.
It's much simpler, faster, requires less code, less function calls
and, having debuted in IE 4, has enough successful history behind it
to be trusted.

Why is that faster? Only because the native html parser is faster than
the javascript interpreter?
I could understand the use of innerHTML when it's for large static
elements delivered as plain text per ajax - where the server can
serialize the structure into HTML. But when it comes to elements with
event listeners, I absolutely prefer DOM building methods.
Using a "library" function that takes the element name, attributes,
child nodes and event listeners as attributes, I can build dom
structures much easier than (hand)writing serialized html strings.
Also I can't believe that it would be faster to serialize the structure
to a string, process this with the innerHTML parser, and then dig in the
dom tree again to set the listeners (Thats what I really hate jQuery for).
Using plain dom methods (document.createElement,
document.createTextNode, el.appendChild, el.addEventListener) I've never
experienced problems with tables or select elements, and I'm not limited
to divs.
Or does that only work in recent browsers?

Bergi
 
G

Gregor Kofler

Am 2011-11-15 22:26, Andreas Bergmaier meinte:
Why is that faster? Only because the native html parser is faster than
the javascript interpreter?
I could understand the use of innerHTML when it's for large static
elements delivered as plain text per ajax - where the server can
serialize the structure into HTML. But when it comes to elements with
event listeners, I absolutely prefer DOM building methods.
Using a "library" function that takes the element name, attributes,
child nodes and event listeners as attributes, I can build dom
structures much easier than (hand)writing serialized html strings.
Also I can't believe that it would be faster to serialize the structure
to a string, process this with the innerHTML parser, and then dig in the
dom tree again to set the listeners (Thats what I really hate jQuery for).
Using plain dom methods (document.createElement,
document.createTextNode, el.appendChild, el.addEventListener) I've never
experienced problems with tables or select elements, and I'm not limited
to divs.
Or does that only work in recent browsers?

I only resort to innerHTML with "dumb" (and unproblematic) blobs of
markup. Whenever I need references to single elements within the
generated markup, I prefer the approach you outlined.

Gregor
 
J

J.R.

Why is that faster? Only because the native html parser is faster than
the javascript interpreter?
I could understand the use of innerHTML when it's for large static
elements delivered as plain text per ajax - where the server can
serialize the structure into HTML. But when it comes to elements with
event listeners, I absolutely prefer DOM building methods.
Using a "library" function that takes the element name, attributes,
child nodes and event listeners as attributes, I can build dom
structures much easier than (hand)writing serialized html strings.
Also I can't believe that it would be faster to serialize the structure
to a string, process this with the innerHTML parser, and then dig in the
dom tree again to set the listeners (Thats what I really hate jQuery for).
Using plain dom methods (document.createElement,
document.createTextNode, el.appendChild, el.addEventListener) I've never
experienced problems with tables or select elements, and I'm not limited
to divs.
Or does that only work in recent browsers?

Searching the Internet, we can find some performance tests proving that
innerHTML really seems to be faster than the W3C DOM methods. Of course
results may differ significantly from browser to browser and from
version to version. E.g. <http://www.quirksmode.org/dom/innerhtml.html>

However, take a look at this article from Mozilla:
<https://developer.mozilla.org/En/DOM/Element.innerHTML>

Also: innerHTML in HTML 5
<http://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0>
 
T

Thomas 'PointedEars' Lahn

Andreas said:
Why is that faster?

Possibility: An assignment to `innerHTML' would trigger once a markup parser
written entirely in the host environment's native programming language, such
as C++, thereby probably being executed as machine code. Whereas with
several DOM method calls each would trigger the native implementation,
switching to and back from native code in the process. The stack operations
involved in the latter, and the fact that the ECMAScript implementation
would be single-threaded, could contribute to its being slower than the
former.
Only because the native html parser is faster than the javascript
interpreter?

There is no "javascript", and you have a common misconception of how code
written in an ECMAScript implementation is being executed by a Web browser.
I could understand the use of innerHTML when it's for large static
elements delivered as plain text per ajax - where the server can
serialize the structure into HTML.

What you are writing may read sophisticated and even meaningful to the
uninitiated, but it actually makes no sense at all. I strongly suggest you
familiarize yourself with the workings of Web server and browser – and
perhaps English – before making further (English-language) statements on
this topic. For example, the Web *server* does not (need to) serialize
anything if you use XHR to request HTML markup in the response message.


PointedEars
 
A

Andreas Bergmaier

Thomas said:
There is no "javascript"

I know I should spell it "JavaScript", but when I'm writing English my
shift key often stucks - sorry. You don't have to remind me of that, but
thanks.
and you have a common misconception of how code
written in an ECMAScript implementation is being executed by a Web browser.

I hope not, but isn't "interpreting code" a short form for "interpreting
syntax, building a (implementation specific) native structure, and
execute it when requested"?
What you are writing may read sophisticated and even meaningful to the
uninitiated, but it actually makes no sense at all. I strongly suggest you
familiarize yourself with the workings of Web server and browser – and
perhaps English – before making further (English-language) statements on
this topic. For example, the Web *server* does not (need to) serialize
anything if you use XHR to request HTML markup in the response message.

No, you understood me right. A server has same structured data, for
example in a database, which should be displayed on the client's screen.
So either the server generates HTML markup, or it sends the data as some
other type - e.g. json -, which could decrease the file size
significantly. In the first case the script engine would use innerHTML,
in the second it could either build the table with dom methods or
serialize the data to a markup string and use innerHTML again. So which
of the three ways is faster?

Bergi
 
D

Dr J R Stockton

In comp.lang.javascript message <b8aaaf65-2095-4145-8992-651aae0e9a69@p9
g2000vbb.googlegroups.com>, Tue, 15 Nov 2011 11:47:08, David Mark
Keeping these facts in mind during design can save a world of time.
I've found that most designs can do just fine sticking to the
innerHTML properties of DIV elements (and perhaps the occasional TD or
TR, which won't change the structure of containing tables).

The simplest rendition of the setter with simplified feature
detection*:-

// NOTE: Don't use this rendition with anything but DIV's


Were you intending to imply that it is not safe with SPAN elements?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top