Migrate apps from Internet Explorer to Mozilla

来源:百度文库 编辑:神马文学网 时间:2024/06/12 17:57:13
How to make Internet Explorer-specific Web applications work in Mozilla-based browsers

Document options

Print this page

E-mail this page
Free software for rapid results

Kick-start your Java apps
Rate this page

Help us improve this content
Level: Introductory
Doron Rosenberg (doronr@us.ibm.com), Staff Software Engineer, IBM
26 Jul 2005
Ever have trouble getting your Internet Explorer-specific Web applications to work with Mozilla? This article covers common issues associated with migrating applications to the open source Mozilla-based browser. You‘ll first learn basic cross-browser development techniques, and then develop strategies for overcoming the differences between Mozilla and Internet Explorer.
When Netscape started the Mozilla browser, it made the conscious decision to support W3C standards. As a result, Mozilla is not fully backwards-compatible with Netscape Navigator 4.x and Microsoft Internet Explorer legacy code; for example, Mozilla does not support as I will discuss later. Browsers, like Internet Explorer 4, that were built before the conception of W3C standards inherited many quirks. In this article, I will describe Mozilla‘s quirks mode, which provides strong backwards HTML compatibility with Internet Explorer and other legacy browsers.
I‘ll also cover nonstandard technologies, such as XMLHttpRequest and rich text editing, that Mozilla does support because no W3C equivalent existed at the time. They include:
HTML 4.01 andXHTML 1.0/1.1 Cascade Style Sheets (CSS):CSS Level 1,CSS Level 2 and parts ofCSS Level 3 Document Object Model (DOM):DOM Level 1,DOM Level 2 and parts ofDOM Level 3 Mathematical Markup Language:MathML Version 2.0 Extensible Markup Language (XML):XML 1.0,Namespaces in XML,Associating Style Sheets with XML Documents 1.0,Fragment Identifier for XML XSL Transformations:XSLT 1.0 XML Path Language:XPath 1.0 Resource Description Framework:RDF Simple Object Access Protocol:SOAP 1.1 ECMA-262, revision 3 (JavaScript 1.5):ECMA
Even though Web standards do exist, different browsers behave differently (in fact, the same browser may behave differently depending on the platform). Many browsers, such as Internet Explorer, also support pre-W3C APIs and have never added extensive support for the W3C-compliant ones.
Before I go into the differences between Mozilla and Internet Explorer, I‘ll cover some basic ways you can make a Web application extensible in order to add new browser support later.
Since different browsers sometimes use different APIs for the same functionality, you can often find multiple if() else() blocks throughout the code to differentiate between the browsers. The following code shows blocks designated for Internet Explorer:
. . . var elm; if (ns4)   elm = document.layers["myID"]; else if (ie4)   elm = document.all["myID"];
The above code isn‘t extensible, so if you want it to support a new browser, you must update these blocks throughout the Web application.
The easiest way to eliminate the need to recode for a new browser is to abstract out functionality. Rather than multiple if() else() blocks, you increase efficiency by taking common tasks and abstracting them out into their own functions. Not only does this make the code easier to read, it simplifies adding support for new clients:
var elm = getElmById("myID"); function getElmById(aID){   var element = null;   if (isMozilla || isIE5)  element = document.getElementById(aID)   else if (isNetscape4)   element = document.layers[aID]   else if (isIE4)   element = document.all[aID];   return element; }
The above code still has the issue of browser sniffing, or detecting which browser the user is using. Browser sniffing is usually done through the useragent, such as:
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.5) Gecko/20031016
While using the useragent to sniff the browser provides detailed information on the browser in use, code that handles useragents can make mistakes when new browser versions arrive, thus requiring code changes.
If the type of browser doesn‘t matter (suppose that you have already blocked nonsupported browsers from accessing the Web application), it is better to sniff by browser capability. You can usually do this by testing the required functionality in JavaScript. For example, rather than:
if (isMozilla || isIE5)
You would use:
if (document.getElementById)
This would allow other browsers that support that method, such as Opera or Safari, to work without any changes.
Useragent sniffing, however, makes sense when accuracy is important, such as when you‘re verifying that a browser meets the Web application‘s version requirements or you are trying to work around a bug.
JavaScript also allows inline conditional statements, which can help with code readability:
var foo = (condition) ? conditionIsTrue : conditionIsFalse;
For example, to retrieve an element, you would use:
function getElement(aID){   return (document.getElementById) ? document.getElementById(aID) : document.all[aID];    }


Back to top
First, I‘ll discuss the differences in the way HTML behaves between Mozilla and Internet Explorer.
Legacy browsers introduced tooltips into HTML by showing them on links and using the value of the alt attribute as a tooltip‘s content. The latest W3C HTML specification created the title attribute, which is meant to contain a detailed description of the link. Modern browsers will use the title attribute to display tooltips, and Mozilla only supports showing tooltips for that attribute and not the alt attribute.
HTML markup can contain several entities, which theW3 standards body has defined. You can reference entities through their numerical or character reference. For example, you can reference the white space character #160 with   or with its equivalent character reference  .
Some older browsers, such as Internet Explorer, had such quirks as allowing you to use entities by replacing the ; (semi-colon) character at the end with regular text content:
  Foo       Foo
Mozilla will render the above   as white spaces, even though that is against the W3C specification. The browser will not parse a   if it is directly followed by more characters, for example:
 12345
This code does not work in Mozilla, since it is against the W3 standard. Always use the correct form ( ) to avoid browser discrepancies.


Back to top
The Document Object Model (DOM) is the tree structure that contains the document elements. You can manipulate it through JavaScript APIs, which the W3C has standardized. However, prior to W3C standardization, Netscape 4 and Internet Explorer 4 implemented the APIs similarly. Mozilla only implements legacy APIs if they are unachievable with W3C standards.
To retrieve an element reference following the cross-browser approach, you use document.getElementById(aID), which works in Internet Explorer 5.5+, Mozilla, and is part of the DOM Level 1 specification.
Mozilla does not support accessing an element through document.elementName or even through the element‘s name, which Internet Explorer does (also called global namespace polluting). Mozilla also does not support the Netscape 4 document.layers method and Internet Explorer‘s document.all. While document.getElementById lets you retrieve one element, you can also use document.layers and document.all to obtain a list of all document elements with a certain tag name, such as all
elements.
The W3C DOM Level 1 method gets references to all elements with the same tag name through getElementsByTagName(). The method returns an array in JavaScript, and can be called on the document element or other nodes to search only their subtree. To get an array of all elements in the DOM tree, you can use getElementsByTagName(*).
The DOM Level 1 methods, as shown in Table 1, are commonly used to move an element to a certain position and toggle its visibility (menus, animations). Netscape 4 used the tag, which Mozilla does not support, as an HTML element that can be positioned anywhere. In Mozilla, you can position any element using the
tag, which Internet Explorer uses as well and which you‘ll find in the HTML specification.
Table 1. Methods used to access elements Method Description
document.getElementById( aId ) Returns a reference to the element with the specified ID.
document.getElementsByTagName( aTagName ) Returns an array of elements with the specified name in the document.
Mozilla supports the W3C DOM APIs for traversing the DOM tree through JavaScript (see Table 2). The APIs exist for each node in the document and allow walking the tree in any direction. Internet Explorer supports these APIs as well, but it also supports its legacy APIs for walking a DOM tree, such as the children property.
Table 2. Methods used to traverse the DOM Property/Method Description
childNodes Returns an array of all child nodes of the element.
firstChild Returns the first child node of the element.
getAttribute( aAttributeName ) Returns the value for the specified attribute.
hasAttribute( aAttributeName )  Returns a boolean stating if the current node has an attribute defined with the specified name.
hasChildNodes()  Returns a boolean stating whether the current node has any child nodes.
lastChild Returns the last child node of the element.
nextSibling Returns the node immediately following the current one.
nodeName Returns the name of the current node as a string.
nodeType  Returns the type of the current node.
Value Description
1 Element Node
2 Attribute Node
3 Text Node
4 CDATA Section Node
5 Entity Reference Node
6 Entity Node
7 Processing Instruction Node
8 Comment Node
9 Document Node
10 Document Type Node
11 Document Fragment Node
12 Notation Node
nodeValue Returns the value of the current node. For nodes that contain text, such as text and comment nodes, it will return their string value. For attribute nodes, the attribute value is returned. For all other nodes, null is returned.
ownerDocument Returns the document object containing the current node.
parentNode Returns the parent node of the current node.
previousSibling Returns the node immediately preceding the current one.
removeAttribute( aName ) Removes the specified attribute from the current node.
setAttribute( aName, aValue ) Sets the value of the specified attribute with the specified value.
Internet Explorer has a nonstandard quirk, where many of these APIs will skip white space text nodes that are generated, for example, by new line characters. Mozilla will not skip these, so sometimes you need to distinguish these nodes. Every node has a nodeType property specifying the node type. For example, an element node has type 1, while a text node has type 3 and a comment node is type 8. The best way to only process element nodes is to iterate over all child nodes and only process those with a nodeType of 1:
HTML:  
  Test   c
  JavaScript:   var myDiv = document.getElementById("foo");   var myChildren = myXMLDoc.childNodes;   for (var i = 0; i < myChildren.length; i++) {     if (myChildren[i].nodeType == 1){   // element node    }   }
Mozilla supports the legacy methods for adding content into the DOM dynamically, such as document.write, document.open and document.close. Mozilla also supports Internet Explorer‘s InnerHTML method, which it can call on almost any node. It does not, however, support OuterHTML (which adds markup around an element, and has no standard equivalent) and innerText (which sets the text value of the node, and which you can achieve in Mozilla by using textContent).
Internet Explorer has several content manipulation methods that are nonstandard and unsupported in Mozilla, including retrieving the value; inserting text; and inserting elements adjacent to a node, such as getAdjacentElement and insertAdjacentHTML. Table 3 shows how the W3C standard and Mozilla manipulate content, all of which are methods of any DOM node.
Table 3. Methods Mozilla uses to manipulate content Method Description
appendChild( aNode ) Creates a new child node. Returns a reference to the new child node.
cloneNode( aDeep ) Makes a copy of the node it is called on and returns the copy. If aDeep is true, it copies over the node‘s entire subtree.
createElement( aTagName ) Creates and returns a new and parentless DOM node of the type specified by aTagName.
createTextNode( aTextValue ) Creates and returns a new and parentless DOM textnode with the data value specified by aTextValue.
insertBefore( aNewNode, aChildNode ) Inserts aNewNode before aChildNode, which must be a child of the current node.
removeChild( aChildNode ) Removes aChildNode and returns a reference to it.
replaceChild( aNewNode, aChildNode ) Replaces aChildNode with aNewNode and returns a reference to the removed node.
For performance reasons, you can create documents in memory, rather than working on the existing document‘s DOM. DOM Level 1 Core introduced document fragments, which are lightweight documents that contain a subset of a normal document‘s interfaces. For example, getElementById does not exist, but appendChild does. You can also easily add document fragments to existing documents.
Mozilla creates document fragments through document.createDocumentFragment(), which returns an empty document fragment.
Internet Explorer‘s implementation of document fragments, however, does not comply with the W3C standard and simply returns a regular document.


Back to top
Most differences between Mozilla and Internet Explorer are usually blamed on JavaScript. However, the issues usually lie in the APIs that a browser exposes to JavaScript, such as the DOM hooks. The two browsers possess few core JavaScript differences; issues encountered are often timing related.
The only Date difference is the getYear method. Per the ECMAScript specification (which is the specification JavaScript follows), the method is not Y2k-compliant, and running new Date().getYear() in 2004 will return "104". Per the ECMAScript specification, getYear returns the year minus 1900, originally meant to return "98" for 1998. getYear was deprecated in ECMAScript Version 3 and replaced with getFullYear(). Internet Explorer changed getYear() to work like getFullYear() and make it Y2k-compliant, while Mozilla kept the standard behavior.
Different browsers execute JavaScript differently. For example, the following code assumes that the div node already exists in the DOM by the time the script block executes:
...
Loading...

However, this is not guaranteed. To be sure that all elements exist, you should use the onload event handler on the tag:
Loading...
...
Such timing-related issues are also hardware-related -- slower systems can reveal bugs that faster systems hide. One concrete example is window.open, which opens a new window:

The problem with the code is that window.open is asynchronous -- it does not block the JavaScript execution until the window has finished loading. Therefore, you may execute the line after the window.open line before the new window has finished. You can deal with this by having an onload handler in the new window and then call back into the opener window (using window.opener).
JavaScript can, through document.write, generate HTML on the fly from a string. The main issue here is when JavaScript, embedded inside an HTML document (thus, inside an inside the string as the closing tag for the enclosing ")
Since the page is in strict mode, Mozilla‘s parser will see the first . This is because the parser has no knowledge about JavaScript (or any other language) when in strict mode. In quirks mode, the parser is aware of JavaScript when parsing (which slows it down). Internet Explorer is always in quirks mode, as it does not support true XHTML. To make this work in strict mode in Mozilla, separate the string into two parts:
...
Mozilla provides several ways to debug JavaScript-related issues found in applications created for Internet Explorer. The first tool is the built-in JavaScript console, shown in Figure 1, where errors and warnings are logged. You can access it in Mozilla by going to Tools -> Web Development -> JavaScript Console, or in Firefox (the standalone browser product from Mozilla) at Tools -> JavaScript Console.

The JavaScript console can show the full log list or just errors, warnings, and messages. The error message in Figure 1 says that at aol.com, line 95 tries to access an undefined variable called is_ns70. Clicking on the link will open Mozilla‘s internal view source window with the offending line highlighted.
The console also allows you to evaluate JavaScript. To evaluate the entered JavaScript syntax, type in 1+1 into the input field and press Evaluate, as Figure 2 shows.

Mozilla‘s JavaScript engine has built-in support for debugging, and thus can provide powerful tools for JavaScript developers. Venkman, shown in Figure 3, is a powerful, cross-platform JavaScript debugger that integrates with Mozilla. It is usually bundled with Mozilla releases; you can find it at Tools -> Web Development -> JavaScript Debugger. For Firefox, the debugger isn‘t bundled; instead, you can download and install it fromhttp://www.mozilla.org/projects/venkman/. You can also find tutorials at the development page, located athttp://www.hacksrus.com/~ginda/venkman/.

The JavaScript debugger can debug JavaScript running in the Mozilla browser window. It supports such standard debugging features as breakpoint management, call stack inspection, and variable/object inspection. All features are accessible through the user interface or through the debugger‘s interactive console. With the console, you al can execute arbitrary JavaScript in the same scope as the JavaScript currently being debugged.


Back to top
Mozilla has the strongest support for Cascading Style Sheets (CSS), including most of CSS1, CSS2, and parts of CSS3, compared to Internet Explorer as well as all other browsers.
For most issues mentioned below, Mozilla will add an error or warning entry into the JavaScript console. Check the JavaScript console if you encounter CSS-related issues.
The most common CSS-related issue is that CSS definitions inside referenced CSS files are not applied. This is usually due to the server sending the wrong mimetype for the CSS file. The CSS specification states that CSS files should be served with the text/css mimetype. Mozilla will respect this and only load CSS files with that mimetype if the Web page is in strict standards mode. Internet Explorer will always load the CSS file, no matter with which mimetype it is served. Web pages are considered in strict standards mode when they start with a strict doctype. To solve this problem, you can make the server send the right mimetype or remove the doctype. I‘ll discuss more about doctypes in the next section.
Many Web applications do not use units with their CSS, especially when you use JavaScript to set the CSS. Mozilla tolerates this, as long as the page is not rendered in strict mode. Since Internet Explorer doesn‘t support true XHTML, it does not care if no units are specified. If the page is in strict standards mode, and no units are used, then Mozilla ignores the style:
// works in strict mode  
Text
// will fail in strict mode
Text

Since the above example has a strict doctype, the page is rendered in strict standards mode. The first div will have a width of 40px, since it uses units, but the second div won‘t get a width, and thus will default to 100% width. The same would apply if the width were set through JavaScript.
Since Mozilla supports the CSS standards, it also supports the CSS DOM standard for setting CSS through JavaScript. You can access, remove, and change an element‘s CSS rules through the element‘s style member:
Text

You can reach every CSS attribute that way. Again, if the Web page is in strict mode, you must set a unit or else Mozilla will ignore the command.
When you query a value, say through .style.width, in Mozilla and Internet Explorer, the returned value will contain the unit, meaning a string is returned. You can convert the string into a number through parseFloat("40px").
CSS added the notion of overflow, which allows you to define how to handle overflow; for example, when the contents of a div with a specified height are taller than that height. The CSS standard defines that if no overflow behavior is set in this case, the div contents will overflow. However, Internet Explorer does not comply with this, and will expand the div beyond its set height in order to hold the contents. Below is an example that shows this difference:
a

As you can see in Figure 4, Mozilla acts like the standard specifies. The standard says that in this case, the inner div overflows to the bottom since the inner content is taller than it‘s parent. If you prefer the Internet Explorer behavior, simply don‘t specify a height on the outer element.

The nonstandard CSS hover behavior in Internet Explorer occurs on quite a few IBM Web sites. It usually manifests itself by changing text style when hovered over in Mozilla, but not in Internet Explorer. This is because the a:hover CSS selector in Internet Explorer matches but not , which sets anchors in HTML. The text changes occur because authors encapsulate the areas with the anchor-setting markup:
CSS: a:hover {color:green;}  HTML:
This should turn green when you hover over it.   This should change color when hovered over, but doesn‘t in Internet Explorer.
Mozilla follows the CSS specification correctly and will change the color to green in this example. You can use two ways to make Mozilla behave like Internet Explorer and not change the color of the text when hovered over:
First, you can change the CSS rule to be a:link:hover {color:green;}, which will only change the color if the element is a link (has an href attribute). Alternatively, you can change the markup and close the opened before the start of the text -- the anchor will continue to work this way.


Back to top
Older legacy browsers, such as Internet Explorer 4, rendered with so-called quirks under certain conditions. While Mozilla aims to be a standards-compliant browser, it has three modes that support older Web pages created with these quirky behaviors. The page‘s content and delivery determine which mode Mozilla will use. Mozilla will list the rendered mode in View -> Page Info (or Ctrl-i). The mode in which a page is located depends on its doctype.
Doctypes (short for document type declarations) look like this:

The section in blue is called the public identifier, the green part is the system identifier, which is a URI.
Standards mode is the strictest rendering mode -- it will render pages per the W3C HTML and CSS specifications and will not support any quirks. Mozilla uses it for the following conditions:
If a page is sent with a text/xml mimetype or any other XML or XHTML mimetype For any "DOCTYPE HTML SYSTEM" doctype (for example, ), except for the IBM doctype For unknown doctypes or doctypes without DTDs
Mozilla introduced almost standards mode for one reason: a section in the CSS 2 specification breaks designs based on a precise layout of small images in table cells. Instead of forming one image to the user, each small image ends up with a gap next to it. The old IBM homepage shown in Figure 5 offers an example.

Almost standards mode behaves almost exactly as standards mode, except when it comes to an image gap issue. The issue occurs often on standards-compliant pages and causes them to display incorrectly.
Mozilla uses almost standards mode for the following conditions:
For any "loose" doctype (for example, , ) For the IBM doctype ()
You can read more about theimage gap issue.
Currently, the Web is full of invalid HTML markup, as well as markup that only functions due to bugs in browsers. The old Netscape browsers, when they were the market leaders, had bugs. When Internet Explorer arrived, it mimicked those bugs in order to work with the content at that time. As newer browsers came to market, most of these original bugs, usually called quirks, were kept for backwards compatibility. Mozilla supports many of these in its quirks rendering mode. Note that due to these quirks, pages will render slower than if they were fully standards-compliant. Most Web pages are rendered under this mode.
Mozilla uses quirks mode for the following conditions:
When no doctype is specified For doctypes without a system identifier (for example, )
For further reading, check out:List of Quirks andList of Doctypes and What Modes They Cause.


Back to top
Mozilla and Internet Explorer are almost completely different in the area of events. The Mozilla event model follows the W3C and Netscape model. In Internet Explorer, if a function is called from an event, it can access the event object through window.event. Mozilla passes an event object to event handlers. They must specifically pass the object on to the function called through an argument. A cross-browser event handling example follows:
Click me!
 
The properties and functions that the event object exposes are also often named differently between Mozilla and Internet Explorer, as Table 4 shows.
Table 4. Event properties differences between Mozilla and Internet Explorer Internet Explorer Name Mozilla Name Description
altKey altKey Boolean property that returns whether the alt key was pressed during the event.
cancelBubble stopPropagation() Used to stop the event from bubbling farther up the tree.
clientX clientX The X coordinate of the event, in relation to the client.
clientY clientY The Y coordinate of the event, in relation to the client.
ctrlKey ctrlKey Boolean property that returns whether the Ctrl key was pressed during the event.
fromElement relatedTarget For mouse events, this is the element from which the mouse moved away.
keyCode keyCode For keyboard events, this is a number representing the key that was pressed. It is 0 for mouse events.
returnValue preventDefault() Used to prevent the event‘s default action from occurring.
screenX screenX The X coordinate of the event, in relation to the screen.
screenX screenY The Y coordinate of the event, in relation to the screen.
shiftKey shiftKey Boolean property that returns whether the Shift key was pressed during the event.
srcElement target The element to which the event was originally dispatched.
toElement currentTarget For mouse events, this is the element to which the mouse moved.
type type Returns the name of the event.
Mozilla supports two ways to attach events through JavaScript. The first, supported by all browsers, sets event properties directly on objects. To set a click event handler, a function reference is passed to the object‘s onclick property:
Click me!
 
Mozilla fully supports the W3C standard way of attaching listeners to DOM nodes. You use the addEventListener() and removeEventListener() methods, and have the benefit of being able to set multiple listeners for the same event type. Both methods require three parameters: the event type, a function reference, and a boolean denoting whether the listener should catch events in their capture phase. If the boolean is set to false, it will only catch bubbling events. W3C events have three phases: capturing, at target, and bubbling. Every event object has an eventPhase attribute indicating the phase numerically (0 indexed). Every time you trigger an event, the event starts at the DOM‘s outermost element, the element at the top of the DOM tree. It then walks the DOM using the most direct route toward the target, which is the capturing phase. When the event reaches the target, the event is in the target phase.  After arriving at the target, it walks up the DOM tree back to the outermost node; this is bubbling. Internet Explorer‘s event model only has the bubbling phase; therefore, setting the third parameter to false results in Internet Explorer-like behavior:
Click me!

One advantage of addEventListener() and removeEventListener() over setting properties is that you can have multiple event listeners for the same event, each calling another function. Thus, to remove an event listener requires all three parameters be the same as the ones you use when adding the listener.
Mozilla does not support Internet Explorer‘s method of converting