Monday, June 7, 2010

How many ways can I say it?

My son, nearing the ripe old age of seven, has been a sponge for all things scientific these past months, so I've been feeding his appetite in any way that he can absorb.  He's chomping at the bit to put his recently acquired knowledge (and his new multimeter) to practical use.  Over the weekend, we were discussing plans for some summer projects, one of which involves developing a small amount of code to drive an Arduino board.  (The Arduino, a small and very affordable I/O board, is the heart and soul of a lot of planned tinkering sessions.)  He had expressed his desire to learn enough of what I do to be helpful in the software development part of these projects, so we sat down and reviewed some of the more rudimentary aspects of code.

I teach by abstract functionality, not by the nuances of a given language, so in this instance, I used Javascript due to the ability to show him things in a rapid environment without the need to worry about compilers and what not.

In this case, we were drawing a box on the screen and populating it with text.  Your typical "Hello, world!" sample, but with the expansion of a for loop, displaying the text in enumerated fashion.  The next phase of the sample code was to replace the text in our box, not simply append to it (as would be useful in something like a status box).

My usual code for this would be browser independent, which requires checking for the availability of the innerText method, and using innerHTML if it didn't exist, as in:
if (document.getElementById('my_element').innerText === 'undefined') {
    document.getElementById('my_element').innerHTML = txt;
} else {
    document.getElementById('my_element').innerText = txt;
}

I've always despised coding for cross-browser compatibility, and this is one of those cases that drives me nuts.  Worse than that is the fact that in certain elements (such as tables), the IE-specific innerText does not always behave properly, sometimes tossing errors, other times simply doing nothing.  Typically, I'll write a wrapper function for situations like this, so that I can call:
updateElementText(object, text);

I pushed this aside until the end of our lesson, and did a little digging, remembering some little nugget of a textNode object in the DOM.

This little guy is supported cross-browser, which I like.  What I don't like is that, unlike innerWhatever, it doesn't automatically exist; one has to create it prior to using it.  The above functionality can be crudely re-written as:
if (!document.getElementById('my_element').childNodes[0]) {
    document.getElementById('my_element').appendChild(document.createTextNode(txt));
} else {
    document.getElementById('my_element').childNodes[0].nodeValue = txt;
}

I'll give you a couple seconds to find the fatal flaw here.  Give up?  What if 'my_element' already has a child node, and that node is not a text node?  Yep.  That's bad.  And since the textNode object only has two properties -- nodeValue (aka data) and length -- the only solution I see here is to walk the child nodes, looking for my_element.nodeType == 3 (TEXT_NODE).  Definitely not an ideal solution.

So, in this case, it appears the un-sexy browser-dependent innerHTML/innerText is the practical solution.

It's a real shame that some of the simplest tasks in Javascript are anything but.

No comments: