Movatterモバイル変換


[0]ホーム

URL:


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 theHTML 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 is work in progress.

Background

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 aboundary 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.

Seebug 15470. IE9, Firefox 12.0a1, Chrome 17 dev, and Opera Next 12.00 alpha all make the range initially null.

Definition

Everydocument with a [=Document/browsing context=] has a uniqueselection 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. SeeMozilla bug,WebKit bug.

This oneselection must be shared by all the content of thedocument (though not by nesteddocuments), including any [=editing hosts=] in thedocument.

Eachselection can be associated with a singlerange. When there is norange associated with theselection, the selection isempty. The selection must be initiallyempty.

Adocument'sselection is a singleton object associated with thatdocument, so it gets replaced with a new object whenDocument.open() is called. Seebug 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 aselection is associated with a givenrange, it must continue to be associated with that samerange 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()}}, the selection must be associated with a new range object, as required elsewhere in this specification.

If theselection'srange is not null and is [=range/collapsed=], then the caret position must be at thatrange'sboundary point. When theselection is not [=range/collapsed=], this specification does not define the caret position; user agents should follow platform conventions in deciding whether the caret is at the start of theselection, the end of theselection, or somewhere else.

Eachselection has adirection:forwards,backwards, ordirectionless. If the user creates aselection by indicating first oneboundary point of therange and then the other (such as by clicking on one point and dragging to another), and the first indicatedboundary point is [=boundary point/after=] the second, then the correspondingselection must initially bebackwards. If the first indicatedboundary point is [=boundary point/before=] the second, then the correspondingselection must initially beforwards. Otherwise, it must bedirectionless.

When theselection'srange is mutated by scripts, e.g. via {{Range/selectNode(node)}},direction of theselection must be preserved.

Eachselections also have ananchor and afocus. If theselection'srange is null, itsanchor andfocus are both null. If theselection'srange is not null and itsdirection isforwards, itsanchor is therange's [=range/start=], and itsfocus is the [=range/end=]. Otherwise, itsfocus is the [=range/start=] and itsanchor is the [=range/end=].

anchor andfocus ofselection need not to be in the [=document tree=]. It could be in a [=shadow tree=] of the same [=document=].

Eachdocument,input element, andtextarea element has a booleanhas scheduled selectionchange event, which is initially false.

Selection interface

Selection interface provides a way to interact with theselection associated with each document.

        [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 theanchor [=boundary point/node=] of [=this=], or `null` if theanchor is null oranchor is not in the [=document tree=].

anchorOffset

The attribute must return theanchor [=boundary point/offset=] of [=this=], or0 if theanchor is null oranchor is not in the [=document tree=].

focusNode

The attribute must return thefocus [=boundary point/node=] of [=this=], or `null` if thefocus is null orfocus is not in the [=document tree=].

focusOffset

The attribute must return thefocus [=boundary point/offset=] of [=this=], or0 if thefocus is null orfocus is not in the [=document tree=].

isCollapsed

The attribute must return true if and only if theanchor andfocus are the same (including if both are null). Otherwise it must return false.

rangeCount

The attribute must return0 if [=this=] isempty or eitherfocus oranchor is not in the [=document tree=], and must return1 otherwise.

type

The attribute must return `"None"` if [=this=] isempty or eitherfocus oranchor is not in the [=document tree=], `"Caret"` if [=this=]'srange is [=range/collapsed=], and `"Range"` otherwise.

direction

The attribute must return `"none"` if [=this=] isempty or this selection isdirectionless. `"forward"` if this selection's direction isforwards and `"backward"` if this selection's direction isbackwards.

getRangeAt() method

The method must throw an {{IndexSizeError}} exception ifindex is not0, or if [=this=] isempty or eitherfocus oranchor is not in the [=document tree=]. Otherwise, it must return a reference to (not a copy of) [=this=]'srange.

Thus subsequent calls of this method returns the samerange object if nothing has removed [=this=]'s range in the meantime. In particular,getSelection().getRangeAt(0) === getSelection().getRangeAt(0) evaluates totrue if theselection is notempty.

addRange() method

The method must follow these steps:

  1. If the [=tree/root=] of therange's boundary points are not thedocument associated with [=this=], abort these steps.
  2. IfrangeCount is not0, abort these steps.
  3. Set [=this=]'s range torange by a strong reference (not by making a copy).

Sincerange is added by reference, subsequent calls togetRangeAt(0) returns the same object, and any changes that a script makes torange after it is added must be reflected in theselection, until something else removes or replaces [=this=]'srange. In particular, theselection will containb as opposed toa after running the following code:var r = document.createRange(); r.selectNode(a); getSelection().addRange(r); r.selectNode(b);

At Step 2, Chrome 58 and Edge 25 do nothing. Firefox 51 gives you a multi-range selection. At least they keep the exisitingrange.

At Step 3, Chrome 58 and Firefox 51 store a reference, as described here. Edge 25 stores a copy. Firefox 51 changes its selection if therange is modified.

removeRange() method

The method must make [=this=]empty by disassociating itsrange if [=this=]'srange isrange. Otherwise, it must throw a {{NotFoundError}}.

removeAllRanges() method

The method must make [=this=]empty by disassociating itsrange if [=this=] has an associatedrange.

empty() method

The method must be an alias, and behave identically, toremoveAllRanges().

getComposedRanges() method
  1. If [=this=] isempty, return an empty array.
  2. Otherwise, letstartNode be [=range/start node=] of the [=range=] associated with [=this=], and letstartOffset be [=range/start offset=] of the [=range=].
  3. WhilestartNode is a [=node=],startNode's [=tree/root=] is a [=shadow root=], andstartNode's [=tree/root=] is not a [=shadow-including inclusive ancestor=] of any ofoptions["{{GetComposedRangesOptions/shadowRoots}}"], repeat these steps:
    1. SetstartOffset to [=tree/index=] ofstartNode's [=tree/root=]'s [=host=].
    2. SetstartNode tostartNode's [=tree/root=]'s [=host=]'s [=tree/parent=].
  4. LetendNode be [=range/end node=] of the [=range=] associated with [=this=], and letendOffset be [=range/end offset=] of the [=range=].
  5. WhileendNode is a [=node=],endNode's [=tree/root=] is a [=shadow root=], andendNode's [=tree/root=] is not a [=shadow-including inclusive ancestor=] of any ofoptions["{{GetComposedRangesOptions/shadowRoots}}"], repeat these steps:
    1. SetendOffset to [=tree/index=] ofendNode's [=tree/root=]'s [=host=] plus 1.
    2. SetendNode toendNode's [=tree/root=]'s [=host=]'s [=tree/parent=].
  6. Return an array consisting of new {{StaticRange}} whose [=range/start node=] isstartNode, [=range/start offset=] isstartOffset, [=range/end node=] isendNode, and [=range/end offset=] isendOffset.
collapse() method

The method must follow these steps:

  1. Ifnode is null, this method must behave identically asremoveAllRanges() and abort these steps.
  2. Ifnode is a {{DocumentType}}, throw an {{InvalidNodeTypeError}} exception and abort these steps.
  3. The method must throw an {{IndexSizeError}} exception if offset is longer thannode's [=Node/length=] and abort these steps.
  4. Ifdocument associated with [=this=] is not a [=shadow-including inclusive ancestor=] ofnode, abort these steps.
  5. Otherwise, letnewRange be a newrange.
  6. [=Range/Set the start=] the [=range/start=] and the [=range/end=] ofnewRange to (node,offset).
  7. Set [=this=]'srange tonewRange.
setPosition() method

The method must be an alias, and behave identically, tocollapse().

collapseToStart() method

The method must throw {{InvalidStateError}} exception if the [=this=] isempty. Otherwise, it must create a newrange, [=Range/set the start=] both its [=range/start=] and [=range/end=] to the [=range/start=] of [=this=]'srange, and then set [=this=]'srange to the newly-createdrange.

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=] isempty. Otherwise, it must create a newrange, [=Range/set the start=] both its [=range/start=] and [=range/end=] to the [=range/end=] of [=this=]'srange, and then set [=this=]'srange to the newly-createdrange.

extend() method

The method must follow these steps:

  1. If thedocument associated with [=this=] is not a [=shadow-including inclusive ancestor=] ofnode, abort these steps.
  2. If [=this=] isempty, throw an {{InvalidStateError}} exception and abort these steps.
  3. LetoldAnchor andoldFocus be the [=this=]'sanchor andfocus, and letnewFocus be theboundary point (node,offset).
  4. LetnewRange be a newrange.
  5. Ifnode's [=tree/root=] is not the same as the [=this=]'srange's [=tree/root=], [=Range/set the start=]newRange's [=range/start=] and [=range/end=] tonewFocus.
  6. Otherwise, ifoldAnchor is [=boundary point/before=] or equal tonewFocus, [=Range/set the start=]newRange's [=range/start=] tooldAnchor, then set its [=range/end=] tonewFocus.
  7. Otherwise, [=Range/set the start=]newRange's [=range/start=] tonewFocus, then set its [=range/end=] tooldAnchor.
  8. Set [=this=]'srange tonewRange.
  9. IfnewFocus is [=boundary point/before=]oldAnchor, set [=this=]'sdirection tobackwards. Otherwise, set it toforwards.

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:

  1. IfanchorOffset is longer thananchorNode's [=Node/length=] or iffocusOffset is longer thanfocusNode's [=Node/length=], throw an {{IndexSizeError}} exception and abort these steps.
  2. Ifdocument associated with [=this=] is not a [=shadow-including inclusive ancestor=] ofanchorNode orfocusNode, abort these steps.
  3. Letanchor be theboundary point (anchorNode,anchorOffset) and letfocus be theboundary point (focusNode,focusOffset).
  4. LetnewRange be a newrange.
  5. Ifanchor is [=boundary point/before=]focus, [=Range/set the start=] thenewRange's [=range/start=] toanchor and its [=range/end=] to focus. Otherwise, [=Range/set the start=] them tofocus andanchor respectively.
  6. Set [=this=]'srange tonewRange.
  7. Iffocus is [=boundary point/before=]anchor, set [=this=]'sdirection tobackwards. Otherwise, set it toforwards
selectAllChildren() method

The method must follow these steps:

  1. Ifnode is a {{DocumentType}}, throw an {{InvalidNodeTypeError}} exception and abort these steps.
  2. Ifnode's [=tree/root=] is not thedocument associated with [=this=], abort these steps.
  3. LetnewRange be a newrange andchildCount be the number of [=tree/children=] ofnode.
  4. SetnewRange's [=range/start=] to (node,0).
  5. SetnewRange's [=range/end=] to (node,childCount).
  6. Set [=this=]'srange tonewRange.
  7. Set [=this=]'sdirection toforwards.

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:

  1. Ifalter is notASCII case-insensitive match with "extend" or "move", abort these steps.
  2. Ifdirection is notASCII case-insensitive match with "forward", "backward", "left", or "right", abort these steps.
  3. Ifgranularity is notASCII case-insensitive match with "character", "word", "sentence", "line", "paragraph", "lineboundary", "sentenceboundary", "paragraphboundary", "documentboundary", abort these steps.
  4. Ifthisselection is empty, abort these steps.
  5. LeteffectiveDirection bebackwards.
  6. Ifdirection isASCII case-insensitive match with "forward", seteffectiveDirection toforwards.
  7. Ifdirection isASCII case-insensitive match with "right" and [=inline base direction=] ofthisselection'sfocus isltr, seteffectiveDirection toforwards.
  8. Ifdirection isASCII case-insensitive match with "left" and [=inline base direction=] ofthisselection'sfocus isrtl, seteffectiveDirection toforwards.
  9. Setthisselection'sdirection toeffectiveDirection.
  10. Ifalter isASCII case-insensitive match with "extend", setthisselection'sfocus to the location as if the user had requested to extend selection by granularity.
  11. Otherwise, setthisselection'sfocus andanchor to the location as if the user had requested to move selection bygranularity.

We need to more precisely define what it means to extend or move selection by each granularity.

deleteFromDocument() method

The method must invoke {{Range/deleteContents()}} on [=this=]'srange if [=this=] is notempty and bothfocus andanchor are in the [=document tree=]. Otherwise the method must do nothing.

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 returnfalse if [=this=] isempty or ifnode's [=tree/root=] is not the document associated with [=this=].

Otherwise, ifallowPartialContainment isfalse, the method must returntrue if and only if [=range/start=] of itsrange is [=boundary point/before=] or visually equivalent to the firstboundary point in thenodeand [=range/end=] of itsrange is [=boundary point/after=] or visually equivalent to the lastboundary point in thenode.

IfallowPartialContainment istrue, the method must returntrue if and only if [=range/start=] of itsrange is [=boundary point/before=] or visually equivalent to the lastboundary point in thenodeand [=range/end=] of itsrange is [=boundary point/after=] or visually equivalent to the firstboundary point in thenode.

stringifier

The stringification must return the string, which is the concatenation of the rendered text if there is a [=range=] associated with [=this=].

If the selection is within atextarea orinput 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.

This specification follows non-Gecko engines in restricting selections to at most one range, but the API was still originally designed for selections with arbitrary numbers of ranges. This explains oddities like the coexistence ofremoveRange() andremoveAllRanges(), and agetRangeAt() method that takes an integer argument that must always be zero.

All of the members of the {{Selection}} interface are defined in terms of operations on therange object (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 theSelection interface raising exceptions as well, in addition to any explicitly called out above.

Extensions to Other Interfaces

This specification extends several interfaces to provide entry points to the interfaces defined in this specification.

Extensions toDocument interface

TheDocument interface is defined in [[HTML]].

          partial interface Document {            Selection? getSelection();          };
getSelection() method

The method must return theselection associated with [=this=] if [=this=] has an associated [=Document/browsing context=], and it must return `null` otherwise.

Extensions to `Window` interface

TheWindow interface is defined in [[HTML]].

          partial interface Window {            Selection? getSelection();          };
getSelection() method

The method must invoke and return the result of {{Document/getSelection()}} on [=this=]'s {{Window.document}} attribute.

Extensions to `GlobalEventHandlers` interface

TheGlobalEventHandlers interface is defined in [[HTML]].

          partial interface mixin GlobalEventHandlers {            attribute EventHandler onselectstart;            attribute EventHandler onselectionchange;          };
onselectstart

The attribute must be anevent handler IDL attribute for theselectstart event supported by allHTML elements, {{Document}} objects, and {{Window}} objects.

onselectionchange

The attribute must be anevent handler IDL attribute for theselectionchange event supported by allHTML elements, {{Document}} objects, and {{Window}} objects.

Responding to DOM Mutations

When the user agent is to [=replace data=] or [=CharacterData/substring data=] on {{CharacterData}}, the user agent must update therange associated withselection of the [=Node/node document=] of the {{CharacterData}} as if it's alive range.

When the user agent is to split a {{Text}} [=node=], the user agent must update therange associated withselection of the [=Node/node document=] of the {{Text}} as if it's alive range.

When the user agent is to run steps fornormalize() method, the user agent must update therange associated withselection of the [=Node/node document=] of [=this=] as if it's alive range.

When the user agent is to [=remove=] or [=insert=] a [=node=], the user agent must update therange associated withselection of the [=Node/node document=] of the [=node=] as if it's alive range.

User Interactions

The user agent should allow the user to change theselection associated with the [=navigable/active document=]. If the user makes any modification to aselection, the user agent must create a newrange with suitable [=range/start=] and [=range/end=] of therange and associate theselection with this newrange (not modify the existingrange), and set updateselection'sdirection toforwards if the [=range/start=] is [=boundary point/before=] or equal to the [=range/end=],backwards if if the [=range/end=] is [=boundary point/before=] the [=range/start=], ordirectionless if the [=range/start=] and the [=range/end=] cannot be ordered due to the platform convention.

The user agent must not make aselectionempty if it was not alreadyempty in response to any user actions (e.g. clicking on a non-editable region).

Seebug 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.

selectstart event

When the user agent is about to associate a new rangenewRange to theselection in response to a user initiated action, the user agent mustfire an event namedselectstart, which bubbles and is cancelable, at the [=boundary point/node=] associated with theboundary point ofnewRange's [=range/start=] prior to changing the selection if theselection was previouslyempty or the previously associated range was [=range/collapsed=].

If the event is canceled, the user agent must not change theselection.

The user agent must notfire an event when the user agent sets theselectionempty.

selectionchange event

When theselection is dissociated with itsrange, associated with a newrange, or the associatedrange'sboundary point is mutated either by the user or the content script, the user agent mustschedule a selectionchange event ondocument.

When an [^input^] or [^textarea^] element provide a text selection and its selection changes (in either extent or [=direction=]), the user agent mustschedule a selectionchange event on the element.

Schedulingselectionchange event

Toschedule a selectionchange event on a nodetarget, run these steps:

  1. Iftarget'shas scheduled selectionchange event is true, abort these steps.
  2. Queue a task on theuser interaction task source tofire a selectionchange event ontarget.

Firingselectionchange event

Tofire a selectionchange event on a nodetarget, run these steps:

  1. Settarget'shas scheduled selectionchange event to false.
  2. Iftarget is an element,fire an event namedselectionchange, which bubbles and not cancelable, attarget.
  3. Otherwise, iftarget is a document,fire an event namedselectionchange, which does not bubble and not cancelable, attarget.

This specification defines conformance criteria that apply to a single product: theuser agent that implements the interfaces that it contains.

Security and Privacy considerations

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 withselectstart orselectionchange events when the user opts to modify theselection of a document.

Acknowledgements

Many thanks to


[8]ページ先頭

©2009-2025 Movatter.jp