8.1. Introduction

A Range identifies a range of content in a Document, DocumentFragment or
Attr. It is contiguous in the sense that it can be characterized as
selecting all of the content between a pair of boundary-points.

Note:
In a text editor or a word processor, a user can make a selection by
pressing down the mouse at one point in a document, moving the mouse to
another point, and releasing the mouse. The resulting selection is
contiguous and consists of the content between the two points.

The term 'selecting' does not mean that every Range corresponds to a
selection made by a GUI user; however, such a selection can be returned
to a DOM user as a Range.

Note:
In bidirectional writing (Arabic, Hebrew), a range may correspond to a
logical selection that is not necessarily contiguous when displayed. A
visually contiguous selection, also used in some cases, may not
correspond to a single logical selection, and may therefore have to be
represented by more than one range.

The Range interface provides methods for accessing and manipulating the
document tree at a higher level than similar methods in the Node
interface. The expectation is that each of the methods provided by the
Range interface for the insertion, deletion and copying of content can be
directly mapped to a series of Node editing operations enabled by DOM
Core. In this sense, the Range operations can be viewed as convenience
methods that also enable the implementation to optimize common editing
patterns.

This chapter describes the Range interface, including methods for
creating and moving a Range and methods for manipulating content with
Ranges. The feature string for the interfaces listed in this section is
"Range".

8.2. Definitions and Notation

8.2.1. Position

This chapter refers to two different representations of a
document: the text or source form that includes the
document markup and the tree representation similar
to the one described in the Introduction.

A Range consists of two boundary-points
corresponding to the start and the end of the
Range. A
boundary-point's position in a document or document
fragment tree can be characterized by a node and an
offset. The node is called the
container of the boundary-point and of its
position. The container and its ancestors are the
ancestor containers of the boundary-point and
of its position. The offset within the node is called the
offset of the boundary-point and its
position. If the container is an Attribute,
Document, Document Fragment, Element or
EntityReference node, the offset is between its child
nodes. If the container is a CharacterData, Comment or
ProcessingInstruction node, the offset is between the
16-bit units of the UTF-16 encoded string contained by
it.

The boundary-points of
a Range must have a common ancestor container
which is either a Document, DocumentFragment or Attr
node. That is, the content of a Range must be entirely
within the subtree rooted by a single Document,
DocumentFragment or Attr Node. This common
ancestor
container is known as the root
container of the Range. The tree rooted
by the root container
is known as the Range's context tree.

The container of an
boundary-point of
a Range must be an Element, Comment,
ProcessingInstruction, EntityReference, CDATASection,
Document, DocumentFragment, Attr, or Text node. None of
the ancestor
containers of the
boundary-point of a Range can be a
DocumentType, Entity or Notation node.

In terms of the text representation of a document,
the boundary-points of
a Range can only be on token boundaries. That is, the
boundary-point of the
text range cannot be in the middle of a start- or
end-tag of an element or within the name of an entity
or character reference. A Range locates a contiguous
portion of the content of the structure model.

The relationship between locations in a text
representation of the document and in the Node tree
interface of the DOM is illustrated in the following
diagram:

Range Example

In this diagram, four different Ranges are
illustrated. The boundary-points of each range
are labelled with s# (the start of the range)
and e# (the end of the range), where # is the
number of the Range. For Range 2, the start is in the
BODY element and is immediately after the H1 element
and immediately before the P element, so its position
is between the H1 and P children of BODY. The offset of a boundary-point whose container is not a CharacterData
node is 0 if it is before the first child, 1 if
between the first and second child, and so on. So,
for the start of the Range 2, the container is BODY and the
offset is 1. The
offset of a
boundary-point whose
container is a
CharacterData node is obtained similarly but using 16-bit unit
positions instead. For example, the boundary-point labelled
s1 of the Range 1 has a Text node (the one
containing "Title") as its container and an offset of 2 since it is
between the second and third 16-bit unit.

Notice that the boundary-points of Ranges 3 and 4
correspond to the same location in the text
representation. An important feature of the Range is
that a boundary-point of a Range can unambiguously
represent every position within the document tree.

If the
boundary-points of a Range have the same containers and offsets, the Range is said
to be a collapsed Range. (This
is often referred to as an insertion point in a user
agent.)

8.2.2. Selection and Partial Selection

A node or
16-bit unit is said to be selected by a
Range if it is between the two boundary-points of the
Range, that is, if the position immediately
before the node or 16-bit unit is before the end of
the Range and the position immediately after the node
or 16-bit unit is after the start of the range. For
example, in terms of a text representation of the
document, an element would be selected by a Range if its
corresponding start-tag was located after the start of
the Range and its end-tag was located before the end
of the Range. In the examples in the above diagram,
the Range 2 selects the P node and the
Range 3 selects the text node
containing the text "Blah xyz."

A node is said to be partially
selected by a Range if it is an ancestor
container of exactly one boundary-point of the
Range. For example, consider Range 1
in the above diagram. The element H1 is partially
selected by that Range since the start of
the Range is within one of its children.

8.2.3. Notation

Many of the examples in this chapter are
illustrated using a text representation of a
document. The boundary-points of a range are indicated by
displaying the characters (be they markup or data
characters) between the two boundary-points in bold, as
in

<FOO>ABC<BAR>DEF</BAR></FOO>

When both boundary-points are at the same position, they
are indicated with a bold caret ('^'), as in

<FOO>A^BC<BAR>DEF</BAR></FOO>

And when referring to a single boundary-point, it will
be shown as a bold asterisk ('*') as in

<FOO>A*BC<BAR>DEF</BAR></FOO>

8.3. Creating a Range

A range is created by calling the createRange()
method on the DocumentRange interface. This
interface can be obtained from the object implementing the
Document
interface using binding-specific casting methods.

interface DocumentRange {
Range createRange();
}

The initial state of the range returned from this method
is such that both of its boundary-points are positioned at
the beginning of the corresponding Document, before
any content. In other words, the container of each
boundary-point is the Document node and the offset within
that node is 0.

Like some objects created using methods in the Document
interface (such as Nodes and DocumentFragments),
Ranges created via a particular document instance can
select only content associated with that
Document, or with DocumentFragments and Attrs for which
that Document is the ownerDocument. Such
Ranges, then, can not be used with other Document
instances.

8.4. Changing a Range's Position

A Range's position can be specified by setting the
container and offset of each boundary-point with the
setStart and setEnd methods.

If one boundary-point of a Range is set to have a root container other
than the current one for the range, the range is collapsed to the new
position. This enforces the restriction that both
boundary-points of a Range must have the same root container.

The start position of a Range is guaranteed to never be
after the end position. To enforce this restriction,
if the start is set to be at a position after the end,
the range is collapsed to that
position. Similarly, if the end is set to be at a
position before the start, the range is collapsed to that
position.

It is also possible to set a Range's position relative to
nodes in the tree:

Passing TRUE as the parameter
toStart will collapse the Range to its
start , FALSE to its end.

Testing whether a Range is collapsed can be done by examining the
isCollapsed attribute:

readonly attribute boolean isCollapsed;

The following methods can be used to make a range select the
contents of a node or the node itself.

void selectNode(in Node n);
void selectNodeContents(in Node n);

The following examples demonstrate the operation of the methods
selectNode and selectNodeContents:

Before:
^<BAR><FOO>A<MOO>B</MOO>C</FOO></BAR>
After range.selectNodeContents(FOO):
<BAR><FOO>A<MOO>B</MOO>C</FOO></BAR>
(In this case, FOO is the parent of both boundary-points)
After range.selectNode(FOO):
<BAR><FOO>A<MOO>B</MOO>C</FOO></BAR>

8.5. Comparing Range Boundary-Points

It is possible to compare two Ranges by comparing their boundary-points:

where CompareHow is one of four values:
StartToStart, StartToEnd,
EndToEnd and EndToStart. The
return value is -1, 0 or 1 depending on whether the
corresponding boundary-point of the Range is before, equal
to, or after the corresponding boundary-point of
sourceRange. An exception is thrown if
the two Ranges have different root containers.

The result of comparing two boundary-points (or positions) is
specified below. An informal but not always correct
specification is that an boundary-point is before, equal
to, or after another if it corresponds to a location
in a text representation before, equal to, or after
the other's corresponding location.

Let A and B be two boundary-points or positions. Then
one of the following holds: A is before
B, A is equal to B, or A is
after B. Which one holds is specified in
the following by examining four cases:

In the first case the boundary-points have the same container. A is
before B if its offset is less than the
offset of B, A is
equal to B if its offset is equal to the
offset of B, and A
if after B if its offset is greater than the
offset of B.

In the second case a child C of the container of A is an
ancestor
container of B. In this case, A is
before B if the offset of A is less than or
equal to the index of the child C and A is
after B otherwise.

In the third case a child C of the container of B is an
ancestor
container of A. In this case, A is
before B if the index of the child C is
less than the offset of B and A is
after B otherwise.

In the fourth case, none of three other cases hold:
the containers of A and B are siblings or descendants
of sibling nodes. In this case, A is
before B if the container of A is before
the container of
B in a pre-order traversal of the Ranges' context tree and A is
after B otherwise.

Note that because the same location in a text
representation of the document can correspond to two
different positions in the DOM tree, it is possible
for two boundary-points to not compare equal even though
they would be equal in the text representation. For
this reason, the informal definition above can
sometimes be incorrect.

8.6. Deleting Content with a Range

One can delete the contents selected by a Range with:

void deleteContents();

deleteContents() deletes all nodes and
characters selected by the Range. All other nodes and
characters remain in the context tree of the
Range. Some examples of this deletion operation are:

After deleteContents() is invoked on a Range,
the Range is collapsed. If no node was
partially
selected by the Range, then it is collapsed to its original
start point, as in example (1). If a node was partially
selected by the Range and was an ancestor
container of the start of the Range and no
ancestor of the node satisfies these two conditions,
then the Range is collapsed to the position
immediately after the node, as in examples (2) and
(4). If a node waspartially
selected by the Range and was an ancestor
container of the end of the Range and no
ancestor of the node satisfies these two conditions,
then the Range is collapsed to the position
immediately before the node, as in examples (3) and
(4).

8.7. Extracting Content

If the contents of a range need to be extracted rather than
deleted, the following method may be used:

DocumentFragment extractContents();

The extractContents() method removes nodes
from the Range's context
tree similarly to the deleteContents()
method. In addition, it places the deleted contents in a new
DocumentFragment. The following examples illustrate the
contents of the returned document fragment:

It is important to note that nodes that are partially
selected by the range are cloned. Since part
of such a node's contents must remain in the Range's
context tree
and part of the contents must be moved to the new
fragment, a clone of the partially
selected node is included in the new
fragment. Note that cloning does not take place for
selected
elements; these nodes are moved to the new
fragment.

8.8. Cloning Content

The contents of a range may be duplicated using the following
method:

DocumentFragment cloneContents();

This method returns a DocumentFragment that is similar to
the one returned by the method
extractContents(). However, in this case,
the original nodes and character data in the Range are
not removed from the Range's context tree. Instead,
all of the nodes and text content within the returned
DocumentFragment are cloned.

8.9. Inserting Content

A node may be inserted into a range using the following method:

void insertNode(in Node n) raises(RangeException);

The insertNode() method inserts the specified
node into the Range's context tree. For this
method, the end of the range is ignored and the node
is inserted at the start of the range.

The Node passed into this method can be a
DocumentFragment. In that case, the contents of the
fragment are inserted at the start position of the
range, but the fragment itself is not. Note that if
the Node represents the root of a sub-tree, the entire
sub-tree is inserted.

The same rules that apply to the
insertBefore() method on the Node
interface apply here. Specifically, the Node passed in,
if it already has a parent,
will be removed from its existing position.

8.10. Surrounding Content

The insertion of a single node to subsume the content
selected by a Range can be performed with:

void surroundContents(in Node n);

The surroundContents() method causes all of
the content selected by the range to be rooted by the
specified node. Calling
surroundContents() with the node FOO in
the following examples yields:

One can get a copy of all the character data selected or
partially selected by a range with:

DOMString toString();

This does nothing more than simply concatenate all the character
data selected by the range. This includes character data
in both Text and CDATASection
nodes.

8.12. Range modification under document mutation

As a document is modified, the Ranges within the document
need to be updated. For example, if one boundary-point of a
Range is within a node and that node is removed from
the document, then the Range would be invalid unless
it is fixed up in some way. This section describes how
Ranges are modified under document mutations so that
they remain valid.

There are two general principles which apply to Ranges
under document mutation: The first is that all Ranges
in a document will remain valid after any mutation
operation and the second is that, as much as possible,
all Ranges will select the same portion of the
document after any mutation operation.

Any mutation of the document tree which affect Ranges can
be considered to be a combination of basic delete and
insertion operations. In fact, it can be convenient to
think of those operations as being accomplished using
the deleteContents() and
insertNode() Range methods.

8.12.1. Insertions

An insertion occurs at a single point, the
insertion point, in the document. For any Range in
the document tree, consider each boundary-point. The
only case in which the boundary-point will be changed
after the insertion is when the boundary-point and the
insertion point have the same container and the
offset of the
insertion point is strictly less than the offset of the Range's
boundary-point. In that case the offset of the Range's
boundary-point will be increased so that it is between
the same nodes or characters as it was before the
insertion.

Note that when content is inserted at a boundary-point, it is ambiguous
as
to where the boundary-point should be repositioned if its relative position
is to be
maintained.

Issue:

This is not the same as the principle, given above, of
having the
Range select the same content, although often the Range
ends up selecting the
same content.There are two possibilities: at the start or at the end of the
newly inserted content. We have chosen that in this case neither the
container nor offset of the boundary-point is changed. As a
result, it
will be positioned at the start of the newly inserted content.

Examples:

Suppose the Range selects the following:

<P>Abcd efgh XY blah ijkl</P>

Consider the insertion of the text "inserted text" at the
following positions:

8.12.2. Deletions

Any deletion from the document tree can be considered as a sequence
of
deleteContents() operations applied to a minimal set of
disjoint
Ranges. To specify how a Range is modified under deletions we need
only to
consider what happens to a Range under a single
deleteContents() operation of another Range. And, in
fact, we need
only to consider what happens to a single boundary-point of the Range since
both
boundary-points are modified using the same algorithm.

If a boundary-point is within the content being deleted, then after the
deletion it will be at the same position as the one common to the
boundary-points of
the Range used to delete the contents.

If a boundary-point is after the content being deleted then it is not
affected by the deletion unless its container is also the container of one of the boundary-points of the
range
being deleted. If there is such a common container, then the index of the
boundary-point is
modified so that the boundary-point maintains its position relative to the
content
of the container.

If a boundary-point is before the content being deleted then it is not
affected by the deletion at all.

Examples:

In these examples, the Range on which deleteContents()
is
invoked is indicated by the underline.

Example 1.

Before:

<P>Abcd efgh The Range
ijkl</P>

After:

<P>Abcd Range ijkl</P>

Example 2.

Before:

<p>Abcd efgh The Range ijkl</p>

After:

<p>Abcd ^kl</p>

Example 3.

Before:

<P>ABCD efgh The
<EM>Range</EM> ijkl</P>

After:

<P>ABCD <EM>ange</EM> ijkl</P>

In this example, the container of the start boundary-point after the deletion is the Text node holding the string "ange".

Example 4.

Before:

<P>Abcd efgh The Range ijkl</P>

After:

<P>Abcd he Range ijkl</P>

Example 5.

Before:

<P>Abcd <EM>efgh The Range
ij</EM>kl</P>

After:

<P>Abcd ^kl</P>

8.13. Formal Description of the Range Interface

To summarize, the complete, formal description of the Range
interface is given below:

INDEX_SIZE_ERR: Raised if offset is negative or greater than the number of child units in refNode. Child units are 16-bit units if refNode is a CharacterData, Comment or ProcessingInstruction node. Child units are Nodes in all other cases.

INVALID_STATE_ERR: Raised if detach() has already been invoked on this object.

INDEX_SIZE_ERR: Raised if offset is negative or greater than the number of child units in refNode. Child units are 16-bit units if refNode is a CharacterData, Comment or ProcessingInstruction node. Child units are Nodes in all other cases.

INVALID_STATE_ERR: Raised if detach() has already been invoked on this object.

NO_MODIFICATION_ALLOWED_ERR: Raised if an
ancestor container of either
boundary-point of the range is read-only.

WRONG_DOCUMENT_ERR: Raised if
newParent and the
container of the start of the Range were
not created from the same document.

HIERARCHY_REQUEST_ERR: Raised if the
container of the start of the Range is of
a type that does not allow children of
the type of newParent or if
newParent is an ancestor of the
container
or if node would end up
with a child node of a type not allowed
by the type of node.

INVALID_STATE_ERR: Raised if detach() has already been invoked on this object.

Called to indicate that the range is no
longer in use and that the implementation
may relinquish any resources associated with
this range. Subsequent calls to any methods
or attribute getters on this range will
result in a DOMException being
thrown with an error code of
INVALID_STATE_ERR.

The initial state of the range returned from this
method is such that both of its boundary-points are
positioned at the beginning of the corresponding
Document, before any content. The range returned
can only be used to select content associated with this
Document, or with DocumentFragments and Attrs for which
this Document is the ownerDocument.