This blog post lists the browser compatibility issues (with solutions) I've encountered when creating a simple HTML(4) + JavaScript + CSS web page containing presentation slides with a little bit of user interaction.
My goal was to make the web page compatible with Google Chrome 8.0 or later, Firefox 3.6 or later, Safari in Leopard or later, Opera 10.63 or later, Konqueror 4.4.2 or later, Internet Explorer 8.0 or later. (I'll call them main browsers from now on.) Please note that anything stated in this blog post may not be true for earlier web browser versions. There was no plan to make the web page work in any earlier web browser version, but it turned out that it was possible and easy to make the page work with Internet Explorer 7.0 with some small rendering quality degradation, so I happened to add support for that as well.
My workflow:
- Edit the web page directly in a text editor, as a single HTML file (containing HTML, JavaScript and CSS). Split it to different files only after the prototype is found working and all browser compatibility issues have been registered (and most of them resolved).
- Make sure the browser renders the page in standards compliant mode, not in quirks mode (see below how and why).
- View the local HTML file in Google Chrome, reload the page after each file save. Try it after major modifications in Firefox. As soon as the web page works in these two browsers, fix it for other browsers as well.
- When porting to other browsers, especially Internet Explorer, search in the jQuery source code for the feature (class or method name) that works in one browser. Most probably they jQuery source will contain its alternative implementations for other browsers.
- Use the Developer Tools feature of Google Chrome (activate it with Ctrl-Shift-J) to debug the DOM, the CSS and the JavaScript on the current page. With Firefox, use the Firebug extension and/or the shell bookmarklet for debugging. Should you need it, use the Tools / Developer Tools (activate it with F12; it is available as an extension called Developer Toolbar for IE7 and earlier) in Internet Explorer 8 for debugging.
- With Internet Explorer Developer Tools you can verify that the web page is indeed rendered in standards compliant mode, and you can also force a rendering which Internet Explorer 7 would do.
- After each major change, validate the web page that it is valid HTML 4.01 transitional. You can use the HTML Validator Google Chrome extension.
My general knowledge and advice on cross-browser compatibility:
- All main browsers (Google Chrome, Firefox, Safari, Opera and Konqueror) are very similar to each other except for Internet Explorer.
- Google Chrome, Safari and Konqueror are even more similar to each other, because they share the same original code base (KHTML in Konqueror, which WebKit in Safari is based on, which WebKit in GoogleChrome is based on).
- Rendering (application of CSS) in most main browsers in much more similar to each other when they render the web page in standards compliant mode rather than in quirks mode. So to get rid of many incompatibilities, just make sure that the browser renders the page in standards compliant mode.
- To enable standards compliant mode, just start your HTML with a doctype (right at the beginning of the file). To get HTML 4.01 transitional, prepend this to your HTML file:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- Make sure your editor saves the HTML file in UTF-8 encoding (character set), without the byte order mark (BOM).
- Make sure the character set is indicatied the
<head>
. This is needed and used when the web page is loaded using afile:///
URL or imported to a word processor such as OpenOffice Writer or LibreOffice Writer. Example:<meta http-equiv="content-type" content="text/html; charset=utf-8">
- Make sure your webserver returns the proper character set for the web page in the HTTP response headers (e.g.
Content-Type: text/html; charset=utf-8
). This setting is used forhttp://
andhttps://
URLs. The HTTP response header takes precedence over<meta http-equiv=
. If you use the Apache webserver, you may be able to force the UTF-8 charset by creating a file named.htaccess
(filename starting with a dot) next to the HTML file, containingAddDefaultCharset UTF-8
. If it doesn't seem to take effect, then put an error (temporarily) to the.htaccess
file (e.g.nosuchdirective 42
), do a Shift-reload on your web page. If that causes an Internal Server Error, then.htaccess
is indeed honored by Apache. If you don't get such an error, talk to your webmaster or system administrator (i.e. copy-paste this paragraph to an e-mail in addition to the URL). - The corresponding SVN command (for Google Code) for the character set is:
svn propset 'svn:mime-type' 'text/html; charset=utf-8' webpage.html
- Use jQuery, and make it take care of most browser compatibility issues. This document assumes, however, that no JavaScript framework is used, so all issues have to be resolved manually.
Some specific cross-browser compatibility issues I have encountered:
- See a cross-browser comparison of all JavaScript browser events here.
- Internet Explorer has a different API for installing and handling events.
- Event propagation and bubbling semantics are different. You can avoid these issues by installing all event handlers for
document.body
andwindow
. (Sorry, I don't know more about this.) - For Internet Explorer, change
obj.addEventListener(eventName, handler, false)
toobj.attachEvent('on' + eventName, handler)
. - For Internet Explorer, change
obj.removeEventListener(eventName, handler, false)
toobj.detachEvent('on' + eventName, handler)
. - Internet Explorer supports the
DOMContentLoaded
event under a different name (onreadystatechange
) and different semantics, see below. - Internet Explorer needs
event.preventDefault()
to be specified differently. Here is the cross-browser solution:event.preventDefault ? event.preventDefault() : (event.returnValue = false)
- Internet Explorer needs
event.stopPropagation()
to be specified differently. Here is the cross-browser solution:event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true)
- Internet Explorer doesn't pass the
event
object to the event handler function. To make it cross browser, write it like this:function handler(event) { if (!event) event = window.event; ... }
- Event propagation and bubbling semantics are different. You can avoid these issues by installing all event handlers for
- Internet Explorer doesn't have
window.getComputedStyle
, so the current (computed) CSS properties have to be queried differently. An example, specific cross-browser solution:function getBackgroundColor(element) { return element.currentStyle ? element.currentStyle.backgroundColor : window.getComputedStyle(element, null). getPropertyValue('background-color') }
- Internet Explorer 8 doesn't have
elementOrDocument.getElementsByClassName
, so it has to be emulated using a loop and e.g.elementOrDocument.getElementsByTagName
and some manual checking. The emulation is a bit slower. - The
string.substr
method in Internet Explorer doesn't accept negative values in its first argument, so e.g.myString.substr(-2)
has to be replaced bymyString.substr(myString.length - 2)
. - The
DOMContentLoaded
event is fired as soon as the main HTML and the JavaScript and CSS files it references have finished loading — but before images and other objects on the page have finished. By that time, the rendering is done and measurements are made, so e.g.document.body.offsetWidth
is valid. In contrast, theonload
even fires as soon as images etc. are also finished loading Here is how to handleDOMContentLoaded
in a cross-browser way:function onDomReady() { ... } if (navigator.userAgent.indexOf(' KHTML/') >= 0) { document.addEventListener('load', onDomReady, false) } else if (document.addEventListener) { document.addEventListener('DOMContentLoaded', onDomReady, false) } else if (document.attachEvent) { document.attachEvent('onreadystatechange', function() { if (document.readyState == 'complete') { document.detachEvent('onreadystatechange', onReadyStateChange) onDomReady() } }) }
Here, theload
event is used instead ofDOMContentLoaded
, because Konqueror 4.4.2 doesn't compute element dimensions (e.g.document.body.offsetWidth
and CSS properties) atDOMContentLoaded
time; but it returns bogus default values. For Internet Explorer,onreadystatechange
has to be used instead. - There are two keyboard events when a key is pressed or auto-repeated:
keydown
andkeypress
.- Use
keypress
for Google Chrome and Safari (i.e.navigator.userAgent.indexOf(' AppleWebKit/') >= 0
), because they don't send the arrow key (and any other non-letter) events forkeypress
. - Use
keydown
for Firefox (for 3.6), because it doesn't send auto-repeats forkeydown
(see more here). - Use
onkeydown
on Internet Explorer. - Some browsers have a meaningless
event.keyCode
(such as 0 for Firefox 3.6 or the same as theevent.charCode
for Konqueror 4.4.2) for some keys. In this case, use theevent.charCode
instead. - The
event.charCode
depends on whether Shift is down (e.g. 65 for A and 97 for Shift-A), but it doesn't depend on whether Ctrl or other modifiers are down.event.charCode
is usually a Unicode code point (character code). - See
event.keyCode
constants here. - See more here about keyboard event compatibility.
- Use
- Many browsers (such as Firefox 3.6) send multiple
resize
events (as part of an animation) when the window is maximized. - To get the vertical scroll position, use
document.body.scrollTop || document.documentElement.scrollTop
, because Konqueror always returns 0 for justdocument.body.scrollTop
. Do this respectively for the horizontal scroll position (scrollLeft
). - To scroll the page in a cross-browser way (which works in Konqueror as well), use
window.scroll(newScrollLeft, newScrollTop)
. - Internet Explorer doesn't support
window.innerWidth
, usewindow.innerWidth || document.documentElement.clientWidth
for cross-browser compatibility. Do the same withwindow.innerHeight
anddocument.documentElement.clientHeight
as well. - Internet Explorer 7 doesn't support an extra comma right before
]
and}
in array and object constructors.
One more random compatibility issue. You can load style sheets (with a link element) that don't take effect by default, but can later be enabled by javascript. However, browsers recognize such stylesheets in two different ways: Firefox looks at whether the rel attribute of the link element says "stylesheet" or "alternate stylesheet", whereas Konqueror checks whether the stylesheet has an "id" attribute.
ReplyDeleteOne more important thing: make sure to set the X-UA-Compatible meta tag for Explorer 8 and above. If you don't do so, it will choose one of "standards" (the best it can do) or "compatibility" (emulate IE7 more or less) based on the _user's_ settings and other criteria. So the page might display correcty for some users and incorrectly for others. Testing for both scenarios becomes a nightmare too. The solution is to explicitly tell IE which rendering mode to use, via the meta X-UA-Compatible tag.
ReplyDelete