Copyright © 2026 World Wide Web Consortium . W3C ® liability , trademark and permissive document license rules apply.
This document is a preliminary draft of a specification for the Selection API and selection related functionality. It replaces a couple of old sections of the HTML specification , the selection part of the old DOM Range specification .
This document defines APIs for selection, which allows users and authors to select a portion of a document or specify a point of interest for copy, paste, and other editing operations.
This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C standards and drafts index .
This is work in progress.
This document was published by the Web Editing Working Group as an Editor's Draft.
Publication as an Editor's Draft does not imply endorsement by W3C and its Members.
This is a draft document and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to cite this document as other than a work in progress.
This document was produced by a group operating under the W3C Patent Policy . W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent that the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy .
This document is governed by the 18 August 2025 W3C Process Document .
This section is non-normative.
IE9 and Firefox 6.0a2 allow arbitrary ranges in the selection, which follows what this spec originally said. However, this leads to unpleasant corner cases that authors, implementers, and spec writers all have to deal with, and they don't make any real sense. Chrome 14 dev and Opera 11.11 aggressively normalize selections, like not letting them lie inside empty elements and things like that, but this is also viewed as a bad idea, because it takes flexibility away from authors.
So I changed the spec to a made-up compromise that allows some simplification but doesn't constrain authors much. See discussion . Basically it would throw exceptions in some places to try to stop the selection from containing a range that had a boundary point other than an Element or Text node, or a boundary point that didn't descend from a Document.
But this meant getRangeAt() had to start returning a copy, not a reference. Also, it would be prone to things failing weirdly in corner cases. Perhaps most significantly, all sorts of problems might arise when DOM mutations transpire, like if a boundary point's node is removed from its parent and the mutation rules would place the new boundary point inside a non-Text/Element node. And finally, the previously-specified behavior had the advantage of matching two major implementations, while the new behavior matched no one. So I changed it back.
See bug 15470 . IE9, Firefox 12.0a1, Chrome 17 dev, and Opera Next 12.00 alpha all make the range initially null.
Every document with a browsing context has a unique selection associated with it.
This is a requirement of the HTML spec. IE9 and Opera Next 12.00 alpha seem to follow it, while Firefox 12.0a1 and Chrome 17 dev seem not to. See Mozilla bug , WebKit bug .
This one selection must be shared by all the content of the document (though not by nested documents ), including any editing hosts in the document .
Each
selection
can
be
associated
with
a
single
one
or
more
range
ranges
.
When
there
is
are
no
range
ranges
associated
with
the
selection
,
the
selection
is
empty
.
The
selection
must
be
initially
empty
.
A
document
's
selection
is
a
singleton
object
associated
with
that
document
,
so
it
gets
replaced
with
a
new
object
when
Document.open()
is
called.
See
bug
15470
.
IE9
and
Opera
Next
12.00
alpha
allow
the
user
to
reset
the
range
to
null
after
the
fact
by
clicking
somewhere;
Firefox
12.0a1
and
Chrome
17
dev
do
not.
We
follow
Gecko/WebKit,
because
it
lessens
the
chance
of
getRangeAt(0)
throwing.
Once a selection is associated with a given range , it must continue to be associated with that same range until this specification requires otherwise.
For
instance,
if
the
DOM
changes
in
a
way
that
changes
the
range's
boundary
points,
or
a
script
modifies
the
boundary
points
of
the
range,
the
same
range
object
must
continue
to
be
associated
with
the
selection.
However,
if
the
user
changes
the
selection
or
a
script
calls
addRange
()
,
methods
that
replace
or
remove
associated
ranges
,
the
selection
must
be
set
of
associated
with
a
new
range
object,
ranges
changes
as
required
elsewhere
in
this
specification.
If
the
selection
's
has
exactly
one
associated
range
is
not
null
and
that
range
is
collapsed
,
then
the
caret
position
must
be
at
that
range
's
boundary
point
.
When
the
selection
is
not
collapsed
,
Otherwise,
this
specification
does
not
define
the
caret
position;
user
agents
should
follow
platform
conventions
in
deciding
whether
the
caret
is
at
the
start
of
the
selection
,
the
end
of
the
selection
,
or
somewhere
else.
Each selection has a direction : forwards , backwards , or directionless . If the user creates a selection by indicating first one boundary point of the range and then the other (such as by clicking on one point and dragging to another), and the first indicated boundary point is after the second, then the corresponding selection must initially be backwards . If the first indicated boundary point is before the second, then the corresponding selection must initially be forwards . Otherwise, it must be directionless .
When
the
selection
's
range
is
mutated
by
scripts,
e.g.
via
selectNode
(
node
)
,
direction
of
the
selection
must
be
preserved.
Each
selection
s
also
have
an
anchor
and
a
focus
.
If
the
selection
's
range
is
null,
empty
,
its
anchor
and
focus
are
both
null.
If
the
selection
's
range
is
not
null
and
its
direction
is
forwards
,
Otherwise,
its
anchor
is
the
range
's
start
of
its
first
associated
range
,
and
its
focus
is
the
end
.
Otherwise,
of
its
focus
last
associated
range
,
where
associated
ranges
is
the
are
ordered
by
increasing
start
and
its
anchor
in
document
is
the
end
.
order.
anchor and focus of selection need not to be in the document tree . It could be in a shadow tree of the same document .
Each document , input element, and textarea element has a boolean has scheduled selectionchange event , which is initially false.
Selection interface provides a way to interact with the selection associated with each document.
WebIDL[Exposed=Window]
interface Selection {
readonly attribute Node? anchorNode;
readonly attribute unsigned long anchorOffset;
readonly attribute Node? focusNode;
readonly attribute unsigned long focusOffset;
readonly attribute boolean isCollapsed;
readonly attribute unsigned long rangeCount;
readonly attribute DOMString type;
readonly attribute DOMString direction;
Range getRangeAt(unsigned long index);
undefined addRange(Range range);
undefined removeRange(Range range);
undefined removeAllRanges();
undefined empty();
sequence<StaticRange> getComposedRanges(optional GetComposedRangesOptions options = {});
undefined collapse(Node? node, optional unsigned long offset = 0);
undefined setPosition(Node? node, optional unsigned long offset = 0);
undefined collapseToStart();
undefined collapseToEnd();
undefined extend(Node node, optional unsigned long offset = 0);
undefined setBaseAndExtent(Node anchorNode, unsigned long anchorOffset, Node focusNode, unsigned long focusOffset);
undefined selectAllChildren(Node node);
undefined modify(optional DOMString alter, optional DOMString direction, optional DOMString granularity);
[CEReactions] undefined deleteFromDocument();
boolean containsNode(Node node, optional boolean allowPartialContainment = false);
stringifier;
};
dictionary GetComposedRangesOptions {
sequence<ShadowRoot> shadowRoots = [];
};
anchorNode
The
attribute
must
return
the
anchor
node
of
this
,
or
null
if
the
anchor
is
null
or
anchor
is
not
in
the
document
tree
.
anchorOffset
The
attribute
must
return
the
anchor
offset
of
this
,
or
0
if
the
anchor
is
null
or
anchor
is
not
in
the
document
tree
.
focusNode
The
attribute
must
return
the
focus
node
of
this
,
or
null
if
the
focus
is
null
or
focus
is
not
in
the
document
tree
.
focusOffset
The
attribute
must
return
the
focus
offset
of
this
,
or
0
if
the
focus
is
null
or
focus
is
not
in
the
document
tree
.
isCollapsed
The attribute must return true if and only if the anchor and focus are the same (including if both are null). Otherwise it must return false.
rangeCount
The
attribute
must
return
0
if
this
is
empty
or
either
focus
or
anchor
the
number
of
associated
ranges
is
not
in
the
document
tree
,
and
must
return
1
otherwise.
this
.
type
The
attribute
must
return
"None"
if
this
is
empty
or
either
focus
or
anchor
is
not
in
the
document
tree
,
"Caret"
if
this
's
has
exactly
one
associated
range
and
that
range
is
collapsed
,
and
"Range"
otherwise.
direction
The
attribute
must
return
"none"
if
this
is
empty
or
this
selection
is
directionless
.
"forward"
if
this
selection's
direction
is
forwards
and
"backward"
if
this
selection's
direction
is
backwards
.
getRangeAt()
method
The
method
must
throw
an
IndexSizeError
exception
if
index
is
not
0
,
greater
than
or
if
equal
to
this
is
empty
or
either
focus
or
anchor
is
not
in
the
document
tree
's
rangeCount
.
Otherwise,
it
must
return
a
reference
to
(not
a
copy
of)
this
's
the
associated
range
at
index
in
this
.
Thus
subsequent
calls
of
this
method
returns
the
same
range
object
for
the
same
index
if
nothing
has
removed
this
's
or
replaced
that
associated
range
in
the
meantime.
In
particular,
getSelection().getRangeAt(0)
===
getSelection().getRangeAt(0)
evaluates
to
true
if
the
selection
is
not
empty
has
at
least
one
associated
range
.
addRange()
method
The method must follow these steps:
Since
range
is
added
by
reference,
subsequent
calls
to
getRangeAt(0)
returns
the
same
object,
and
any
changes
that
a
script
makes
to
range
after
it
is
added
must
be
reflected
in
the
selection
,
until
something
else
removes
or
replaces
this
's
that
associated
range
.
In
particular,
the
selection
will
contain
b
as
opposed
to
a
after
running
the
following
code:
var
r
=
document.createRange();
r.selectNode(a);
getSelection().addRange(r);
r.selectNode(b);
removeRange()
method
The
method
must
make
disassociate
range
from
this
empty
by
disassociating
its
range
if
this
's
has
an
associated
range
that
is
range
.
Otherwise,
it
must
throw
a
NotFoundError
.
removeAllRanges()
method
The
method
must
make
this
empty
by
disassociating
its
all
associated
range
ranges
if
from
this
has
an
associated
range
.
empty()
method
The
method
must
be
an
alias,
and
behave
identically,
to
removeAllRanges()
.
getComposedRanges()
method
shadowRoots
"],
repeat
these
steps:
shadowRoots
"],
repeat
these
steps:
StaticRange
whose
start
node
is
startNode
,
start
offset
is
startOffset
,
end
node
is
endNode
,
and
end
offset
is
endOffset
,
to
composedRanges
.
collapse()
method
The method must follow these steps:
removeAllRanges()
and
abort
these
steps.
DocumentType
,
throw
an
InvalidNodeTypeError
exception
and
abort
these
steps.
IndexSizeError
exception
if
offset
is
longer
than
node
's
length
and
abort
these
steps.
setPosition()
method
The
method
must
be
an
alias,
and
behave
identically,
to
collapse()
.
collapseToStart()
method
The
method
must
throw
InvalidStateError
exception
if
the
this
is
empty
.
Otherwise,
it
must
create
a
new
range
,
set
the
start
both
its
start
and
end
to
the
start
of
this
's
first
associated
range
,
and
then
set
this
's
associated
range
ranges
to
contain
only
the
newly-created
range
.
For collapseToStart/End, IE9 mutates the existing range, while Firefox 9.0a2 and Chrome 15 dev replace it with a new one. The spec follows the majority and replaces it with a new one, leaving the old Range object unchanged.
collapseToEnd()
method
The
method
must
throw
InvalidStateError
exception
if
the
this
is
empty
.
Otherwise,
it
must
create
a
new
range
,
set
the
start
both
its
start
and
end
to
the
end
of
this
's
last
associated
range
,
and
then
set
this
's
associated
range
ranges
to
contain
only
the
newly-created
range
.
extend()
method
The method must follow these steps:
InvalidStateError
exception
and
abort
these
steps.
Reverse-engineered circa January 2011. IE doesn't support it, so I'm relying on Firefox (implemented extend() sometime before 2000) and WebKit (implemented extend() in 2007). I'm mostly ignoring Opera, because gsnedders tells me its implementation isn't compatible. Firefox 12.0a1 seems to mutate the existing range. IE9 doesn't support extend(), and it's impossible to tell whether Chrome 17 dev or Opera Next 12.00 alpha mutate or replace, because getRangeAt() returns a copy anyway. Nevertheless, I go against Gecko here, to be consistent with collapse().
setBaseAndExtent()
method
The method must follow these steps:
IndexSizeError
exception
and
abort
these
steps.
selectAllChildren()
method
The method must follow these steps:
DocumentType
,
throw
an
InvalidNodeTypeError
exception
and
abort
these
steps.
0
).
Based mostly on Firefox 9.0a2. It has a bug that I didn't reproduce, namely that if you pass a Document as the argument, the end offset becomes 1 instead of the number of children it has. It also throws a RangeException instead of DOMException, because its implementation predated their merging.
IE9 behaves similarly but with glitches. It throws "Unspecified error." if the node is detached or display:none, and apparently in some random other cases too. It throws "Invalid argument." for detached comments (only!). Finally, if you pass it a comment, it seems to select the whole comment, unlike with text nodes.
Chrome 16 dev behaves as you'd expect given its Selection implementation. It refuses to select anything that's not visible, so it's almost always wrong. Opera 11.50 just does nothing in all my tests, as usual.
The new range replaces any existing one, doesn't mutate it. This matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 12.00 alpha can't be tested, because getRangeAt() returns a copy anyway.)
modify()
method
The method must follow these steps:
extend
(
node
,
offset
)
.
We need to more precisely define what it means to extend or move selection by each granularity.
deleteFromDocument()
method
The
If
this
is
empty
,
the
method
must
do
nothing.
Otherwise,
the
method
must
invoke
deleteContents
()
on
this
's
each
associated
range
if
this
is
not
empty
and
both
focus
and
anchor
are
in
the
descending
start
document
tree
.
Otherwise
the
method
must
do
nothing.
order.
This is the one method that actually mutates the range instead of replacing it. This matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 12.00 alpha can't be tested, because getRangeAt() returns a copy anyway.)
containsNode()
method
The
method
must
return
false
if
this
is
empty
or
if
node
's
root
is
not
the
document
associated
with
this
.
Otherwise,
if
allowPartialContainment
is
false
,
the
method
must
return
true
if
and
only
if
start
of
its
there
exists
an
associated
range
in
this
whose
start
is
before
or
visually
equivalent
to
the
first
boundary
point
in
the
node
and
whose
end
of
its
range
is
after
or
visually
equivalent
to
the
last
boundary
point
in
the
node
.
If
allowPartialContainment
is
true
,
the
method
must
return
true
if
and
only
if
start
of
its
there
exists
an
associated
range
in
this
whose
start
is
before
or
visually
equivalent
to
the
last
boundary
point
in
the
node
and
whose
end
of
its
range
is
after
or
visually
equivalent
to
the
first
boundary
point
in
the
node
.
stringifier
The
stringification
must
return
the
string,
which
is
the
concatenation
of
the
rendered
text
if
there
is
a
of
all
associated
range
ranges
associated
with
in
this
.
,
in
associated-range
order.
If the selection is within a textarea or input element, it must return the selected substring in its value.
See also nsISelection.idl from Gecko. This spec doesn't have everything from there yet, in particular selectionLanguageChange() and containsNode() are missing. They are missing because I couldn't work out how to define them in terms of Ranges.
Originally,
the
Selection
interface
was
a
Netscape
feature.
The
original
implementation
was
carried
on
into
Gecko
(Firefox),
and
the
feature
was
later
implemented
independently
by
other
browser
engines.
The
Netscape
implementation
always
allowed
multiple
ranges
in
a
single
selection,
for
instance
so
the
user
could
select
a
column
of
a
table
However,
multi-range
selections
proved
to
be
an
unpleasant
corner
case
that
web
developers
didn't
know
about
and
even
Gecko
developers
rarely
handled
correctly.
Other
browser
engines
never
implemented
the
feature,
and
clamped
selections
to
a
single
range
in
various
incompatible
fashions.
table.
This
specification
follows
non-Gecko
engines
in
restricting
selections
to
at
most
one
range,
but
the
API
was
still
originally
designed
defines
interoperable
behavior
for
selections
with
arbitrary
numbers
of
ranges.
This
explains
oddities
like
the
coexistence
of
removeRange()
and
removeAllRanges()
,
associated
ranges
,
including
ordering,
merging,
and
a
getRangeAt()
method
that
takes
an
integer
argument
API
semantics
that
must
always
be
zero.
operate
across
multiple
ranges.
All
of
the
members
of
the
Selection
interface
are
defined
in
terms
of
operations
on
the
associated
range
object
objects
(if
any)
represented
by
the
object.
These
operations
can
raise
exceptions,
as
defined
for
the
Range
interface;
this
can
therefore
result
in
the
members
of
the
Selection
interface
raising
exceptions
as
well,
in
addition
to
any
explicitly
called
out
above.
This specification extends several interfaces to provide entry points to the interfaces defined in this specification.
The
Document
interface
is
defined
in
[
HTML
].
WebIDLpartial interface Document {
Selection? getSelection();
};
getSelection()
method
The
method
must
return
the
selection
associated
with
this
if
this
has
an
associated
browsing
context
,
and
it
must
return
null
otherwise.
The
Window
interface
is
defined
in
[
HTML
].
WebIDLpartial interface Window {
Selection? getSelection();
};
getSelection()
method
The
method
must
invoke
and
return
the
result
of
getSelection
()
on
this
's
Window
.
document
attribute.
The
GlobalEventHandlers
interface
is
defined
in
[
HTML
].
WebIDLpartial interface mixin GlobalEventHandlers {
attribute EventHandler onselectstart;
attribute EventHandler onselectionchange;
};
onselectstart
The
attribute
must
be
an
event
handler
IDL
attribute
for
the
selectstart
event
supported
by
all
HTML
elements
,
Document
objects,
and
Window
objects.
onselectionchange
The
attribute
must
be
an
event
handler
IDL
attribute
for
the
selectionchange
event
supported
by
all
HTML
elements
,
Document
objects,
and
Window
objects.
When
the
user
agent
is
to
replace
data
or
substring
data
on
CharacterData
,
the
user
agent
must
update
the
range
associated
with
selection
of
the
node
document
of
the
CharacterData
as
if
it's
a
live
range
.
When
the
user
agent
is
to
split
a
Text
node
,
the
user
agent
must
update
the
range
associated
with
selection
of
the
node
document
of
the
Text
as
if
it's
a
live
range
.
When
the
user
agent
is
to
run
steps
for
normalize()
method,
the
user
agent
must
update
the
range
associated
with
selection
of
the
node
document
of
this
as
if
it's
a
live
range
.
When the user agent is to remove or insert a node , the user agent must update the range associated with selection of the node document of the node as if it's a live range .
The user agent should allow the user to change the selection associated with the active document . If the user makes any modification to a selection , the user agent must create a new range with suitable start and end of the range and associate the selection with this new range (not modify the existing range ), and set update selection 's direction to forwards if the start is before or equal to the end , backwards if if the end is before the start , or directionless if the start and the end cannot be ordered due to the platform convention.
The user agent must not make a selection empty if it was not already empty in response to any user actions (e.g. clicking on a non-editable region).
See bug 15470 . IE9 and Opera Next 12.00 alpha allow the user to reset the range to null after the fact by clicking somewhere; Firefox 12.0a1 and Chrome 17 dev do not. I follow Gecko/WebKit, because it lessens the chance of getRangeAt(0) throwing.
When
the
user
agent
is
about
to
associate
a
new
range
newRange
to
the
selection
in
response
to
a
user
initiated
action,
the
user
agent
must
fire
an
event
named
selectstart
,
which
bubbles
and
is
cancelable,
at
the
node
associated
with
the
boundary
point
of
newRange
's
start
prior
to
changing
the
selection
if
the
selection
was
previously
empty
or
the
previously
associated
range
was
collapsed
.
If the event is canceled, the user agent must not change the selection .
The user agent must not fire an event when the user agent sets the selection empty .
When the selection is dissociated with its range , associated with a new range , or the associated range 's boundary point is mutated either by the user or the content script, the user agent must schedule a selectionchange event on document .
When
an
input
or
textarea
element
provide
a
text
selection
and
its
selection
changes
(in
either
extent
or
direction
),
the
user
agent
must
schedule
a
selectionchange
event
on
the
element.
To schedule a selectionchange event on a node target , run these steps:
To fire a selectionchange event on a node target , run these steps:
selectionchange
,
which
bubbles
and
not
cancelable,
at
target
.
selectionchange
,
which
does
not
bubble
and
not
cancelable,
at
target
.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
This specification defines conformance criteria that apply to a single product: the user agent that implements the interfaces that it contains.
There are no known security considerations for this standard.
To
mitigate
potential
privacy
risks
of
exposing
user's
use
of
assistive
technologies,
for
example,
user
agent
may
elect
to
emulate
mouse
and
keyboard
events
typically
associated
with
selectstart
or
selectionchange
events
when
the
user
opts
to
modify
the
selection
of
a
document.
Many thanks to
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: