A Range identifies a range of content in a Document or
DocumentFragment. It is contiguous in the sense that
it can be characterized as selecting all of the
content between a single pair of end-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
'selected' 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.
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 Level 1. 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 to create and move a Range and methods to use
Ranges to manipulate content.
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 DOM Level 1Introduction.
A Range consists of two end-points corresponding to the start and the end of the Range. An end-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 end-point and of its position. The container and its ancestors are the ancestor containers of the end-point and of its position. The offset within the node is called the offset of the end-point and its position. If the container is an Attribute, Document, Document Fragment, Element or EntityReference node, the offset is within its child nodes list. If the container is a CharacterData, Comment or Processing Instruction node, the offset is within the 16-bit units contained by it.
The end-points of a Range must have a common ancestor container which is either a Document, DocumentFragment or Attr node. That is, the Range must contain content that is entirely within the subtree rooted by a single Document, DocumentFragment or Attr Node. The container of an end-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 end-point of a Range can be a DocumentType, Entity and Notation node.
Viewed in terms of the text representation of a document, the end-points of a Range can only be on token boundaries. That is, the end-point of the text range cannot be in the middle of a start- or end-tag of an element or within 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:
In this diagram, four different Ranges are illustrated. The end-points of each range are labelled with s (the start of the range) and e (the end of the range). For the red Range, 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 an end-point whose container is not a Text 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 red Range, the container is BODY and the offset is 1. The offset of an end-point whose container is a Text node is obtained similarly but using 16-bit unit positions instead. For example, the end-point labelled s of the green Range 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 end-points of purple and blue ranges correspond to the same location in the text representation. An important feature of the Range is that an end-point of a Range can unambiguously represent every position within the document tree.
The containers and offsets of the end-points can be obtained through the following read-only Range attributes:
readonly attribute Node startContainer; readonly attribute long startOffset; readonly attribute Node endContainer; readonly attribute long endOffset;
If the
end-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.)
A node or 16-bit unit is said to be selected by a Range if it is between the two end-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 red Range selects the P node and the purple Range 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 end-point of the
Range. For example, consider the green Range
in the above diagram. H1 is partially
selected by that Range since the start of
the Range is within one of its children.
Many of the examples in this chapter are illustrated using a text representation of a document. The end-points of a range are indicated by displaying the characters (be they markup or data characters) between the two end-points in bold, as in
<FOO>ABC<BAR>DEF</BAR></FOO>
When both end-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 end-point, it will be shown as a bold asterisk ('*') as in
<FOO>A*BC<BAR>DEF</BAR></FOO>
A range is created by calling a method on the RangeFactory interface. The expectation is that this interface can be obtained from the object implementing the Document using binding-specific casting methods.
interface RangeFactory { Range createRange(); }
The initial state of the range returned from this method is such that both of its end-points are positioned at the beginning of the corresponding Document, before any content. In other words, the container of each end-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 DocumentFragments and Attrs for which
that Document is the ownerDocument
. This
Range can then not be be used with other Document
instances.
The DOM WG is considering allowing a Range instance
to be used with any Document. While the rules
associated with common ancestor containers for
a Range's end-points will remain the same, a Range
would not be tied to a specific Document instance.
A Range's position can be specified by setting the
container and offset of each end-point with the
setStart
and setEnd
methods.
void setStart(in Node parent, in long offset) raises(RangeException); void setEnd(in Node parent, in long offset) raises(RangeException);
If one end-point of a Range is set to be positioned somewhere in a Document, DocumentFragment or Attr node other than the one in which the range is currently positioned, the range is collapsed to the new position. This enforces the restriction that both end-points of a Range must be in the same document or fragment.
The start position 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. The case in which the end is set to be at a position before the start is similarly handled.
It is also possible to set a Range's position relative to nodes in the tree:
void setStartBefore(in Node node); raises(RangeException); void setStartAfter(in Node node); raises(RangeException); void setEndBefore(in Node node); raises(RangeException); void setEndAfter(in Node node); raises(RangeException);
The parent of the node becomes the container of the
end-point and the Range is subject to the same
restrictions as given above in the description of
setStart()
and setEnd()
.
A Range can be collapsed to either end-point:
void collapse(in boolean toStart);
Passing TRUE
to 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> After range.selectNode(FOO): <BAR><FOO>A<MOO>B</MOO>C</FOO></BAR>
It is possible to compare two Ranges by comparing their end-points:
int compareEndPoints(CompareHow how, Range sourceRange);
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 end-point of the Range is before, equal to, or after
the
corresponding end-point of sourceRange.
The result of comparing two end-points (or positions) is specified below. An informal but incorrect specification is that an end-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 end-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 end-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 (then 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 container of B in a pre-order traversal 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 end-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.
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 document or
document
fragment that the Range is in. Some examples:
(1) <FOO>AB<MOO>CD</MOO>CD</FOO> --> <FOO>A^CD</FOO>
(2) <FOO>A<MOO>BC</MOO>DE</FOO> --> <FOO>A<MOO>B</MOO>^E</FOO>
(3) <FOO>XY<BAR>ZW</BAR>Q</FOO> --> <FOO>X^<BAR>W</BAR>Q</FOO>
(4) <FOO><BAR1>AB</BAR1><BAR2/><BAR3>CD</BAR3></FOO> --> <FOO><BAR1>A</BAR1>^<BAR3>D</BAR3>
After deleteContents()
, 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 was partially 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).
If the contents of a range should be extracted rather than deleted, the following method may be used:
DocumentFragment extractContents();
The extractContents()
method does what the
deleteContents()
method does and, in addition, places the
deleted
contents in a new DocumentFragment. The following examples illustrate
the
contents of the returned document fragment:
(1) <FOO>AB<MOO>CD</MOO>CD</FOO> --> B<MOO>CD</MOO>
(2) <FOO>A<MOO>BC</MOO>DE</FOO> --> <MOO>C<MOO>D
(3) <FOO>XY<BAR>ZW</BAR>Q</FOO> --> <BAR>Y</BAR>Z
(4) <FOO><BAR1>AB</BAR1><BAR2/><BAR3>CD</BAR3></FOO> --> <BAR1>B</BAR1><BAR2/><BAR3>C</BAR3>
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
original
document (or document fragment) and part of the contents must be moved
to the
new fragment, a clone of the partially
selected node is brought along to the new fragment. Note that
cloning
does not take place for selected
elements;
these nodes are moved to the new fragment.
The contents of a range may be duplicated using the following method:
DocumentFragment cloneContents();
This method returns a document fragment that is similar to the one
returned by the method extractContents()
. However, in this
case,
the original nodes and text content in the range are not deleted from
the
original document. Instead, all of the nodes and text content within the
returned document fragment are cloned.
A node may be inserted into a range using the following method:
void insertNode(in Node n);
The insertNode()
method inserts the specified node into the
document or document fragment in which the range resides. 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.
Note that the same rules that apply to the insertBefore()
method on the Node interface apply here. Specifically, the Node passed
in will
be removed from its existing position in the same document or another
fragment.
The insertion of a single node to subsume the content selected by range can be performed with:
void surroundContents(in Node n);
The surroundContents
member differs from
insertNode()
in that surroundContents
() causes
all of
the content selected by the range to become content of
node
,
whereas insertNode()
splices in existing content at the
given
point in the document.
For example, calling surroundContents()
with the node FOO
yields:
Before: <BAR>AB<MOO>C</MOO>DE</BAR> After surroundContents ( FOO ): <BAR>A<FOO>B<MOO>C</MOO>D</FOO>E</BAR>
Another way of of describing the effect of this member on the document or fragment tree is to decompose it in terms of other operations:
extractContents()
.node
where the range is now collapsed (after
the
extraction) with insertNode()
node
.node
and all of its contents with
selectNode()
.Because inserting a node in such a manner will be a common operation,
surroundContents()
is provided to avoid the overhead of
these four
steps.
The surroundContents()
method raises an exception if the
range partially selects a
non-Text node. An example of a range for which
surroundContents()
raises an exception is:
<FOO>AB<BAR>CD</BAR>E</FOO>
If node
has any children, those children are removed before
its insertion. Also, if node
is part of any existing
content, it
is also removed from that content before insertion.
One can clone a Range:
Range cloneRange();
This creates a new Range which selects exactly the same content of the Range on which it was called. No content is affected by this operation.
Because the end-points of a range do not necessarily have the same containers, use:
readonly attribute Node commonAncestorContainer;
to get the deepest node that is an ancestor container of both end-points.
One can get a copy of all the text nodes selected or partially selected by a range with:
DOMString toString();
This does nothing more than simply concatenate all the characters
selected by the range.
As a document is mutated, the Ranges within the document need to be updated. For example, if one end-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, loosely speaking, all Ranges will select the same portion of the document after any mutation operation, where that is possible.
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.
An insertion occurs at a single point, the insertion point, in the document. For any Range in the document tree, consider each end-point. The only case in which the end-point will be changed after the insertion is when the end-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 end-point. In that case the offset of the Range's end-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 an end-point, it is ambiguous as to where the end-point should be repositioned if its relative position is to be maintained.
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 end-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:
1. Before the 'X': <P>Abcd efgh inserted textXY blah ijkl</P> 2. After the 'X': <P>Abcd efgh Xinserted textY blah ijkl</P> 3. After the 'Y': <P>Abcd efgh XYinserted text blah ijkl</P> 4. After the 'h' in "Y blah": <P>Abcd efgh XY blahinserted text ijkl</P>
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 only under a single
deleteContents()
operation of another Range. And, in
fact, we need
only to consider what happens to a single end-point of the Range since
both
end-points are modified using the same algorithm.
If an end-point is within the content being deleted, then after the deletion it will be at the same position as the one common to the end-points of the Range used to delete the contents.
If an end-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 end-points of the range being deleted. If there is such a common container, then the index of the end-point is modified so that the end-point maintains its position relative to the content of the container.
If an end-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>
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 Rangeij</EM>kl</P>
After:
<P>Abcd ^kl</P>
To summarize, the complete, formal description of the Range
interface is given below:
interface Range { readonly attribute Node startContainer; readonly attribute long startOffset; readonly attribute Node endContainer; readonly attribute long endOffset; readonly attribute boolean isCollapsed; readonly attribute Node commonAncestorContainer; void setStart(in Node node, in long offset) raises(RangeException); void setEnd(in Node node, in long offset) raises(RangeException); void setStartBefore(in Node node) raises(RangeException); void setStartAfter(in Node node) raises(RangeException); void setEndBefore(in Node node) raises(RangeException); void setEndAfter(in Node node) raises(RangeException); void collapse(in boolean toStart); void selectNode(in Node node) raises(RangeException); void selectNodeContents(in Node node) raises(RangeException); typedef enum CompareHow_ { StartToStart, StartToEnd, EndToEnd, EndToStart } CompareHow; short compareEndPoints(in CompareHow how, in Range sourceRange) raises(DOMException); void deleteContents() raises(DOMException); DocumentFragment extractContents() raises(DOMException); DocumentFragment cloneContents() raises(DOMException); void insertNode(in Node node) raises(DOMException, RangeException); void surroundContents(in Node node) raises(DOMException, RangeException); Range cloneRange(); DOMString toString(); };
startContainer
startOffset
endContainer
endOffset
isCollapsed
commonAncestorContainer
Enumerator Values |
StartToStart | |
StartToEnd | |
EndToEnd | |
EndToStart |
setStart
node |
The | |
offset |
The |
RangeException
NULL_NODE_ERR: Raised if node
is null.
INVALID_NODE_TYPE_ERR: Raised ifnode
or an ancestor of node
is anAttr, Entity, Notation, or DocumentType node.
If an offset is out-of-bounds, shouldit just be fixed up or should an exception be raised.
setEnd
node |
The | |
offset |
The |
RangeException
NULL_NODE_ERR: Raised if node
is null.
INVALID_NODE_TYPE_ERR: Raised ifnode
or an ancestor of node
is anAttr, Entity, Notation, or DocumentType node.
setStartBefore
node |
Range starts before |
RangeException
INVALID_NODE_TYPE_ERR: Raised if an ancestorof node
is an Attr, Entity,Notation, or DocumentType node or if node
is a Document,DocumentFragment, Attr, Entity, or Notation node.
setStartAfter
node |
Range starts after |
RangeException
INVALID_NODE_TYPE_ERR: Raised if an ancestorof node
is an Attr, Entity,Notation, or DocumentType node or if node
is a Document,DocumentFragment, Attr, Entity, or Notation node.
setEndBefore
node |
Range ends before |
RangeException
INVALID_NODE_TYPE_ERR: Raised if an ancestorof node
is an Attr, Entity,Notation, or DocumentType node or if node
is a Document,DocumentFragment, Attr, Entity, or Notation node.
setEndAfter
node |
Range ends after |
RangeException
INVALID_NODE_TYPE_ERR: Raised if an ancestorof node
is an Attr, Entity,Notation or DocumentType node or if node
is a Document,DocumentFragment, Attr, Entity, or Notation node.
collapse
toStart |
If TRUE, collapses the Range onto its start;if FALSE, collapses it onto its end. |
selectNode
node |
The node to select. |
RangeException
INVALID_NODE_TYPE_ERR: Raised if an ancestorof node
is an Attr, Entity,Notation or DocumentType node or if node
is a Document,DocumentFragment, Attr, Entity, or Notation node.
selectNodeContents
node |
Node to select from |
RangeException
INVALID_NODE_TYPE_ERR: Raised ifnode
or an ancestor of node
is anAttr, Entity, Notation or DocumentType node.
compareEndPoints
how | ||
sourceRange |
sourceRange
. DOMException
WRONG_DOCUMENT_ERR: Raised if the two Rangesare not in the same document or document fragment.
deleteContents
DOMException
NO_MODIFICATION_ALLOWED_ERR: Raised if anyportion of the content of the range is read-only or anyof the nodes that contain any of the content of the range areread-only.
extractContents
DOMException
NO_MODIFICATION_ALLOWED_ERR: Raised if anyportion of the content of the range is read-only or anyof the nodes which contain any of the content of the range are read-only.
HIERARCHY_REQUEST_ERR: Raised if aDocumentType node would be extracted into the newDocumentFragment.
cloneContents
DOMException
HIERARCHY_REQUEST_ERR: Raised if aDocumentType node would be extracted into the newDocumentFragment.
insertNode
node |
The node to insert at the start of therange |
DOMException
NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of the start of the range is read-only.
WRONG_DOCUMENT_ERR: Raised ifnode
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 ofthe type of node
or if node
is an ancestor of thecontainer.
RangeException
INVALID_NODE_TYPE_ERR: Raised ifnode
is an Attr, Entity, Notation,DocumentFragment, or Document node.
surroundContents
node |
The node to surround the contents with. |
DOMException
NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either end-point of the range is read-only.
WRONG_DOCUMENT_ERR: Raised ifnode
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 ofthe type of node
or if node
is an ancestor of thecontaineror if node
would end up with a child node of a type not allowedby the type of node
.
RangeException
BAD_ENDPOINTS_ERR: Raised if the range partially selects a non-text node.
INVALID_NODE_TYPE_ERR: Raised ifnode
is an Attr, Entity, DocumentType, Notation,Document, or DocumentFragment node.
cloneRange
toString
The Range object needs additional exception codes to thosein DOM Level 1. These codes will need to be consolidated withother exception codes added to DOM Level 2.
exception RangeException { unsigned short code; }; // RangeExceptionCode const unsigned short BAD_ENDPOINTS_ERR = 201; const unsigned short INVALID_NODE_TYPE_ERR = 202; const unsigned short NULL_NODE_ERR = 203;
An integer indicating the type of error generated.
BAD_ENDPOINTS_ERR |
If the end-points of a range do not meet specific requirements. |
INVALID_NODE_TYPE_ERR |
If the container of an end-point of a range is being set to either a node ofan invalid type or a node with an ancestor of an invalid type. |
NULL_NODE_ERR |
If the container of an end-point of a range is being set to null. |