HTML5 Zone is brought to you in partnership with:

Mitch Pronschinske is a Senior Content Analyst at DZone. That means he writes and searches for the finest developer content in the land so that you don't have to. He often eats peanut butter and bananas, likes to make his own ringtones, enjoys card and board games, and is married to an underwear model. Mitch is a DZone Zone Leader and has posted 2573 posts at DZone. You can read more from them at their website. View Full User Profile

Why Your JavaScript Developed in Firefox Might Fail in IE

09.19.2011
| 8847 views |
  • submit to reddit
It's so nice to find concise guides on frustrating web development issues that are commonplace, but not well documented or easy to find.  Today I found an answer on SO about developing JavaScript across Firefox and IE that could be so useful to lots of people if they can find it.  I'm sticking it in the Web Builder Zone now so it will hopefully be findable by more web developers.  The answer became a community wiki edited by David Morrissey, Marcel Korpel, Crozin, and Mathias Bynens. The rest of this post is all their fine work:

Note: IE9 fixes many of the following issues, so a lot of this only applies to IE8 and below and to a certain extent IE9 in quirks mode. For example, IE9 supports SVG, <canvas>, <audio> and <video> natively, however you must enable standards compliance mode for them to be available.

General:

  • Problems with partially loaded documents: It’s a good idea to add your JavaScript in a window.onload or similar event as IE doesn’t support many operations in partially loaded documents.

  • Differing attributes: In CSS, it's elm.style.styleFloat in IE vs elm.style.cssFloat in Firefox. In <label> tags the for attribute is accessed with elm.htmlFor in IE vs elm.for in Firefox. Note that for is reserved in IE so elm['for'] is probably a better idea to stop IE from raising an exception.


Base JavaScript language:

  • Access characters in strings: 'string'[0] isn’t supported in IE as it’s not in the original JavaScript specifications. Use 'string'.charAt(0) or 'string'.split('')[0] noting that arrays are significantly faster than strings in IE.

  • Commas before the end of objects: e.g. {'foo': 'bar',} aren't allowed in IE.


Element-specific issues:

  • Getting the document of an IFrame:

    • Firefox: IFrame.contentWindow.document
    • IE: IFrame.contentDocument
    • (IFrame.contentWindow refers to the window in both browsers.)

       

  • Canvas: Versions of IE before IE9 don't support the <canvas> element. IE does support VML which is a similar technology however, and explorercanvas can provide an in-place wrapper for <canvas> elements for many operations. Be aware that IE8 in standards compliance mode is 10x slower and has many more glitches than when in quirks mode when using VML though.

  • SVG: IE9 supports SVG natively. IE6-8 can support SVG, but only with external plugins with only some of those plugins supporting JavaScript manipulation.

  • <audio> and <video>: are only supported in IE9.

  • Dynamically creating radio buttons: IE <8 has a bug which makes radio buttons created with document.createElement uncheckable. See also How do you dynamically create a radio button in Javascript that works in all browsers? for a way to get around this.

  • Embedded JavaScript in <a href> tags and onbeforeunload conflicts in IE: If there's embedded JavaScript in the href part of an a tag (e.g. <a href="javascript: doStuff()"> then IE will always show the message returned from onbeforeunload unless the onbeforeunload handler is removed beforehand. See also Javascript function, on web page close.

  • <script> tag event differences: onsuccess and onerror aren't supported in IE and are replaced by an IE-specific onreadystatechange which is fired regardless of whether the download succeeded or failed. See also JavaScript Madness for more info.


Element size/position/scrolling and mouse position:

  • Getting element size/position: width/height of elements is sometimes elm.style.pixelHeight/Width in IE rather than elm.offsetHeight/Width, but neither is reliable in IE, especially in quirks mode, and sometimes one gives a better result than the other.

    elm.offsetTop and elm.offsetLeft are often incorrectly reported, leading to finding positions of elements being incorrect, which is why popup elements etc are a few pixels off in a lot of cases.

    Also note that if an element (or a parent of the element) has a display of none then IE will raise an exception when accessing size/position attributes rather than returning 0 as Firefox does.

  • Get the screen size (Getting the viewable area of the screen):

    • Firefox: window.innerWidth/innerHeight
    • IE standards mode: document.documentElement.clientWidth/clientHeight
    • IE quirks mode: document.body.clientWidth/clientHeight
  • Document scroll position/mouse position: This one is actually not defined by the w3c so is non-standard even in Firefox. To find the scrollLeft/scrollTop of the document:

    • Firefox and IE in quirks mode: document.body.scrollLeft/scrollTop
    • IE in standards mode: document.documentElement.scrollLeft/scrollTop
    • NOTE: Some other browsers use pageXOffset/pageYOffset as well.

      function getDocScrollPos() {
       var x = document.body.scrollLeft ||
               document.documentElement.scrollLeft ||
               window.pageXOffset || 0,
           y = document.body.scrollTop ||
               document.documentElement.scrollTop ||
               window.pageYOffset || 0;
       return [x, y];
      };<span class="kwd"></span><span class="pun"></span><span class="pln">
      </span>

    In order to get the position of the mouse cursor, evt.clientX and evt.clientY in mousemove events will give the position relative to the document without adding the scroll position so the previous function will need to be incorporated:

    var mousepos = [0, 0];
    document.onmousemove = function(evt) {
     evt = evt || window.event;
     if (typeof evt.pageX != 'undefined') {
      // Firefox support
      mousepos = [evt.pageX, evt.pageY];
     } else {
      // IE support
      var scrollpos = getDocScrollPos();
      mousepos = [evt.clientX+scrollpos[0], evt.clientY+scrollpos[1]];
     };
    };<span class="kwd"></span><span class="pun"></span><span class="pln">
    </span>

Selections/ranges:

  • <textarea> and <input> selections: selectionStart and selectionEnd are not implemented in IE, and there's a proprietary "ranges" system in its place, see also How to get cursor position in textarea?.

  • Getting the currently selected text in the document:

    • Firefox: window.getSelection().toString()
    • IE: document.selection.createRange().text

Getting elements by ID:

  • document.getElementById can also refer to the name attribute in forms (depending which is defined first in the document) so don't have different elements which have the same name and id! This dates back to the days when id wasn't a w3c standard. document.all is significantly faster than document.getElementById, but it's even more nasty as it always prioritizes name before id. I personally use this fast "hybrid" code, falling back with additional checks just to be sure:

    function getById(id) {
     var e;
     if (document.all) {
      e = document.all[id];
      if (e && e.tagName && e.id === id) {
       return e;
      };
     };
     e = document.getElementById(id);
     if (e && e.id === id) {
      return e;
     } else if (!e) {
      return null;
     } else {
      throw 'Element found by `name` instead of `id`: ' + id;
     };
    };<span class="kwd"></span><span class="pun"></span><span class="pln">
    </span>

Problems with read only innerHTML:

  • IE doesn't support setting the innerHTML of table, tbody, tfoot, thead, and tr elements. Here's a function which works around that:

    function setHTML(elm, html) {
     // Try innerHTML first
     try {
      elm.innerHTML = html;
     } catch (exc) {
      // IE 6-8 don't support setting innerHTML for
      // TABLE, TBODY, TFOOT, THEAD, and TR directly
      var tn = elm.tagName.toLowerCase();
      if (tn === 'table') {
       replace(getElm('<table>' + html + '</table>'));
      } else if (tn in {tbody:0, tfoot:0, thead:0}) {
       replace(getElm('<table><tbody>' + html + '</tbody></table>').firstChild);
      } else if (tn === 'tr') {
       replace(getElm('<table><tbody><tr>' + html + '</tr></tbody></table>').firstChild.firstChild);
      } else {
       throw exc;
      };
      function getElm(html) {
       // Create a new element and return the first child
       var e = document.createElement('div');
       e.innerHTML = html;
       return e.firstChild;
      };
      function replace(elms) {
       // Remove the old elements from `elm`
       var x = elm.children.length, y = 0, c;
       while (c = elm.children[--x]) {
        elm.removeChild(c);
       };
       // Add the new elements from `elms` to `elm`
       while (c = elms.children[y++]) {
        elm.appendChild(c);
       };
      };
     };
    };

     

    Also note that IE requires adding a <tbody> to a <table> before appending <tr>s to that <tbody> element when creating using document.createElement, for example:

    var table = document.createElement('table');
    var tbody = document.createElement('tbody');
    var tr = document.createElement('tr');
    var td = document.createElement('td');
    table.appendChild(tbody);
    tbody.appendChild(tr);
    tr.appendChild(td);
    // and so on<span class="kwd"></span><span class="com"></span><span class="pln">
    </span>

Event differences:

  • Getting the event variable: DOM events aren't passed to functions in IE and are accessible as window.event. One common way of getting the event is to use e.g.
    elm.onmouseover = function(evt) {evt = evt||window.event}
    which defaults to window.event if evt is undefined.

  • Key event code differences: Key event codes vary wildly, though if you look at Quirksmode or JavaScript Madness, it's hardly specific to IE, Safari and Opera are different again.

  • Mouse event differences: the button attribute in IE is a bit-flag which allows multiple mouse buttons at once:

    • Left: 1 (var isLeft = evt.button & 1)
    • Right: 2 (var isRight = evt.button & 2)
    • Center: 4 (var isCenter = evt.button & 4)

      The W3C model (supported by Firefox) is less flexible than the IE model is, with only a single button allowed at once with left as 0, right as 2 and center as 1. Note that, as Peter-Paul Koch mentions, this is very counter-intuitive, as 0 usually means 'no button'.

      offsetX and offsetY are problematic and it's probably best to avoid them in IE. A more reliable way to get the offsetX and offsetY in IE would be to get the position of the relatively positioned element and subtract it from clientX and clientY.

      Also note that in IE to get a double click in a click event you'd need to register both a click and dblclick event to a function. Firefox fires click as well as dblclick when double clicking, so IE-specific detection is needed to have the same behaviour.

  • Differences in the event handling model: Both the proprietary IE model and the Firefox model support handling of events from the bottom up, e.g. if there are events in both elements of <div><span></span></div> then events will trigger in the span then the div rather than the order which they're bound if a traditional e.g. elm.onclick = function(evt) {} was used.

    "Capture" events are generally only supported in Firefox etc, which will trigger the div then the span events in a top down order. IE has elm.setCapture() and elm.releaseCapture() for redirecting mouse events from the document to the element (elm in this case) before processing other events, but they are slow and should generally be avoided.

    • Firefox:

      Attach: elm.addEventListener(type, listener, useCapture [true/false])
      Detach: elm.removeEventListener(type, listener, useCapture)
      (type is e.g. 'mouseover' without the on)

    • IE: Only a single event of a given type on an element can be added in IE - an exception is raised if more than one event of the same type is added. Also note that the this refers to window rather than the bound element in event functions (so is fairly useless):

      Attach: elm.attachEvent(sEvent, fpNotify)
      Detach: elm.detachEvent(sEvent, fpNotify)
      (sEvent is e.g. 'onmouseover')

  • Event attribute differences:

    • Stop events from being processed by any other listening functions:

      Firefox: evt.stopPropagation()
      IE: evt.cancelBubble = true

    • Stop e.g. key events from inserting characters or stopping checkboxes from getting checked:

      Firefox: evt.preventDefault()
      IE: evt.returnValue = false
      Note: Just returning false in keydown, keypress, mousedown, mouseup, click and reset will also prevent default.

    • Get the element which triggered the event:

      Firefox: evt.target
      IE: evt.srcElement

    • Getting the element the mouse cursor moved away from: evt.fromElement in IE is evt.target in Firefox if in an onmouseout event, otherwise evt.relatedTarget

    • Getting the element the mouse cursor moved to: evt.toElement in IE is evt.relatedTarget in Firefox if in an onmouseout event, otherwise evt.target

    • Note: evt.currentTarget (the element to which the event was bound) has no equivalent in IE.


A majority of this page is reproduced from this answer created and shared on Stack Overflow and used according to terms described in the Creative Commons 3.0 Attribution License.