StaticRange
and
Range
objects (ranges) represent a sequence
of
content within a node
tree. Each range has a
start and an end which
are boundary points. A boundary point is a tuple consisting
of
a node and an offset. So
in
other words, a range
represents a
piece of content within a node tree between two boundary points.
Ranges are frequently used in editing for selecting and copying content.
Element
:
p
In the node tree
above, a
range can be used to represent
the
sequence
“syndata is awes”. Assuming p is assigned to the p
element, and em to the
em
element,
this would be done as follows:
var range = new Range(),
firstText = p. childNodes[ 1 ],
secondText = em. firstChild
range. setStart( firstText, 9 ) // do not forget the leading space
range. setEnd( secondText, 4 )
// range now stringifies to the aforementioned quote
Attributes such as src
and alt
in the node tree above
cannot
be represented by a range. Ranges are only useful for nodes.
Range
objects, unlike
StaticRange
objects, are affected by mutations to the node tree. Therefore they are also known as live ranges. Such mutations will not
invalidate them and will try to ensure that it still represents the same piece of content.
Necessarily, a live
range might itself be modified as part of the mutation to the node tree when, e.g., part of the content
it
represents is mutated.
See the insert and remove algorithms, the normalize()
method, and the replace
data and split
algorithms for details.
Updating live ranges
in
response to node tree
mutations can be expensive. For every node tree change, all affected Range
objects need to
be
updated. Even if the application
is uninterested in some live ranges, it still has to pay the cost of keeping them
up-to-date
when a mutation occurs.
A StaticRange
object is a lightweight range
that
does not update when the node
tree mutates. It is therefore not subject to the same maintenance cost as live ranges.
A boundary point is a tuple consisting of a node (a node) and an offset (a non-negative integer).
A correct boundary point’s offset will be between 0 and the boundary point’s node’s length, inclusive.
The position of a boundary point (nodeA, offsetA) relative to a boundary point (nodeB, offsetB) is before, equal, or after, as returned by these steps:
Assert: nodeA and nodeB have the same root.
If nodeA is following nodeB, then if the position of (nodeB, offsetB) relative to (nodeA, offsetA) is before, return after, and if it is after, return before.
If nodeA is an ancestor of nodeB:
Return before.
AbstractRange
In all current engines.
[Exposed =Window ]interface {
AbstractRange readonly attribute Node startContainer ;readonly attribute unsigned long startOffset ;readonly attribute Node endContainer ;readonly attribute unsigned long endOffset ;readonly attribute boolean collapsed ; };
Objects implementing the AbstractRange
interface are known as ranges.
A range has two associated boundary points — a start and end.
For convenience, a range’s start node is its start’s node, its start offset is its start’s offset, its end node is its end’s node, and its end offset is its end’s offset.
A range is collapsed if its start node is its end node and its start offset is its end offset.
In all current engines.
In all current engines.
In all current engines.
node = range . startContainer
In all current engines.
In all current engines.
In all current engines.
offset = range . startOffset
In all current engines.
In all current engines.
In all current engines.
node = range . endContainer
In all current engines.
In all current engines.
In all current engines.
offset = range . endOffset
In all current engines.
In all current engines.
In all current engines.
collapsed = range . collapsed
The startContainer
getter steps are to return this’s start
node.
The startOffset
getter steps are to return this’s start
offset.
The endContainer
getter steps are to return this’s end node.
The endOffset
getter steps are to return this’s end offset.
The collapsed
getter steps are to return true if this is collapsed; otherwise
false.
StaticRange
In all current engines.
dictionary {
StaticRangeInit required Node ;
startContainer required unsigned long ;
startOffset required Node ;
endContainer required unsigned long ; }; [
endOffset Exposed =Window ]interface :
StaticRange AbstractRange {constructor (StaticRangeInit ); };
init
In all current engines.
staticRange = new StaticRange(init)
Returns a new range object that does not update when the node tree mutates.
The new StaticRange(init)
constructor steps
are:
If init["startContainer
"]
or init["endContainer
"]
is a DocumentType
or Attr
node, then throw an "InvalidNodeTypeError
"
DOMException
.
Set this’s
start to
(init["startContainer
"],
init["startOffset
"])
and end to
(init["endContainer
"],
init["endOffset
"]).
A StaticRange
is valid if all of the following are true:
Its start offset is between 0 and its start node’s length, inclusive.
Its end offset is between 0 and its end node’s length, inclusive.
Range
In all current engines.
[Exposed =Window ]interface :
Range AbstractRange {constructor ();readonly attribute Node commonAncestorContainer ;undefined setStart (Node ,
node unsigned long );
offset undefined setEnd (Node ,
node unsigned long );
offset undefined setStartBefore (Node );
node undefined setStartAfter (Node );
node undefined setEndBefore (Node );
node undefined setEndAfter (Node );
node undefined collapse (optional boolean =
toStart false );undefined selectNode (Node );
node undefined selectNodeContents (Node );
node const unsigned short = 0;
START_TO_START const unsigned short = 1;
START_TO_END const unsigned short = 2;
END_TO_END const unsigned short = 3;
END_TO_START short compareBoundaryPoints (unsigned short ,
how Range ); [
sourceRange CEReactions ]undefined deleteContents (); [CEReactions ,NewObject ]DocumentFragment extractContents (); [CEReactions ,NewObject ]DocumentFragment cloneContents (); [CEReactions ]undefined insertNode (Node ); [
node CEReactions ]undefined surroundContents (Node ); [
newParent NewObject ]Range cloneRange ();undefined detach ();boolean isPointInRange (Node ,
node unsigned long );
offset short comparePoint (Node ,
node unsigned long );
offset boolean intersectsNode (Node );
node stringifier ; };
Objects implementing the Range
interface are
known
as live ranges.
Algorithms that modify a tree (in particular the insert, remove, replace data, and split algorithms) modify live ranges associated with that tree.
The root of a live range is the root of its start node.
A node node is contained in a live range range if node’s root is range’s root, and (node, 0) is after range’s start, and (node, node’s length) is before range’s end.
A node is partially contained in a live range if it’s an inclusive ancestor of the live range’s start node but not its end node, or vice versa.
Some facts to better understand these definitions:
The content that one would think of as being within the live range consists of all
contained nodes, plus
possibly
some of the contents of the start node and end node if those
are
CharacterData
nodes.
The nodes that are contained in a live range will generally not be contiguous, because the parent of a contained node will not always be contained.
However, the descendants of a contained node are contained, and if two siblings are contained, so are any siblings that lie between them.
The start node and end node of a live range are never contained within it.
The first contained node (if there are any) will always be after the start node, and the last contained node will always be equal to or before the end node’s last descendant.
There exists a partially contained node if and only if the start node and end node are different.
The commonAncestorContainer
attribute value is neither contained nor partially contained.
If the start node is an ancestor of the end node, the common inclusive ancestor will be the start node. Exactly one of its children will be partially contained, and a child will be contained if and only if it precedes the partially contained child. If the end node is an ancestor of the start node, the opposite holds.
If the start node is not an inclusive ancestor of the end node, nor vice versa, the common inclusive ancestor will be distinct from both of them. Exactly two of its children will be partially contained, and a child will be contained if and only if it lies between those two.
In all current engines.
range = new Range()
The new Range()
constructor steps
are
to set this’s
start and end to
(current global object’s associated Document
, 0).
In all current engines.
commonAncestorContainer
The commonAncestorContainer
getter steps are:
To set the start or end of a range to a boundary point (node, offset), run these steps:
InvalidNodeTypeError
"
DOMException
.
IndexSizeError
"
DOMException
.
In all current engines.
The setStart(node, offset)
method
steps are to set the
start of this to boundary point (node, offset).
In all current engines.
The setEnd(node, offset)
method
steps are to set the
end of this
to boundary point
(node, offset).
In all current engines.
The setStartBefore(node)
method steps are:
InvalidNodeTypeError
"
DOMException
.
In all current engines.
The setStartAfter(node)
method steps are:
Let parent be node’s parent.
If parent is null, then throw an "InvalidNodeTypeError
"
DOMException
.
Set the start of this to boundary point (parent, node’s index plus 1).
In all current engines.
The setEndBefore(node)
method steps are:
InvalidNodeTypeError
"
DOMException
.
In all current engines.
The setEndAfter(node)
method steps are:
Let parent be node’s parent.
If parent is null, then throw an "InvalidNodeTypeError
"
DOMException
.
Set the end of this to boundary point (parent, node’s index plus 1).
In all current engines.
The collapse(toStart)
method steps are to, if
toStart is true, set end to start; otherwise set start to end.
To select a node node within a range range, run these steps:
Let parent be node’s parent.
If parent is null, then throw an "InvalidNodeTypeError
"
DOMException
.
Let index be node’s index.
Set range’s start to boundary point (parent, index).
Set range’s end to boundary point (parent, index plus 1).
In all current engines.
The selectNode(node)
method steps are to select
node within this.
In all current engines.
The selectNodeContents(node)
method steps
are:
InvalidNodeTypeError
"
DOMException
.
In all current engines.
The compareBoundaryPoints(how, sourceRange)
method steps are:
If how is not one of
then throw a "NotSupportedError
"
DOMException
.
WrongDocumentError
"
DOMException
.
START_TO_START
:
START_TO_END
:
END_TO_END
:
END_TO_START
:
If the position of this point relative to other point is
In all current engines.
The deleteContents()
method steps are:
If original start node is original end node and it is a CharacterData
node, then replace
data
with node original start node, offset original start offset, count
original
end offset minus original start offset, and data the empty string, and then
return.
If reference node’s parent were null, it would be the root of this, so would be an inclusive ancestor of original end node, and we could not reach this point.
If original start node is a CharacterData
node, then replace
data
with node original start node, offset original start offset, count
original
start node’s length minus original start offset, data the
empty
string.
For each node in nodes to remove, in tree order, remove node.
If original end node is a CharacterData
node, then replace
data
with node original end node, offset 0, count original end offset and data the
empty string.
To extract a live range range, run these steps:
Let fragment be a new DocumentFragment
node whose node
document is range’s start node’s node document.
If range is collapsed, then return fragment.
If original start node is original end node and it is a CharacterData
node, then:
These variable assignments do actually always make sense. For instance, if original start node is not an inclusive ancestor of original end node, original start node is itself partially contained in range, and so are all its ancestors up until a child of common ancestor. common ancestor cannot be original start node, because it has to be an inclusive ancestor of original end node. The other case is similar. Also, notice that the two children will never be equal if both are defined.
If any member of contained children is a doctype, then throw a
"HierarchyRequestError
"
DOMException
.
We do not have to worry about the first or last partially contained node, because a doctype can never be partially contained. It cannot be a boundary point of a range, and it cannot be the ancestor of anything.
If reference node’s parent is null, it would be the root of range, so would be an inclusive ancestor of original end node, and we could not reach this point.
If first partially contained child is a CharacterData
node, then:
In this case, first partially contained child is original start node.
Let subfragment be the result of extracting subrange.
If last partially contained child is a CharacterData
node, then:
In this case, last partially contained child is original end node.
Let subfragment be the result of extracting subrange.
In all current engines.
The extractContents()
method steps are to return the
result of extracting this.
To clone the contents of a live range range, run these steps:
Let fragment be a new DocumentFragment
node whose node
document is range’s start node’s node document.
If range is collapsed, then return fragment.
If original start node is original end node and it is a CharacterData
node, then:
These variable assignments do actually always make sense. For instance, if original start node is not an inclusive ancestor of original end node, original start node is itself partially contained in range, and so are all its ancestors up until a child of common ancestor. common ancestor cannot be original start node, because it has to be an inclusive ancestor of original end node. The other case is similar. Also, notice that the two children will never be equal if both are defined.
If any member of contained children is a doctype, then throw a
"HierarchyRequestError
"
DOMException
.
We do not have to worry about the first or last partially contained node, because a doctype can never be partially contained. It cannot be a boundary point of a range, and it cannot be the ancestor of anything.
If first partially contained child is a CharacterData
node, then:
In this case, first partially contained child is original start node.
Let subfragment be the result of cloning the contents of subrange.
If last partially contained child is a CharacterData
node, then:
In this case, last partially contained child is original end node.
Let subfragment be the result of cloning the contents of subrange.
In all current engines.
The cloneContents()
method steps are to return the
result of cloning the
contents of this.
To insert a node node into a live range range, run these steps:
ProcessingInstruction
or Comment
node, is a Text
node whose parent is
null,
or is node, then throw a "HierarchyRequestError
"
DOMException
.
Text
node,
set referenceNode to that Text
node.
Text
node, set
referenceNode to the result of splitting it with
offset range’s start offset.
DocumentFragment
node, and one otherwise.
If range is collapsed, then set range’s end to (parent, newOffset).
In all current engines.
The insertNode(node)
method steps are to insert
node into this.
In all current engines.
The surroundContents(newParent)
method steps
are:
If a non-Text
node is partially
contained in this, then throw an "InvalidStateError
"
DOMException
.
If newParent is a Document
,
DocumentType
,
or DocumentFragment
node, then throw an "InvalidNodeTypeError
"
DOMException
.
For historical reasons CharacterData
nodes are not checked
here
and end up throwing later on as a side effect.
Let fragment be the result of extracting this.
If newParent has children, then replace all with null within newParent.
Append fragment to newParent.
In all current engines.
The cloneRange()
method steps are to return a new live range with
the
same start and end as this.
The detach()
method steps are to do nothing. Its
functionality (disabling a Range
object) was
removed, but the method itself
is preserved for compatibility.
In all current engines.
comparePoint(node, offset)
In all current engines.
intersectsNode(node)
In all current engines.
The isPointInRange(node, offset)
method steps are:
InvalidNodeTypeError
"
DOMException
.
IndexSizeError
"
DOMException
.
The comparePoint(node, offset)
method
steps are:
WrongDocumentError
"
DOMException
.
InvalidNodeTypeError
"
DOMException
.
IndexSizeError
"
DOMException
.
The intersectsNode(node)
method steps are:
In all current engines.
The stringification behavior must run these steps:
Let s be the empty string.
If this’s start
node is this’s end node and
it is a Text
node, then return the
substring of that Text
node’s data beginning
at this’s start offset and ending
at this’s
end
offset.
If this’s start
node is a Text
node, then append
the substring of that node’s data from this’s start offset until
the end to s.
Append the concatenation of the data of all Text
nodes that are contained in this,
in
tree order,
to
s.
If this’s end
node is a Text
node, then
append the substring of that node’s data from its start until this’s end offset to
s.
Return s.
The createContextualFragment()
,
getClientRects()
,
and getBoundingClientRect()
methods are defined in other specifications. [DOM-Parsing] [CSSOM-VIEW]