Towards Internet Explorer 11 Compatibility

Updated on 8 August 2016, see here.

I currently work for Jisuanke and have an internal project of developing an in-browser app that works inside one of our internal site, which will greatly improve our efficiency on some tasks that are performed repeatedly. In other words, it’s an automation utility.

The idea was proposed by several people in my group as the current system (or shall I say interface) for doing that task is not suitable for batch operation. Moreover, the front-end and the back-end themselves are buggy. I reported a whole lot of bugs during this project (the project is still in its life-cycle when I’m writing this post therefore there are more to come) and luckily, most are fixed. When I first proposed the idea (certainly I had a hard time using the system), my mentor told me that another intern is developing an extension for Chrome. Well, that doesn’t stop me from doing a better job. I aimed to develop a cross-browser app that can be loaded via a bookmarklet, and I did.

Our website employs HTTPS and Content-Security-Policy, therefore I must host the code in the allowed domain list, which is okay for me (even with a self-signed SSL certificate!). But the majority of my colleagues use Chrome on Mac, therefore IIS won’t work for them. Therefore I developed the app with Chrome by adding a shim (extension manifest). The code should still work for Microsoft Edge, Internet Explorer and Safari. Except it doesn’t work on Internet Explorer.

There are some undesired (undesired by me) behaviour that Internet Explorer is exhibiting. Mostly the DOM APIs. In addition to the Jisuanke project, I was also working on some COM interop with HTMLFILE class. On my machine its main interoperability assembly is mshtml.dll and it is implemented as mshtml.HTMLDocumentClass, which is the document object of InternetExplorer.Application object.

The followings are some of those behaviours.

X-UA-Compatible is mandatory

I was developing a script module that enables me to build my blog, I needed to use HTMLFILE object to help me operate DOM thingies and take the documentElement.outerHTML as the output. However, things aren’t as easy as it seems to be. The following PowerShell script produces upper-case tag names:

$html = New-Object -ComObject HTMLFILE;
$html.IHTMLDocument2_write('<html><head></head><body></body></html>');
$html.documentElement.outerHTML;

It produces:

<HTML><HEAD></HEAD>
<BODY></BODY></HTML>

But if you write the HTML of the homepage of Microsoft, you will find the tag names are case-kept. Why is that? Further trial-and-error shows that HTMLFILE object decides its documentMode from the head element. Therefore, the following will cure:

$html = New-Object -ComObject HTMLFILE;
$html.IHTMLDocument2_write('<html><head><meta http-equiv="X-UA-Compatible" content="IE=Edge"></head><body></body></html>');
$html.documentElement.outerHTML;

Actually, IE 9 suffices, but it doesn’t hurt to be latest, except it does.

zoom vs -ms-zoom

This problem also arises from the blog-building tool.

On Microsoft Edge, when you set someElement.style.zoom = "100%";, the element reads <elem style="zoom: 100%;">….

On Internet Explorer, when you do the same thing, you get <elem style="-ms-zoom: 100%">….

To reproduce it, execute these commands in PowerShell:

$html = New-Object -ComObject HTMLFILE;
$html.IHTMLDocument2_write('<html><head><meta http-equiv="X-UA-Compatible" content="IE=Edge"></head><body><div id="elem"></div></body></html>');
$html.getElementById('elem').style.zoom = '100%';
$html.getElementById('elem').outerHTML;

Now I shall explain the last sentence in the prior section. If you change IE=Edge to IE=9, the annoying renaming goes away. But I certainly don’t want to use IE 9 for this reason. My solution is to set the style attribute as style_<some newly generated GUID> attribute, and later replace that string to style.

Tampering with innerHTML can be a mess

Consider the following code fragment (one arising from the Jisuanke project):

var elem = document.createElement('div');
elem.innerHTML = '<p>1</p>\n<p>2\n</p>';
var elemChildNodes = Array.prototype.map.call(elem.childNodes, function (x) { return x; });
elem.innerHTML = '';
elemChildNodes.forEach(function (x) { console.log(x); });
/* elemChildNodes[1] is a text node. */
elemChildNodes[1].data = 'my data';

On Microsoft Edge and Chrome, the elements already taken into elemChildNodes array are not affected by clearing elem.innerHTML.

On Internet Explorer, however, the elemChildNodes becomes empty after clearing elem.innerHTML. To make it worse, the last line raises an exception.

To solve this, use the following code instead:

/* elem.innerHTML = ''; */
elemChildNodes.forEach(function (x) { elem.removeChild(x); });

Setting some DOM properties via COM interop fails

This arises from my blog-building tool. Open about:blank with Internet Explorer, and run the following code in console:

Question why about:blank?

Answer Because you work on this page if you use HTMLFILE object.

document.createElement('a').href = '/path';

The code succeeds. But if you run the following script in PowerShell: (Remember we need to use IE 11!)

$html = New-Object -ComObject HTMLFILE;
$html.IHTMLDocument2_write('<html><head><meta http-equiv="X-UA-Compatible" content="IE=Edge"></head><body></body></html>');
$html.createElement('a').href = '/path';

It fails with HRESULT 0x800A01B6. Sadly, this time we have to return to IE 8 or lower to make the last line successful. However, we can replace the last line with:

$html.createElement('a').setAttribute('href', '/path');

And the complain stops. Moreover, if you inspect href of that element, it reads about:/path, but the outerHTML is still good.

You cannot have a recursive iframe

This arises from the Jisuanke project. If you put an iframe that loads the current page (exactly), you will see it not loading on Internet Explorer. This is called a recursive iframe. To demonstrate the behaviour, I have put an iframe here:

Click here to set the iframe to the current page. The iframe will not load on Internet Explorer. Here is a possible workaround if passing garbage query string is okay.

Updated on 8 August 2016

The blog builder is now based on marked and has no COM Internet Explorer Object dependency. It works by replacing text.

Please enable JavaScript to view the comments powered by Disqus.