A guide to building webapps with IE8 support
If you build web applications for healthcare, you’re fooling yourself if you don’t support legacy Internet Explorer browsers. This should make it a bit easier.
Internet Explorer 8’s market share dropped below 10% in May, and continues to drop, but US healthcare facilities have maintained a tight grip on Windows XP and its 4-year-old web browser despite poor performance and potential security risks.
At ACT.md, we’ve catalogued all the fixes we’ve applied to our Backbone.js web application to support IE8 with the hope that we can ease the suffering of other front-end developers.
Detect IE in JavaScript
We needed to enable jQuery CORS support for IE users (see next item), so we had to detect IE first. Older solutions didn’t account for IE10, which foolishly doesn’t consider itself a version of IE, so we extended it.
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
if (Function('/*@cc_on return document.documentMode===10@*/')()){
v = 10;
}
return v > 4 ? v : undef;
}());
Disable AJAX caching
With the above, we can conditionally disable caching of AJAX requests, which, oddly, is IE’s default.
if (ie) {
$.ajaxSetup({ cache: false });
}
- stackoverflow: How to prevent jquery ajax caching in Internet Explorer?
Enable CORS support
When we started building our webapp, we deployed our Python-based API using Flask’s built in Werkzeug server on a different port from our front-end’s Apache web server, so we needed to use cross origin request sharing (CORS). We developed happily in Chrome by setting the jQuery crossDomain
AJAX option to true, but IE would throw the cryptic error, “No Transport,” which is its peculiar way of rejecting cross-site AJAX requests.
We ultimately refactored our development environment to use Apache and mod_wsgi
so both front and back ends could be served from the same webserver, but before that we got by with this IE CORS workaround:
if (ie) {
jQuery.support.cors = true;
}
…which shouldn’t work. Probably better to use a plugin like jQuery-ajaxTransport-XDomainRequest if you have to.
- stackoverflow: jQuery Call to WebService returns “No Transport” error
Simulate PATCH in IE8
We use the PATCH method to save partially-modified Backbone models, but IE8 was released a year before PATCH was formally proposed by the IETF. (Of course, PATCH has been around at least since HTTP 1.1.)
On the front-end we use Backbone’s emulateHTTP
option, which sends a POST
with an X-HTTP-Method-Override
header.
In our Python/Flask back-end, we needed to accept this POST
in place of a PATCH
with an HTTP middleware.
- github: HTTP PATCH method not supported in IE
- Flask Documentation: Adding HTTP Method Overrides
Don’t use unquoted reserved words as keys
Chrome and Firefox don’t seem to mind an object like { return: 1, continue: 2 }
but IE cries bloody murder. Make sure you quote your keys, e.g. { "return": 1, "continue": 2 }
Use height
instead of line-height
on input
elements
Consistently formatting input
elements between browsers has been a challenge, especially using Bootstrap 2 with the FF DIN Web typeface. Moving away from setting line-height
on our input
elements has made it easier.
- Joshua Hibbert: Line-Height Doesn’t Work As Expected On Inputs
Don’t use HTML5 placeholder
attribute
Yes, there are shims that make this work, but it was tedious and messy to use one across a Backbone-generated UI. We redesigned our forms such that placeholder
s only provide additional instructions, but aren’t the only way for a user to identify a form field.
- Cory Matthews: HTML5 Placeholder in IE and Unsupported Browsers
Use conditional comments for IE-specific behavior
Sometimes you need to only override IE styles. Rather than use conditional stylesheets, or hacks within your stylesheets, you can use conditional comments to apply IE-specific classes to the html
element. We used this to fine-tune our input
and select
styling.
- stackoverflow: css property recognized only in Internet Explorer
Detect HTML5 pushState
and redirect the user on initial access
We use clean URLs with Backbone.js, so that we don’t need nasty hash marks. This requires HTML5’s pushState functionality, and some mod_rewrite
magic in Apache. Older browsers, though, have to fall back on the hash-marked URL. To avoid some seriously mangled URLs, we detect the lack of pushState
support, and gracefully redirect the user to a well-formed hash-marked URL.
var hasPushState = Modernizr.history;
// We need to redirect you properly before getting started.
if (!hasPushState && window.location.pathname != "/") {
window.location.replace("/#" + window.location.pathname.substr(1));
} else {
var application = new ActApplication();
Backbone.history.start({ pushState: hasPushState });
}
Route internal links through Backbone.history.navigate
Internal links should be handled by Backbone’s router, rather than your web server, so I started with Tim Branyen’s gist and then tweaked it for IE support.
// Use absolute URLs to navigate to anything not in your Router.
// Use delegation to avoid initial DOM selection and allow all matching elements to bubble
$(document).delegate("a", "click", function(evt) {
// Get the anchor href and protcol
var href = $(this).attr("href"),
target = $(this).attr("target"),
protocol = this.protocol;
// 1. Ensure the protocol is not part of URL, meaning it's relative.
// 2. Leave alone anchor URLs, since those are 'for real' now
// 3. If target attribute is specified, let the browser handle it
if (href.substring(0, protocol.length) !== protocol && href.substring(0, 1) !== '#' && target == null) {
if (!evt.isDefaultPrevented()) {
// Stop the event bubbling to ensure the link will not cause a page refresh
evt.preventDefault();
// Scroll user to the top of the page, as she would expect
window.scrollTo(0, 0);
// Send the URL to the router
Backbone.history.navigate(href, true);
}
}
});
- jabbett: backbone_pushstate_router.js
Use modern.ie VMs to test on old versions
Finally, we use the IE8, IE9, and IE10 VirtualBox VMs from modern.ie to hand-test our app, along with automated cross-browser testing using Behave, Selenium Web Driver, and Sauce Labs.
Sometimes we need Fiddler to debug network issues within a VM, since the IE developer console is terrible.