Can't set innerHTML on tbody in IE
I have a table like this:
<table>
<thead>
<tr>
<th colspan="1">a</th>
<th colspan="3">b</th>
</tr>
</thead>
<tbody id="replaceMe">
<tr>
<td>data 1</td>
<td>data 2</td>
<td>data 3</td>
<td>data 4</td>
</tr>
</tbody>
</table>
and a method returns me the following after an ajax request:
<tr>
<td>data 1 new</td>
<td>data 2 new</td>
<td>data 3 new</td>
<td>data 4 new</td>
</tr>
I want to change the innerHTML like
document.getElementById('replaceMe').innerHTML = data.responseText;
However, it seems that IE can't set innerHTML on <tbody>
. Can anyone help me with a simple workaround for this issue?
That is true, innerHTML on tbody elements is readOnly in IE
The property is read/write for all objects except the following, for which it is read-only: COL, COLGROUP, FRAMESET, HEAD, HTML, STYLE, TABLE, TBODY, TFOOT, THEAD, TITLE, TR.
source: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
You can do something like this to work around it:
function setTBodyInnerHTML(tbody, html) {
var temp = tbody.ownerDocument.createElement('div');
temp.innerHTML = '<table>' + html + '</table>';
tbody.parentNode.replaceChild(temp.firstChild.firstChild, tbody);
}
Basically it creates a temporary node into which you inject a full table
. Then it replaces the tbody
with the tbody
from the injected table
. If it proves to be slow, you could make it faster by caching temp
instead of creating it each time.
Create a temp node to store a table in, then copy them to the tbody
var tempNode = document.createElement('div');
tempNode.innerHTML = "<table>" + responseText+ "</table>";
var tempTable = tempNode.firstChild;
var tbody = // get a reference to the tbody
for (var i=0, tr; tr = tempTable.rows[i]; i++) {
tbody.appendChild(tr);
}
Both the answers above seem a bit unclear. Plus, the created div is never removed, so calling those functions repeatedly eats memory. Try this:
// this function must come before calling it to properly set “temp”
function MSIEsetTBodyInnerHTML(tbody, html) { //fix MS Internet Exploder’s lameness
var temp = MSIEsetTBodyInnerHTML.temp;
temp.innerHTML = '<table><tbody>' + html + '</tbody></table>';
tbody.parentNode.replaceChild(temp.firstChild.firstChild, tbody); }
MSIEsetTBodyInnerHTML.temp = document.createElement('div');
if (navigator && navigator.userAgent.match( /MSIE/i ))
MSIEsetTBodyInnerHTML(tbody, html);
else //by specs, you can not use “innerHTML” until after the page is fully loaded
tbody.innerHTML=html;
Even with this code, though, MSIE does not seem to properly re-size the table cells in my application, but I'm filling an empty tbody tag with variable generated content, while the thead cells' colspan values are set to a fixed value: the maximum number of cells that may be in the generated tbody. While the table tbody is 50 cells wide, only two columns show. Perhaps if the table was originally filled, and the cells were replaced with the same internal structure, this method would work. Google's Chrome does an excellent job of rebuilding the table, while Opera's desktop browser can resize to more columns just fine, but if you remove columns, the remaining column widths remain as narrow as they were; however with Opera, by hiding the table (display=none) then re-showing it (display=table), the generated table tbody cells then size properly. I've given up with Firefox. It's the MSIE-6 of 2012 - a nightmare to develop for which must have additional markup added just to make HTML-CSS layouts work because it does not conform to standards that even MSIE now does. So I haven't tested the tbody.innerHTML workings in Firefox.
This can be fixed by creating a shim/polyfill for .innerHTML. This could get you (you, dear reader) started:
if (/(msie|trident)/i.test(navigator.userAgent)) {
var innerhtml_get = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerHTML").get
var innerhtml_set = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerHTML").set
Object.defineProperty(HTMLElement.prototype, "innerHTML", {
get: function () {return innerhtml_get.call (this)},
set: function(new_html) {
var childNodes = this.childNodes
for (var curlen = childNodes.length, i = curlen; i > 0; i--) {
this.removeChild (childNodes[0])
}
innerhtml_set.call (this, new_html)
}
})
}
var mydiv = document.createElement ('div')
mydiv.innerHTML = "test"
document.body.appendChild (mydiv)
document.body.innerHTML = ""
console.log (mydiv.innerHTML)
http://jsfiddle.net/DLLbc/9/