Wrox Home  
Search
Professional JavaScript for Web Developers
by Nicholas C. Zakas
April 2005, Paperback


Inserting DOM range content

The previous three methods all dealt with removing information from the range in one way or another. It is also possible to add content to the range using a couple of different methods.

The insertNode() method enables you to insert a node at the beginning of the selection. Suppose you wanted to insert the following HTML code into the range defined in the previous section:

<span style="color: red">Inserted text</span>

The following code accomplishes this:

var oP1 = document.getElementById("p1");
var oHello = oP1.firstChild.firstChild;
var oWorld = oP1.lastChild;
var oRange = document.createRange();
var oSpan = document.createElement("span");
oSpan.style.color = "red";
oSpan.appendChild(document.createTextNode("Inserted text"));
                
oRange.setStart(oHello, 2);
oRange.setEnd(oWorld, 3);
oRange.insertNode(oSpan);

Running this JavaScript effectively creates the following HTML code:

<p id="p1"><b>He<span style="color: red">Inserted text</span>llo</b> World</p>

Note that the <span/> is inserted just before the llo in Hello, which is the first part of the range selection. Also note that the original HTML didn't add or remove <b/> elements because none of the methods introduced in the previous section were used. You can use this technique to insert helpful information, such as an image next to links that open in a new window.

Along with inserting into the range, it is possible to insert content surrounding the range by using the surroundContents() method. This method accepts one parameter, which is the node that surrounds the range contents. Behind the scenes, the following steps are taken:

  1. The contents of the range are extracted (similar to extractContents()).
  2. The given node is inserted into the position in the original document where the range was.
  3. The contents of the document fragment is added to the given node.

This sort of functionality is useful online to highlight certain words in a web page, like this:

var oP1 = document.getElementById("p1");
var oHello = oP1.firstChild.firstChild;
var oWorld = oP1.lastChild;
var oRange = document.createRange();
var oSpan = document.createElement("span");
oSpan.style.backgroundColor = "yellow";
                
oRange.setStart(oHello, 2);
oRange.setEnd(oWorld, 3);
oRange.surroundContents(oSpan);

The previous code highlights the range selection with a yellow background.

Collapsing a DOM Range

To empty a range, (that is, to have it select no part of the document), you collapse it. Collapsing a range resembles the behavior of a text box. When you have text in a text box, you can highlight an entire word using the mouse. However, if you left-click the mouse again, the selection is removed and the cursor is located between two letters. When you collapse a range, you are setting its locations between parts of a document, either at the beginning of the range selection or at the end. Figure 6 illustrates what happens when a range is collapsed.

JavaScript DOM Ranges : Figure 6
Figure 6

You can collapse a range by using the collapse() method, which accepts a single argument: a Boolean value indicating which end of the range to collapse to. If the argument is true, then the range is collapsed to its starting point; if false, the range is collapsed to its ending point. To determine if a range is collapsed, you can use the collapsed property:

oRange.collapse(true);      //collapse to the starting point
alert(oRange.collapsed);    //outputs "true"

Testing whether a range is collapsed is helpful if you aren't sure if two nodes in the range are next to each other. For example, consider this HTML code:

<p id="p1">Paragraph 1</p><p id="p2">Paragraph 2</p>

If you don't know the exact makeup of this code (because, perhaps, it is automatically generated), you might try creating a range like this:

var oP1 = document.getElementById("p1");
var oP2 = document.getElementById("p2");
var oRange = document.createRange();
oRange.setStartAfter(oP1);
oRange.setStartBefore(oP2);
alert(oRange.collapsed);    //outputs "true"

In this case, the created range is collapsed because there is nothing between the end of p1 and the beginning of p2.

Comparing DOM ranges

If you have more than one range, you can use the compareBoundaryPoints() method to determine if the ranges have any boundaries (start or end) in common. The method accepts two arguments: the range to compare to and how to compare, which is a constant value:

  • * START_TO_START (0) — Compares the starting point of the first range to the starting point of the second
  • START_TO_END (1) — Compares the starting point of the first range to the end point of the second
  • END_TO_END (2) — Compares the end point of the first range to the end point of the second.
  • END_TO_START (3) — Compares the end point of the first range to the start point of the second

The compareBoundaryPoints() method returns -1 if the point from the first range comes before the point from the second range, 0 if the points are equal, or 1 if the point from the first range comes after the point from the second range.

For example:

var oRange1 = document.createRange();
var oRange2 = document.createRange();
var oP1 = document.getElementById("p1");
oRange1.selectNodeContents(oP1);
oRange2.selectNodeContents(oP1);
oRange2.setEndBefore(oP1.lastChild);
alert(oRange1.compareBoundaryPoints(Range.START_TO_START, oRange2)); 
	//outputs 0
alert(oRange1.compareBoundaryPoints(Range.END_TO_END, oRange2));      
	//outputs 1;

In this code, the starting points of the two ranges are exactly the same because both use the default value from selectNodeContents(); therefore, the method returns 0. For oRange2, however, the end point is changed using setEndBefore(), making the end point of oRange1 come after the end point of oRange2 (see Figure 7), so the method returns 1.

JavaScript DOM Ranges : Figure 7
Figure 7

Cloning DOM ranges

If you find the need, you can duplicate any range by calling the cloneRange() method. This method creates an exact duplicate of the range on which it is called:

var oNewRange = oRange.cloneRange();

The new range contains all of the same properties as the original and can be modified without affecting the original in any way.

Clean up

When you are done using a range, it is best to call the detach() method to free up system resources. This isn't required because dereferenced ranges are picked up by the garbage collector eventually. If, however, the range is used initially and then no longer required, calling detach() ensures that it isn't taking up any more memory than necessary:

oRange.detach();

Nicholas C. Zakas is the lead author of Professional Ajax by (Wrox, 2006, ISBN: 0-471-77778-1). This article is adapted from Chapter 10 "Advanced DOM Techniques" of his first book Professional JavaScript for Web Developers (Wrox, 2005, ISBN: 0-7645-7908-8). He has worked in web development for more than five years. He has helped develop web solutions in use at some of the largest companies in the world. Nicholas is also an active blogger on JavaScript and Ajax topics at http://www.nczonline.net/ and some of his other recent articles at Wrox.com are JavaScript XSLT Support in Firefox, Creating an Ajax Search Widget, XPath Support in Browsers, Introduction to the Google Maps API, XMLHttp Requests for Ajax, Ajax and the Yahoo! Connection Manager, and Ajax Submission Throttling.