Using the range object in Mozilla

In this column on the DOM Range Object I will be discussing the application of the Range object with JavaScript for HTML and XML documents. I have found that the Range object is possibly the singularly most powerful object given to us by the W3C DOM Level 2 Recommendation. The only drawback is that no browser currently fully supports it. At least, until now that is. [Article written by Jeffrey M. Yates 26 March 2001]

Introduction

The Range object is a standard Document Object Model object described by the World Wide Web Consortium’s Document Object Model Level 2 Transversal and Range Specification recommendation. Those browsers based upon Mozilla’s code (the Gecko engine) currently implement the Range object. These browsers include but are not limited to, Mozilla, Netscape 6.x, K-Mellon, Galeon and possibly many more.

One of the projects that I have taken on is to write a WYSIWYG (What You See Is What You Get) Rich Text Editor that is cross-browser compatible. In order to do this I have to be able to accept input from the user of what text they have selected via the mouse, as well as a method of selecting text via script for keyboard navigation. In order to do this I needed a working Range object (Microsoft’s version of this is the TextRange object).

Notice: Since the writing of this article it has been found that the Range object has an additional bug. All of the comparison operations of a Range object fail if the range is selecting the contents of an attribute node.

From what I have been able to find out the problem lies with the parentNode property of the children of the Attr node. The parentNode property is always returning null. Until this is corrected I cannot “Patch” Mozilla’s Range object to compensate for this.

This means that the Range object is useless for implementations dealing with attributes, such as the text within an input element or textarea element.

Target audience

This article is for intermediate and advance level JavaScript programmers. The reader is assumed to understand basic JavaScript programming language (statements, syntax, operators, object inheritance, ect.) and the W3C DOM Level 2 Recommendation. Some of the content that goes into advanced programming techniques I will explain or refer the reader to other online resources that does. I suggest all readers to look though the W3C’s Document Object Model Range document if you are not familiar with the Range Object recommendation.

The Range Object

A Range represents a “selection” of a portion of a web document or a document fragment. This range is described as all of the content between two boundary-points, the start and end boundary-points. The range object was created to facilitate editing operations to the DOM, such as cutting, copying, pasting and deleting of content.

Creating an instance of a Range object

The Range object, like most DOM objects, does not have a directly accessible constructor. Instead, the constructor is a method of other DOM objects. In this case (like many other’s) it is a method attached to the Document object. This method is the createRange( ) method.

Properties of a Range object

A range is defined by five read-only properties of the Range object: startContainer, endContainer, startOffset, endOffset and the commonAncestorContainer property, and one “state” property, the collapsed property.

It is important to realize that the range boundary points respond automatically to the mutation of the range contents. An example would be if the start container were removed from the DOM. When this happens the start boundary-point would be shifted to AFTER where the start container lied within the DOM before it’s removal. The mutations that the Range object compensates for are Node deletions, insertions and CharacterData modifications. For more information read Range modification under document mutation.

Visual Representation Of A Range

So far I have been talking about a range as if it something concrete instead of a concept. It is not concrete, but it can be visualized in a concrete manor. There are two methods of visually describing a range, a markup “view” and a Node “view”. Most of the time I will be discussing ranges using the markup view but it is important to note that the range is ALWAYS dealing with the Node view. For a side-by-side comparison of the two I have reproduced the graphic provided in the W3C DOM Level 2 Range Recommendation here.

The Markup View of a Range

Since a range marks all of the markup between two boundary-points in the DOM it is convenient to show this range as within the markup that the range is contained within. I will show this visually by making the markup that the range selects bold as follows:

<BODY><H1>Title</H1><P>Blah xyz.</P></BODY>

In this markup the following text is highlighted: “tle

Bl”. This represents the range. Its startContainer would be the first child node of the H1 element with a startOffset of 2. Its endContainer will be the first child node of the P element with an offset of 2. Its commonAncestorContainer will be the BODY element. I will now walk you though why this is.

The range starts within the text contained within the H1 element. Since the text is only partially selected the start container CAN NOT BE the H1 element itself. Since the text is represented in DOM as a text node the start container is this text node. Since this text node is the first (and in this case only) child node of the H1 element, the start container is the first child node of the H1 element. Notice that the range starts with the third character of this text node. Thus the offset for the start of the range is after the second character but before the third character, thus the offset is 2. You can follow this same logic for the end container and offset.

The common ancestor container of the range is the first element that is a parent of both the start container and the end container. From the start container (a text node) it’s first parent node is the H1 element. This element is only partially selected AND does not contain the end of the range. This being the case it cannot be the ancestor container. The parent node of the H1 element is the BODY element. Following the same logic it is found that the end container also has the BODY element as its parent, thus the BODY element is the common ancestor container.

As you can see, viewing the range in a markup “view” is rather convenient when using a word processor. The Node view on the other hand requires the use of graphics. In this article I will not be using the Node view for this reason.

Manipulating the Range Boundary-Points

Since the properties of a Range object are read-only it would be a useless object if the programmer could not manipulate what part of the DOM that the range selects. Since above I have stated that this is a most powerful object then this must not be a limitation of this object. The Range Recommendation has provided the Range object with a number of methods for manipulating it.

Methods for setting the Range

There are nine functions defined by the W3C that are to be used for selecting the boundary points of a range.

When manipulating the boundary points it is important that the programmer keeps in mind if you attempt to place the starting boundary point AFTER the end boundary point an error will be thrown. The same is true if trying to set the end boundary point before the start boundary point. This cannot be stressed enough. It has caused me numerous errors in my coding until this sunk in.

The Editing Methods of the Range Object

After selecting a fragment of the DOM the Range object allows you to manipulate the contents of the DOM that the range selects. The operations that can be accomplished loosely correspond to deletion, cutting, copying, inserting and surrounding the contents with another node. Even though these operations sound the same as text editors such as FrontPage, they are not the same. More on the differences as I discuss each operation.

The Range object has five methods for manipulating the contents of a range: deleteContents, extractContents, cloneContents, insertNode and surroundContents. Each of these methods are described below.

The DOM before the range extraction: <BODY><H1>Title</H1><P>Blah xyz.</p></body>

The DOM after the range extraction: <BODY><P id=”first”>Ti</P><P id=”second”>ah xyz.</P></BODY>

The document fragment returned: <BODY><P id=”first”>tle</P><P id=”second”>ah xyz.</P></BODY>

Notice how, after the range is extracted, you still have two P nodes. The contents of the partially selected nodes are NOT combined. This is important.

Miscellaneous Range Methods

There are a few methods that do not fall under the broad categories above. I will discuss each below.

Mozilla’s Extensions to the Range Object

Every implementation of W3C’s Document Object Model will include their own extensions to the implementation. The reason why is that the programmers find that in order to get the job done they need more than what the Recommendation calls for. In this section I will list Mozilla’s extension to the Range object. I have not been able to find any documentation on these methods. The following is what I have been able to figure out by testing purposes only.

Making Mozilla’s Implementation of the Range Object Work

At the time of this writing (March 23, 2001) the current build of Mozilla (build 2001031604) does not implement all of the methods of the Range object correctly, or at all. The methods effected are extractContents (bug #58969), cloneContents (bug #58970), insertNode (bug #58972), surroundContents (bug #58974) and deleteContents (bug #43535). These bugs also are in Netscape 6 and 6.01 since this browser is based upon the Mozilla browser’s code.

Related Objects to the Range Object

There is one object that is highly related to the Range object. This object is the Selection object. This object represents the graphically displayed selection within the browser. This selection can be set via the mouse by the user, or it can be set programmatically via JavaScript. Either way it shows up within the browser as highlighted content.

How is this object related to the Range object? It is related because its methods take the Range object as it’s arguments and uses Range object’s as it’s return values. Where the Range object is a powerful tool, the Selection object is the handle to the tool. Below is a quick breakdown of the properties and methods of the Selection object.

Below is a listing of the properties and methods of the Selection object in Mozilla based browsers. I have not found any actual documentation on this object so what you find below is what I have been able to find out experimentally. As I find out more I will update the descriptions below.

Wrap-up

By this time I am hopeful that I have convinced you of the power of the Range object. With the power of this object in the hands of web designers the web will be opened up to an age of dynamic web pages where the page viewer has the ability to change the content as needed. I see a future for web pages to be given the power of true applications with full user editing capabilities. I see a lifting of the chains of static content controlled only by the author of the page. Eventually the end user will be given control.

Where to go from here? This is hard for me to say. For me I will continue developing my DHTML cross platform rich text editor that can be used for e-mail, message boards, text editors, and other user applications. If you, the reader, can think of any other good uses for the Range object please visit www.pbwizard.com and talk to me.

Jeff Yates

Javascript

If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.

Leave Comment

(required)

(required)