Internet Explorer's behavior sometimes really, really makes me angry.

We recently rolled out a copy of our javascript libraries after much testing and, a few hours later, discovered a page on our site that IE was barfing on. Specifically, the page would load about half way and then announce that it could not load the page (despite the fact that it's clearly loaded behind the error message) and then present you with its generic "page not found" content.

I've seen this behavior before and generally know what to look for, but it's a huge pain because the page is gone and your only method for debugging it is to slowly remove code, line by line, until it stops doing it. Then you put things back bit by bit until you narrow it down to the offending line. It's painstaking work and the constant error popping up begins to really grate.

So why does IE do this?

Perhaps I should use "when" instead of "why" in that header, because I don't really know why the developers of IE would do this.

Update: Bit thanks to Jon in the comments for turning me on to this MSFT support page about this topic. I've updated this article here to more accurately describe the problem. More detail can be found in the MSFT article.

IE does this when you attempt to modify a DOM element before it is closed. This means that if you try and append a child element to another and that other element (like the document.body) is still loading, you'll get this error. This will occur if you use .appendChild (which in Mootools includes .adopt, .injectInside, .injectBefore, etc.) or if you use Element.innerHTML = "" (or in Mootools, the .setHTML method).

The general rule of DOM manipulation is that you can't mess with it until it's loaded, which is why we use the domready event (this is not a native event, but most frameworks have some method for creating this event). In Mootools, you do it like this:

JavaScript:
window.addEvent('domready', function(){
  //DOM code...
});

drag to resize


The exception to this rule is that you can modify the DOM that's loaded so far if you do it inline. In other words, you can't modify the DOM before it's loaded, but you can modify the portion of the DOM that's loaded at any given point. For example, you could have this and it would work:

HTML:
<div id="example"></div>
<script>
//note: this is Mootools javascript
$('example').innerHTML="hello world";
$('example').adopt(new Element('img').setProperty('src','....some url...');
</script>

drag to resize


Because you are executing this code after the DOM element is loaded, it's ok.

...except when it's not.

What I found today was that a Class I'd written long ago (IframeShim) created a conflict of sorts with this general rule (the rule that you can alter DOM elements that have already loaded, even if the page hasn't finished).

IframeShim puts a hiden iframe under an element to hide things below it (like select lists; see the link above for more detail). Previously, I inserted the iframe into the DOM right after the element that it was shimming:

JavaScript:
var iframe = new Element('iframe');
//...some more stuff to format it
iframe.injectAfter(elementToShim);

drag to resize


I recently changed this because if the parent element of the one you were shimming had CSS properties that cascaded to its children, the iframe, which is positioned absolutely, would inherit margins and padding and it would throw off the positioning. Instead, I needed to inject the iframe into the document body:

JavaScript:
var iframe = new Element('iframe');
//...some more stuff to format it
iframe.injectInside(document.body);

drag to resize


This code on its own worked fine (and passed our QA process). Here's where it gets bizarre (perhaps not, as this sort of thing is almost normal in IE).

The document that was breaking (the "Operation aborted" message) had in it a line like the first example above:

HTML:
<div id="example"></div>
<script>
//note: this is Mootools javascript
$('example').innerHTML="hello world";
</script>

drag to resize


This is an over simplified example, but when I removed all the other code it was the innerHTML = "something" that was the culprit.

If I removed that line, the page worked. The thing is, the only thing that changed was our library (which has a LOT of stuff in it). So how did that change suddenly make this innerHTML = "something" become so destructive?

So then I started removing chunks of the library until I found that it was the IframeShim class, which I had recently changed. This led me to the change I mention above, where I append the iframe to the document.body.

It would seem that IE is ok with adding something to the document body at any time, but if you use innerHTML = "string..." after you do it, you'll somehow retroactively-prematurely close the DOM or something. That is messed up.

Anyway, it took me a day to figure all this out and make it so that IframeShim won't append the element to the DOM until it's ready (in IE) and fix the bug.

After reading the MSFT support article, it's apparent that this behavior isn't quite explained by it. According to the article, you'll get this error when you try and inject content into a dom element that isn't closed. Their example:

This problem occurs because a child container HTML element contains script code that tries to modify the parent container element of the child container. The script code tries to modify the parent container element by using either the innerHTML method or the appendChild method.

HTML:
<html>
  <body>
      <div>
                  <script type="text/Javascript">
                    document.body.innerHTML+="sample text";
                  </script>
      </div>
  </body>
</html>

drag to resize


According to MSFT, this error will occur because you are trying to modify the before the is closed. Using the onDomReady event would solve this problem here. But in my problem that I worked out above, I append something to the before it's loaded, but IE doesn't complain. It only complains when I have that AND a innerHTML="something" line on the same page. The line that uses the innerHTML="something" isn't referencing a tag that is still open, though the IframeShim is. It's like trying to diagnose why opening the gas cap on your car makes the hub caps fall off. They aren't connected.

This breaks

HTML:
<html>
  <body>
      <div>
            <div id="example"></div>
                  <script type="text/Javascript">
                        document.body.appendChild(document.createElement('div'));
                        document.getElementById('example').innerHTML+="sample text";
                  </script>
      </div>
  </body>
</html>

drag to resize


This doesn't break

HTML:
<html>
  <body>
      <div>
            <div id="example"></div>
                  <script type="text/Javascript">
                        //document.body.appendChild(document.createElement('div'));
                        document.getElementById('example').innerHTML+="sample text";
                  </script>
      </div>
  </body>
</html>

drag to resize


This doesn't break

HTML:
<html>
  <body>
      <div>
            <div id="example"></div>
                  <script type="text/Javascript">
                        document.body.appendChild(document.createElement('div'));
                        //document.getElementById('example').innerHTML+="sample text";
                  </script>
      </div>
  </body>
</html>

drag to resize


Ungh.

Microsoft, please make better browsers (note that IE7 has this problem, too).