Cross-browser AJAX updates to table elements

Posted by Michael on June 29, 2008 in JavaScript

It seems that one of the toughest Javascript challenge is to get your AJAX code consistently behave between browsers when you’re dealing with IE’s handling of table elements vs. Gecko and other engines. It took quite a bit of finagling to figure out exactly where the issues and trappings are. Most web developers know about IE’s, ahem, rather unque way of handling tables and the likes, but what’s a web application to do if (s)he is gonna bring some sanity to the picture? Well, Mootools 1.2 goes a long ways towards easing your misery in terms of interacting with the DOM and doing some really cool Javascript things in a consistent manner, but you still have to figure out the specifics that you’re encountering for your application’s needs.

My needs were simple. I wanted to use AJAX to pop up an embedded dialog (just some fancy CSS styling to get a DIV hovering over an overlay, really), update some values, and have that info populate back to the table where said information was presently displayed. I also wanted to be able to add new records and delete existing rows through AJAX calls rather than refreshing the whole page.

This article will walk you through the basic approach I used. I’ll leave out the fancy dialogs and all that and just hard-code some buttons so as to keep this article tightly focused on the task at hand: manipulating tables consistently across all browsers through Mootools 1.2.

This technique was borne of some code and help I got from Jan Kassens http://blog.kassens.net/ on the IRC channel for mootools (#mootools at freenode.net).

First, the HTML page:

<table id="mytable" style="border: 1px solid blue">
    <tbody id="mytbody">
    <tr id="row1">
        <td>r1c1</td>
        <td>r1c2</td>
        <td>r1c3</td>
    </tr>
    <tr id="row2">
        <td>r2c1</td>
        <td>r2c2</td>
        <td>r2c3</td>
    </tr>
    <tr id="row3">
        <td>r3c1</td>
        <td>r3c2</td>
        <td>r3c3</td>
    </tr>
    <tr id="row4">
        <td>r4c1</td>
        <td>r4c2</td>
        <td>r4c3</td>
    </tr>
    </tbody>
</table>

Cut and paste that into a more complete HTML document if you’re gonna play with it. But as you can probably see, that code generates a fairly simple table with named table row elements and a named tbody element (which is very important for IE).

One of the first things I found out is that you can’t inject directly on the table element in IE, you have to inject into the TBODY element, otherwise, IE just accepts your injects gleefully without ever presenting them (I know this because I could subsequently select with $(’new_element’) and delete the elements I dynamically created!).

So now that we have that first big “gotcha” out of the way, lets look at some mootools functions you can use to mess with your tables. First, be sure you have mootools loading into your HTML document:

<script type="text/javascript" src="scripts/mootools-1.2-core-nc.js" charset="utf-8"></script>

And now, some javascript to let us do a little table manipulation:

<script type="text/javascript">

	function htmlToElements(str){
		return new Element('div', {html: '<table><tbody>' + str + '</tbody></table>'}).getElement('tr');
	}

	function add_row() {
		var myTBody = $('mytbody');

		newRow = htmlToElements("<tr><td>foo</td><td>bar</td></tr>");
		newRow.inject(myTBody);
	}
	function delete_row() {
		$('row5').dispose();
	}

	function replace_row(row) {
		var newRow = htmlToElements('<tr id="' + row + '"><td>foo</td><td>bar</td><td>replaces!</td></tr>');
		newRow.replaces($(row));
	}
</script>

With this, I can put some buttons on the page to click and call these nifty functions accordingly:

<button onclick="add_row();">add row</button>
<button onclick="delete_row();">delete row</button>
<button onclick="replace_row('row2');">replace row 2</button>

So, lets go over this a bit. delete_row() is the simplest one. It just shows that you can use mootool’s dollar function to grab a TR and throw it away. Works great in FF2 and IE without any tricks. Next up, add_row(). This one turned out to be tricky because I was initially trying to add to the “mytable” TABLE element and it worked in Firefox, but not Internet Explorer. I really got frustrated with this one, trying all manner of innerHTML, outerHTML, DOM manipulators (e.g. appendChild) with *no* success until Jan tipped me off that its the tbody you have to work against.

Alright, so with that nasty one out of the way, the next step was getting rows into IE. It seems that just assigning innerHTML property for a TR was a major no-no and there’s plenty of blogs and other forum posts to tell you all about this one. But I get my new data from the server as HTML, not DOM objects. I needed a simple helper method to get me to DOM elements I could inject. So Jan whips up what I needed in htmlToElements(str) function in which a a fully valid table is constructed (including the TBODY element IE depends on) in which the TR is selected and returned from it. With a TR element in hand, it was embarrassingly simple to call mootool’s inject on the TBODY element.

But wait, there’s a bonus round: You can also replace an existing TR just as easily as you can add a new TR to the table and Internet Explorer (or maybe it was mootools!) tickled me pink in happily complying with my wishes. With this toolset, you can build a fairly comprehensive set of functionality to add, delete, and update your rows in any browser.

10 Comments on Cross-browser AJAX updates to table elements

By links for 2008-06-30 | iKeif on June 30, 2008 at 5:31 pm

[...] Cross-browser AJAX updates to table elements - Ramblings 2 hours agoI’m a firm believer that when problems arise, someone, somewhere will be working on your [...]

By Max on July 3, 2008 at 1:41 pm

Working with .innerHTML and IE is lots of fun! (not)

You’ve encountered a bug in IE that I track as bug 210.
http://webbugtrack.blogspot.com/2007/12/bug-210-no-innerhtml-support-on-tables.html

If you want to keep up to date with when IE fixes this issue (e.g. it isn’t fixed in IE8 Betas yet), view other innerHTML bugs (http://webbugtrack.blogspot.com/search/label/innerHTML) or view other frustrating IE bugs feel free to peruse my bug blog.

Have a great day,
Max

By Michael on September 2, 2008 at 4:44 pm

excellent post. helped a lot!

imo such a htmlToElement method is one of the most missing features of mootools. It’s okay that they prefer working with DOM elements over using plain html for injection but there should at least be a simple bridge supported by the framework.

I’m considering about to create a patch for the Elements constructor, which allows constructing DOM elements from plain HTML.

But i’ts pretty hard to find a concise API for that purpose:

new Element(null,’…’);

is as ugly as..

Element.fromHTML(’…’);

also extending the String class is not the best option?

somewhat like:

“…”.toElement();

ideas? ;) welcome!

and again.. thanks for that article!

By Michael on September 2, 2008 at 7:24 pm

When I’m struggling with a question like yours myself, I usually have to recast the question in the context of what the Framework’s purpose is. In other words, ask yourself how your patches would significantly elevate/improve the framework itself? In this particular case, the framework *does* provide you with ability to generate DOM objects out of HTML strings as I did above by wrapping my intent/purpose for the table elements in the htmlToElements() function call.

Sometimes you have to let the frameworks remain uncluttered and capture your specific use cases into a thin veneer (Adapter pattern) that your application invokes. With this approach, what often happens is a library of useful functions and classes will grow organically and then become quite powerful and useful in of themselves. http://clientside.cnet.com/ is a great example of such a set of libraries growing out from the Mootools framework.

By Michael Aufreiter on September 2, 2008 at 9:45 pm

sure you are right. I’m aware of that and my intension is not to “pollute” the framework itself, but in this specific case I’ve come to the conclusion that there is something missing. IMO it’s a very common use-case to apply server-generated html to the page, and the need for a fake element in order to convert a html-fragment to a DOM Element feels unnatural to me.

I created a post at the mootools newsgroup looking for a mootools equivalent for Prototype’s Element.replace method (with deals with HTML directly) that does exactly that in a straight-forward way without all the hassles.

here’s the link: http://groups.google.com/group/mootools-users/browse_thread/thread/1dffe721ee0e8c1c/184c0d22e0a684b6?hl=en&lnk=gst&q=Element.replace#184c0d22e0a684b6

I’m fine with the mootools way, using DOM Elements through-out the framework, as long as there is a simple way to get this desired DOM Elements. ;-)

I would definitely vote for a HTML to DOM-Elements functionality in mootools-core…

By Jan Kassens on September 27, 2008 at 7:22 pm

MooTools 1.2.1 will likely include a fix for set(’html’, str) on all elements in IE. (table stuff, and select elements)

- jan

By Recent Faves Tagged With "crossbrowser" : MyNetFaves on December 3, 2008 at 8:35 am

[...] ago A Cross-Browser, Bookmarklet Speed Reader First saved by bleulayette | 19 days ago Cross-browser AJAX updates to table elements First saved by jpojman | 20 days ago HOW TO make web pages “cross browser” First saved by [...]

By mark valles on February 20, 2009 at 5:21 am

Here’s to make things a bit easier…

Element.implement({
/**
* Here’s how you do innerHTML with ie6 on mootools.1.2 framework
*
* @params String htmlContent - A string representation of raw HTML which is about to be created.
* @params String wrapperTagName - htmlContent must have some sort of wrapper tag which holds the rest of html content.
* @returns Object - Converted html objects
*/
injectHTML: function( htmlContent, wrapperTagName ){
var x = new Element(’div’, {html: htmlContent}).getElement( wrapperTagName );
x.inject( this );
}
});

By mark valles on February 20, 2009 at 5:35 am

Here’s a better one…

Element.implement({
/**
* Here’s how you do innerHTML with ie6 on mootools.1.2 framework - moerk
*
* @params String htmlContent - A string representation of raw HTML which is about to be created.
* @params String wrapperTagName - htmlContent must have some sort of wrapper tag which holds the rest of html content.
* @returns Object - Converted html objects
*/
injectHTML: function( htmlContent, wrapperTagName ){
var x = new Element(’div’, {html: htmlContent}).getElement( wrapperTagName );
this.empty();
x.inject( this );
}
});

Say you get HTML string like this returned from the server:

“Here’s the list:item1item2″

Calling it would simply be like this:

var eAlertMsg = document.getElementById(’alertMsg’);
$(eAlertMsg).injectHTML( “Here’s the list:item1item2″, “div” );

By mark valles on February 20, 2009 at 5:41 am

i mean…

Say you get HTML string like this returned from the server:

“<div>Here’s the list: <ul><li>item1</li><li>item2</li> </ul></div>″

Calling it would simply be like this:

var eAlertMsg = document.getElementById(’alertMsg’);
$(eAlertMsg).injectHTML( “<div>Here’s the list: <ul><li>item1</li><li>item2</li> </ul></div>″, “div” );

Write a Comment on Cross-browser AJAX updates to table elements

Subscribe

Follow comments by subscribing to the Cross-browser AJAX updates to table elements Comments RSS feed.

More

Read more posts by Michael

About the Author

A software developer and network engineer for over 25 years. Currently developing Microsoft Windows desktop applications with Delphi and web services with Ruby, Ruby on Rails, Ramaze and Javascript. Web services are hosted on CentOS and Ubuntu servers under either Xen or VMWare powered via Apache, passenger, mysql and postgresql.

A model-free wizard DRYing your Views