Document Object Model

DOM Resources

DOM

All modern web browsers have some support for the W3C's Document Object Model, which describes an HTML or XML document as a tree made up of nodes. The trouble is that every browser differs in the way that it support the DOM.

The mozile.dom module contains a number of functions that were written to bridge the gap between DOM implementations. Take a look at Mozile's API documentation for mozile.dom. The odds are that if some developer took the trouble to write a function for that module, then there must have been some cross-browser annoyance that prompted him to do it. You should take a look and consider using it in your code.

XML

The mozile.xml module bridges the differences between browsers when it comes to things like parsing and serializing XML. It also holds a list of common XML namespaces in mozile.xml.ns.

XML namespace support is important to Mozile. However, namespace support is not even as consistent between browsers as the rest of the DOM is. When Mozile is editing HTML documents, no namespaces should ever be used and all nodes should be HTML nodes. When editing XHTML and XML namespaces should always be used, and all nodes should be XML nodes.

Caution

Mozile's support for XML documents and namespaces is not yet complete.

XPath

In the DOM, each node is an object. You can navigate from one node to another. You can move nodes. You can create new node and destroy them. But as the structure of the document changes, and nodes move and are replaced, we need a way to access the document by its structure, not just by the nodes. Event if a node has moved from the first paragraph to the fifth paragraph, we still need a way to address the first node of the first paragraph.

XPath is a way of doing just this. The XPath specification is closely tied to the XSL specification, and you can find a good tutorial for bother of them at Zvon.org. You can think of XPath as like a file path; it's a hierarchy which starts at a root and leads to a node. For example:

Example 3.1. Simple XPath example

/html[1]/body[1]/div[2]/p[3]/span[1]/text()[1]

The numbers inside the square brackets tell us that we want the nth matching node. So in this example we want the first html element, and the first body element which is a child of the html element. Then we want the second div in the body, the third p inside that div, the first span in the p, and the first text node in the span.

XPath can get much more complicated than this. It can select sets of nodes, along different axes, matching different patterns, and using several built-in functions. However in Mozile we only use simple XPaths like these. The only other complication we use in Mozile is namespaces.

Example 3.2. XPath example with namespaces

/xhtml:html[1]/xhtml:body[1]/xhtml:div[2]/mathml:math[3]

In this example we have nodes from the XHTML and MathML namespaces, as indicated by the xhtml and mathml prefixes. That's about as complicated as it gets.

You can look at the API for the mozile.xpath module, but most of the time we just use mozile.xpath.getXPath() to get the XPath address of a node, and mozile.xpath.getNode() to get a node from and XPath address. Any time we need to store a location in the document which might be changed, we store it using XPath.

Mozilla

Mozilla's Gecko rendering engine is known for its standards support. There's also a lot to be said for having access to the Mozilla source code, to see what's happening under the hood. Most of the time, Mozilla's behaviour is the model we follow in Mozile. However, there are also known bugs. And not all of the browsers built on top of Gecko behave the same way in every situation.

One particularly problematic aspect of Mozilla is its designMode. By setting document.designMode = "on" an HTML page will enter a mode which allows editing pretty much the same as the old Netscape Composer. Using document.execCommand() you can add font tags, etc. You can also resize images and tables. Most importantly, you get a cursor which you can use for editing the document.

If the mozile.useDesignMode property is true (which is the default), then Mozile will use designMode in order to get a cursor. No execCommand() calls are made, and designMode is turned off when outside an editable area.

The problem is that designMode hasn't seen nearly as much attention as other parts of Mozilla, and it has many bugs. The alternative is to use the "Caret Browsing" mode, which can be activated by the user by pressing F7, or by setting the browser preference accessibility.browsewithcaret. However, the former must be done manually, and the latter requires the UniversalXPConnect privilege, which is not given to remotely executed scripts (for good reason). Since designMode doesn't work in XHTML and XML documents, Caret Browsing is the only option. For XHTML and XML documents set mozile.useDesignMode = false.

Internet Explorer

Internet Explorer is the most popular browser on the Internet by a large margin. Because it's so dominant it's very difficult to ignore. We may be tempted to ignore it, because its poor standards support often makes developing for Internet Explorer difficult. However, IE does support all the features necessary to make Mozile work. Mozile currently has support for IE 6, and we expect to support IE 7 in the future.

Internet Explorer supports designMode, like Mozilla does, with all of its shortcomings. IE also supports the superior contentEditable attribute, which allows selected elements to be made editable while others are not. Mozile uses contentEditable in order to have a cursor for editing, but it does not use execCommand() to perform any of its editing operations. When contentEditable is enabled, IE uses different white space rules than the standard HTML and XML rules. In most ways this makes editing more convenient, and similar functionality has to be explicitly coded by Mozile for Mozilla.

There are many differences in the way IE implements the DOM. The difference which has had the single largest effect on the Mozile architecture is the fact that DOM objects cannot be prototyped in IE the way they can in other browsers. In Mozilla you can declare a new method Text.prototype.foo(), and all text nodes will gain the foo() method. Text is treated as a normal JavaScript object with prototype-based inheritance. In IE this is not the case. Although prototyping of some DOM objects seems to be allowed, in general it is not. Prototyping of XML objects, for instance, is not permitted. Mozile 0.7 made extensive use of prototyping, adding methods like Node.isBlock(), which in many ways leads to more readable code. But Mozile 0.8 had to abandon this in favour of functions like mozile.edit.isBlock(node).

Most of the DOM Level 1 and DOM Level 2 specifications are implemented similarly by IE and Mozilla. IE has limited namespace support in its DOM methods. It does not allow HTML and XML nodes to mix as freely as Mozilla does. IE does not provide a TreeWalker object, and so Mozile provides one in mozile.dom.TreeWalker.

The largest gap between Mozilla and Internet Explorer that Mozile developers have had to bridge is between the two range and selection implementations. When the user highlights a range of text, we need to be able to determine the start and end points, and then perform manipulations on that range. Mozilla provides Range and Selection objects. End points are specified by the pair of a node and an integer offset; this could be a text node and a number of characters, or an element and a number of child nodes.

Internet Explorer provides a TextRange and a selection object. IE does not use pairs of nodes and points to indicate positions, just character offsets within an element. Since selections are critical for editing, a great deal of effort has been put in to wrapping the IE selection system in an interface which is compatible with the Mozilla system. See the mozile.dom.InternetExplorerRange and mozile.dom.InternetExplorerSelection modules. By accessing the window's selection using mozile.dom.selection.get() you can expect the selection object to behave according to the Mozilla model on all supported browsers.

Caution

The mozile.dom.InternetExplorerRange and mozile.dom.InternetExplorerSelection modules do not yet implement all of the Range and Selection methods. See the API documentation for details.

Internet Explorer's CSS support is not equivalent to Mozilla's in many ways. This affects Mozile's GUI code.

In general, Internet Explorer provides similar features to Mozilla. However be careful of the differences in the way the two implementations work.

Opera

Opera 9 adds a number of new features which make editing with Mozile possible. In most ways Opera does a very good job of matching Mozilla's behaviour, and so adapting Mozile's code to work with Opera is generally easy. However, "porting" Mozile to another browser is always time consuming, and Opera support will have to wait for someone to find that time.

Safari

The current release version of Safari for Mac OS X 10.4.6 lacks a number of features which Mozila requires, including Selection and Range objects, and XML namespace support. Development versions, however, include more and more of these requirements, so we expect to be able to support Safari at some point in the future.

Since Safari and Konqueror are based on the same WebKit/KHTML rendering engine, to a large extent what goes for one goes for the other. However there are important differences, and Konqueror does not always include the latest changes that Safari features.