├── W3CTRMANIFEST ├── .pr-preview.json ├── tidyconfig.txt ├── w3c.json ├── LICENSE.md ├── CODE_OF_CONDUCT.md ├── README.md ├── .github ├── workflows │ ├── tidy.yml │ └── auto-publish.yml └── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md ├── index.html └── publish ├── FPWD-selection-api-20141007.html └── WD-selection-api-2015-Oct.html /W3CTRMANIFEST: -------------------------------------------------------------------------------- 1 | index.html?specStatus=WD&shortName=selection-api respec 2 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.html", 3 | "type": "respec" 4 | } 5 | -------------------------------------------------------------------------------- /tidyconfig.txt: -------------------------------------------------------------------------------- 1 | char-encoding: utf8 2 | indent: yes 3 | indent-spaces: 2 4 | wrap: 80 5 | tidy-mark: no 6 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [ 3 | "131776" 4 | ], 5 | "contacts": [ 6 | "siusin" 7 | ], 8 | "shortName": "selection-api", 9 | "repo-type": "rec-track" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/copyright-software). 4 | 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | selection-api 2 | ============= 3 | 4 | [Selection API Specification](http://w3c.github.io/selection-api/) defines APIs for selection, 5 | which allows users and authors to select a portion of 6 | a document or specify a point of interest for copy, paste, and other editing operations. 7 | -------------------------------------------------------------------------------- /.github/workflows/tidy.yml: -------------------------------------------------------------------------------- 1 | name: Tidy document 2 | on: 3 | workflow_dispatch: {} 4 | push: 5 | branches: 6 | - gh-pages 7 | 8 | jobs: 9 | tidy: 10 | name: Tidy up 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Install tidy/html5 15 | run: sudo apt-get install -y tidy 16 | - run: tidy -config tidyconfig.txt -o index.html index.html 17 | - uses: peter-evans/create-pull-request@v5 18 | with: 19 | title: "Tidied up document using tidy-html5" 20 | commit-message: "chore: tidy up index.html" 21 | branch: html-tidy 22 | -------------------------------------------------------------------------------- /.github/workflows/auto-publish.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - gh-pages 7 | pull_request: {} 8 | 9 | jobs: 10 | validate-and-publish: 11 | name: Validate and Publish 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: w3c/spec-prod@v2 16 | with: 17 | TOOLCHAIN: respec 18 | W3C_ECHIDNA_TOKEN: ${{ secrets.ECHIDNA_TOKEN }} 19 | W3C_WG_DECISION_URL: "https://lists.w3.org/Archives/Public/public-webapps/2014JulSep/0627.html" 20 | W3C_NOTIFICATIONS_CC: "${{ secrets.CC }}" 21 | W3C_BUILD_OVERRIDE: | 22 | specStatus: WD 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | For normative changes, the following tasks have been completed: 4 | * [ ] Editing WG resolution on the proposed changes, with at least two implementers participating and not objecting: 5 | * [ ] WebKit 6 | * [ ] Chromium 7 | * [ ] Gecko 8 | 9 | * [ ] For browsers that are shipping the feature, implementation bugs are filed for the proposed changes (link to bug, or write "Not Implementing"): 10 | * [ ] WebKit (https://bugs.webkit.org/show_bug.cgi?id=) 11 | * [ ] Chromium (https://bugs.chromium.org/p/chromium/issues/detail?id=) 12 | * [ ] Gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributions to this repository are intended to become part of Recommendation-track documents 2 | governed by the [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy/) and 3 | [Document License](https://www.w3.org/Consortium/Legal/copyright-documents). To contribute, you must 4 | either participate in the relevant W3C Working Group or make a non-member patent licensing 5 | commitment. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request's body or in subsequent comments. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | 25 | ## Tests 26 | For normative changes, a corresponding 27 | [web-platform-tests](https://github.com/web-platform-tests/wpt) PR should be included. Typically, 28 | both PRs will be merged at the same time. Note that a test change that contradicts the spec should 29 | not be merged before the corresponding spec change. If testing is not practical, please explain why 30 | and if appropriate [file an issue](https://github.com/web-platform-tests/wpt/issues/new) to follow 31 | up later. Add the `type:untestable` or `type:missing-coverage` label as appropriate. 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Selection API 7 | 8 | 10 | 30 | 31 | 32 |
33 |

34 | This document is a preliminary draft of a specification for the 35 | Selection API and selection related functionality. It replaces a couple 36 | of old sections of the HTML 37 | specification, the selection part of the old 39 | DOM Range specification. 40 |

41 |

42 | This document defines APIs for selection, which allows users and 43 | authors to select a portion of a document or specify a point of 44 | interest for copy, paste, and other editing operations. 45 |

46 |
47 |
48 |

49 | This is work in progress. 50 |

51 |
52 |
53 |

54 | Background 55 |

56 |

57 | IE9 and Firefox 6.0a2 allow arbitrary ranges in the selection, which 58 | follows what this spec originally said. However, this leads to 59 | unpleasant corner cases that authors, implementers, and spec writers 60 | all have to deal with, and they don't make any real sense. Chrome 14 61 | dev and Opera 11.11 aggressively normalize selections, like not letting 62 | them lie inside empty elements and things like that, but this is also 63 | viewed as a bad idea, because it takes flexibility away from authors. 64 |

65 |

66 | So I changed the spec to a made-up compromise that allows some 67 | simplification but doesn't constrain authors much. See 69 | discussion. Basically it would throw exceptions in some places to 70 | try to stop the selection from containing a range that had a 71 | boundary point other than an Element or Text node, or a boundary 72 | point that didn't descend from a Document. 73 |

74 |

75 | But this meant getRangeAt() had to start returning a copy, not a 76 | reference. Also, it would be prone to things failing weirdly in corner 77 | cases. Perhaps most significantly, all sorts of problems might arise 78 | when DOM mutations transpire, like if a boundary point's node is 79 | removed from its parent and the mutation rules would place the new 80 | boundary point inside a non-Text/Element node. And finally, the 81 | previously-specified behavior had the advantage of matching two major 82 | implementations, while the new behavior matched no one. So I changed it 83 | back. 84 |

85 |
86 |

87 | See bug 15470. 89 | IE9, Firefox 12.0a1, Chrome 17 dev, and Opera Next 12.00 alpha all 90 | make the range initially null. 91 |

92 |
93 |
94 |
95 |

96 | Definition 97 |

98 |

99 | Every document with a [=Document/browsing context=] has a unique 100 | selection associated with it. 101 |

102 |

103 | This is a requirement of the HTML spec. IE9 and Opera Next 12.00 alpha 104 | seem to follow it, while Firefox 12.0a1 and Chrome 17 dev seem not to. 105 | See Mozilla bug, 107 | WebKit bug. 108 |

109 |

110 | This one selection must be shared by all the content of the 111 | document (though not by nested documents), including any 112 | [=editing hosts=] in the document. 113 |

114 |

115 | Each selection can be associated with a single range. 116 | When there is no range associated with the selection, the 117 | selection is empty. The selection must be initially 118 | empty. 119 |

120 |

121 | A document's selection is a singleton object associated 122 | with that document, so it gets replaced with a new object when 123 | Document.open() is called. See bug 15470. 125 | IE9 and Opera Next 12.00 alpha allow the user to reset the range to 126 | null after the fact by clicking somewhere; Firefox 12.0a1 and Chrome 17 127 | dev do not. We follow Gecko/WebKit, because it lessens the chance of 128 | getRangeAt(0) throwing. 129 |

130 |

131 | Once a selection is associated with a given range, it 132 | must continue to be associated with that same range until this 133 | specification requires otherwise. 134 |

135 |

136 | For instance, if the DOM changes in a way that changes the range's 137 | boundary points, or a script modifies the boundary points of the range, 138 | the same range object must continue to be associated with the 139 | selection. However, if the user changes the selection or a script calls 140 | {{addRange()}}, the selection must be associated with a new range 141 | object, as required elsewhere in this specification. 142 |

143 |

144 | If the selection's range is not null and is 145 | [=range/collapsed=], then the caret position must be at that 146 | range's boundary point. When the selection is not 147 | [=range/collapsed=], this specification does not define the caret 148 | position; user agents should follow platform conventions in deciding 149 | whether the caret is at the start of the selection, the end of 150 | the selection, or somewhere else. 151 |

152 |

153 | Each selection has a direction: forwards, 154 | backwards, or directionless. If the user creates 155 | a selection by indicating first one boundary point of the 156 | range and then the other (such as by clicking on one point and 157 | dragging to another), and the first indicated boundary point is 158 | [=boundary point/after=] the second, then the corresponding 159 | selection must initially be backwards. If the first 160 | indicated boundary point is [=boundary point/before=] the 161 | second, then the corresponding selection must initially be 162 | forwards. Otherwise, it must be directionless. 163 |

164 |

165 | When the selection's range is mutated by scripts, e.g. 166 | via {{Range/selectNode(node)}}, direction of the 167 | selection must be preserved. 168 |

169 |

170 | Each selections also have an anchor and a 171 | focus. If the selection's range is null, its 172 | anchor and focus are both null. If the selection's 173 | range is not null and its direction is forwards, 174 | its anchor is the range's [=range/start=], and its 175 | focus is the [=range/end=]. Otherwise, its focus is the 176 | [=range/start=] and its anchor is the [=range/end=]. 177 |

178 |

179 | anchor and focus of selection need not to be in 180 | the [=document tree=]. It could be in a [=shadow tree=] of the same 181 | [=document=]. 182 |

183 |

184 | Each document, input element, and textarea element 185 | has a boolean has scheduled selectionchange event, which is 186 | initially false. 187 |

188 |
189 |
190 |

191 | Selection interface 192 |

193 |

194 | Selection interface provides a way to interact with the 195 | selection associated with each document. 196 |

197 |
 198 |         [Exposed=Window]
 199 |         interface Selection {
 200 |           readonly attribute Node? anchorNode;
 201 |           readonly attribute unsigned long anchorOffset;
 202 |           readonly attribute Node? focusNode;
 203 |           readonly attribute unsigned long focusOffset;
 204 |           readonly attribute boolean isCollapsed;
 205 |           readonly attribute unsigned long rangeCount;
 206 |           readonly attribute DOMString type;
 207 |           readonly attribute DOMString direction;
 208 |           Range getRangeAt(unsigned long index);
 209 |           undefined addRange(Range range);
 210 |           undefined removeRange(Range range);
 211 |           undefined removeAllRanges();
 212 |           undefined empty();
 213 |           sequence<StaticRange> getComposedRanges(optional GetComposedRangesOptions options = {});
 214 |           undefined collapse(Node? node, optional unsigned long offset = 0);
 215 |           undefined setPosition(Node? node, optional unsigned long offset = 0);
 216 |           undefined collapseToStart();
 217 |           undefined collapseToEnd();
 218 |           undefined extend(Node node, optional unsigned long offset = 0);
 219 |           undefined setBaseAndExtent(Node anchorNode, unsigned long anchorOffset, Node focusNode, unsigned long focusOffset);
 220 |           undefined selectAllChildren(Node node);
 221 |           undefined modify(optional DOMString alter, optional DOMString direction, optional DOMString granularity);
 222 |           [CEReactions] undefined deleteFromDocument();
 223 |           boolean containsNode(Node node, optional boolean allowPartialContainment = false);
 224 |           stringifier;
 225 |         };
 226 | 
 227 |         dictionary GetComposedRangesOptions {
 228 |           sequence<ShadowRoot> shadowRoots = [];
 229 |         };
 230 |       
231 |
232 |
233 | anchorNode 234 |
235 |
236 |

237 | The attribute must return the anchor [=boundary point/node=] 238 | of [=this=], or `null` if the anchor is null or 239 | anchor is not in the [=document tree=]. 240 |

241 |
242 |
243 | anchorOffset 244 |
245 |
246 |

247 | The attribute must return the anchor [=boundary 248 | point/offset=] of [=this=], or 0 if the anchor 249 | is null or anchor is not in the [=document tree=]. 250 |

251 |
252 |
253 | focusNode 254 |
255 |
256 |

257 | The attribute must return the focus [=boundary point/node=] 258 | of [=this=], or `null` if the focus is null or focus 259 | is not in the [=document tree=]. 260 |

261 |
262 |
263 | focusOffset 264 |
265 |
266 |

267 | The attribute must return the focus [=boundary 268 | point/offset=] of [=this=], or 0 if the focus 269 | is null or focus is not in the [=document tree=]. 270 |

271 |
272 |
273 | isCollapsed 274 |
275 |
276 |

277 | The attribute must return true if and only if the 278 | anchor and focus are the same (including 279 | if both are null). Otherwise it must return false. 280 |

281 |
282 |
283 | rangeCount 284 |
285 |
286 |

287 | The attribute must return 0 if [=this=] is 288 | empty or either focus or anchor is not in the 289 | [=document tree=], and must return 1 otherwise. 290 |

291 |
292 |
293 | type 294 |
295 |
296 |

297 | The attribute must return `"None"` if [=this=] is empty or 298 | either focus or anchor is not in the [=document 299 | tree=], `"Caret"` if [=this=]'s range is 300 | [=range/collapsed=], and `"Range"` otherwise. 301 |

302 |
303 |
304 | direction 305 |
306 |
307 |

308 | The attribute must return `"none"` if [=this=] is empty or 309 | this selection is directionless. `"forward"` if this 310 | selection's direction is forwards and `"backward"` if this 311 | selection's direction is backwards. 312 |

313 |
314 |
315 | getRangeAt() method 316 |
317 |
318 |

319 | The method must throw an {{IndexSizeError}} exception if 320 | index is not 0, or if [=this=] is 321 | empty or either focus or anchor is not in the 322 | [=document tree=]. Otherwise, it must return a reference to (not a 323 | copy of) [=this=]'s range. 324 |

325 |

326 | Thus subsequent calls of this method returns the same range 327 | object if nothing has removed [=this=]'s range in the meantime. In 328 | particular, getSelection().getRangeAt(0) === 329 | getSelection().getRangeAt(0) evaluates to true 330 | if the selection is not empty. 331 |

332 |
333 |
334 | addRange() method 335 |
336 |
337 |

338 | The method must follow these steps: 339 |

340 |
    341 |
  1. If the [=tree/root=] of the range's boundary points 342 | are not the document associated with [=this=], abort these 343 | steps. 344 |
  2. 345 |
  3. If rangeCount is not 0, abort these 346 | steps. 347 |
  4. 348 |
  5. Set [=this=]'s range to range by a strong reference 349 | (not by making a copy). 350 |
  6. 351 |
352 |

353 | Since range is added by reference, subsequent calls to 354 | getRangeAt(0) returns the same object, and any changes 355 | that a script makes to range after it is added must be 356 | reflected in the selection, until something else removes or 357 | replaces [=this=]'s range. In particular, the 358 | selection will contain b as opposed to 359 | a after running the following code: var r = 360 | document.createRange(); r.selectNode(a); 361 | getSelection().addRange(r); r.selectNode(b); 362 |

363 |
364 |

365 | At Step 2, Chrome 58 and Edge 25 do nothing. Firefox 51 gives you 366 | a multi-range selection. At least they keep the exisiting 367 | range. 368 |

369 |

370 | At Step 3, Chrome 58 and Firefox 51 store a reference, as 371 | described here. Edge 25 stores a copy. Firefox 51 changes its 372 | selection if the range is modified. 373 |

374 |
375 |
376 |
377 | removeRange() method 378 |
379 |
380 |

381 | The method must make [=this=] empty by disassociating its 382 | range if [=this=]'s range is range. 383 | Otherwise, it must throw a {{NotFoundError}}. 384 |

385 |
386 |
387 | removeAllRanges() method 388 |
389 |
390 |

391 | The method must make [=this=] empty by disassociating its 392 | range if [=this=] has an associated range. 393 |

394 |
395 |
396 | empty() method 397 |
398 |
399 |

400 | The method must be an alias, and behave identically, to 401 | removeAllRanges(). 402 |

403 |
404 |
405 | getComposedRanges() method 406 |
407 |
408 |
    409 |
  1. If [=this=] is empty, return an empty array. 410 |
  2. 411 |
  3. Otherwise, let startNode be [=range/start node=] of 412 | the [=range=] associated with [=this=], and let 413 | startOffset be [=range/start offset=] of the [=range=]. 414 |
  4. 415 |
  5. While startNode is a [=node=], 416 | startNode's [=tree/root=] is a [=shadow root=], and 417 | startNode's [=tree/root=] is not a [=shadow-including 418 | inclusive ancestor=] of any of 419 | options["{{GetComposedRangesOptions/shadowRoots}}"], 420 | repeat these steps: 421 |
      422 |
    1. Set startOffset to [=tree/index=] of 423 | startNode's [=tree/root=]'s [=host=]. 424 |
    2. 425 |
    3. Set startNode to startNode's 426 | [=tree/root=]'s [=host=]'s [=tree/parent=]. 427 |
    4. 428 |
    429 |
  6. 430 |
  7. Let endNode be [=range/end node=] of the [=range=] 431 | associated with [=this=], and let endOffset be 432 | [=range/end offset=] of the [=range=]. 433 |
  8. 434 |
  9. While endNode is a [=node=], endNode's 435 | [=tree/root=] is a [=shadow root=], and endNode's 436 | [=tree/root=] is not a [=shadow-including inclusive ancestor=] of 437 | any of 438 | options["{{GetComposedRangesOptions/shadowRoots}}"], 439 | repeat these steps: 440 |
      441 |
    1. Set endOffset to [=tree/index=] of 442 | endNode's [=tree/root=]'s [=host=] plus 1. 443 |
    2. 444 |
    3. Set endNode to endNode's 445 | [=tree/root=]'s [=host=]'s [=tree/parent=]. 446 |
    4. 447 |
    448 |
  10. 449 |
  11. Return an array consisting of new {{StaticRange}} whose 450 | [=range/start node=] is startNode, [=range/start 451 | offset=] is startOffset, [=range/end node=] is 452 | endNode, and [=range/end offset=] is 453 | endOffset. 454 |
  12. 455 |
456 |
457 |
458 | collapse() method 459 |
460 |
461 |

462 | The method must follow these steps: 463 |

464 |
    465 |
  1. If node is null, this method must behave identically 466 | as removeAllRanges() and abort these steps. 467 |
  2. 468 |
  3. If node is a {{DocumentType}}, throw an 469 | {{InvalidNodeTypeError}} exception and abort these steps. 470 |
  4. 471 |
  5. The method must throw an {{IndexSizeError}} exception if 472 | offset is longer than node's [=Node/length=] and 473 | abort these steps. 474 |
  6. 475 |
  7. If document associated with [=this=] is not a 476 | [=shadow-including inclusive ancestor=] of node, abort 477 | these steps. 478 |
  8. 479 |
  9. Otherwise, let newRange be a new range. 480 |
  10. 481 |
  11. [=Range/Set the start=] the [=range/start=] and the 482 | [=range/end=] of newRange to (node, 483 | offset). 484 |
  12. 485 |
  13. Set [=this=]'s range to newRange. 486 |
  14. 487 |
488 |
489 |
490 | setPosition() method 491 |
492 |
493 |

494 | The method must be an alias, and behave identically, to 495 | collapse(). 496 |

497 |
498 |
499 | collapseToStart() method 500 |
501 |
502 |

503 | The method must throw {{InvalidStateError}} exception if the 504 | [=this=] is empty. Otherwise, it must create a new 505 | range, [=Range/set the start=] both its [=range/start=] and 506 | [=range/end=] to the [=range/start=] of [=this=]'s range, 507 | and then set [=this=]'s range to the newly-created 508 | range. 509 |

510 |

511 | For collapseToStart/End, IE9 mutates the existing range, while 512 | Firefox 9.0a2 and Chrome 15 dev replace it with a new one. The spec 513 | follows the majority and replaces it with a new one, leaving the 514 | old Range object unchanged. 515 |

516 |
517 |
518 | collapseToEnd() method 519 |
520 |
521 |

522 | The method must throw {{InvalidStateError}} exception if the 523 | [=this=] is empty. Otherwise, it must create a new 524 | range, [=Range/set the start=] both its [=range/start=] and 525 | [=range/end=] to the [=range/end=] of [=this=]'s range, and 526 | then set [=this=]'s range to the newly-created range. 527 |

528 |
529 |
530 | extend() method 531 |
532 |
533 |

534 | The method must follow these steps: 535 |

536 |
    537 |
  1. If the document associated with [=this=] is not a 538 | [=shadow-including inclusive ancestor=] of node, abort 539 | these steps. 540 |
  2. 541 |
  3. If [=this=] is empty, throw an {{InvalidStateError}} 542 | exception and abort these steps. 543 |
  4. 544 |
  5. Let oldAnchor and oldFocus be the 545 | [=this=]'s anchor and focus, and let 546 | newFocus be the boundary point (node, 547 | offset). 548 |
  6. 549 |
  7. Let newRange be a new range. 550 |
  8. 551 |
  9. If node's [=tree/root=] is not the same as the 552 | [=this=]'s range's [=tree/root=], [=Range/set the start=] 553 | newRange's [=range/start=] and [=range/end=] to 554 | newFocus. 555 |
  10. 556 |
  11. Otherwise, if oldAnchor is [=boundary point/before=] 557 | or equal to newFocus, [=Range/set the start=] 558 | newRange's [=range/start=] to oldAnchor, then 559 | set its [=range/end=] to newFocus. 560 |
  12. 561 |
  13. Otherwise, [=Range/set the start=] newRange's 562 | [=range/start=] to newFocus, then set its [=range/end=] 563 | to oldAnchor. 564 |
  14. 565 |
  15. Set [=this=]'s range to newRange. 566 |
  16. 567 |
  17. If newFocus is [=boundary point/before=] 568 | oldAnchor, set [=this=]'s direction to 569 | backwards. Otherwise, set it to forwards. 570 |
  18. 571 |
572 |

573 | Reverse-engineered circa January 2011. IE doesn't support it, so 574 | I'm relying on Firefox (implemented extend() sometime before 2000) 575 | and WebKit (implemented extend() in 2007). I'm mostly ignoring 576 | Opera, because gsnedders tells me its implementation isn't 577 | compatible. Firefox 12.0a1 seems to mutate the existing range. IE9 578 | doesn't support extend(), and it's impossible to tell whether 579 | Chrome 17 dev or Opera Next 12.00 alpha mutate or replace, because 580 | getRangeAt() returns a copy anyway. Nevertheless, I go against 581 | Gecko here, to be consistent with collapse(). 582 |

583 |
584 |
585 | setBaseAndExtent() method 586 |
587 |
588 |

589 | The method must follow these steps: 590 |

591 |
    592 |
  1. If anchorOffset is longer than 593 | anchorNode's [=Node/length=] or if 594 | focusOffset is longer than focusNode's 595 | [=Node/length=], throw an {{IndexSizeError}} exception and abort 596 | these steps. 597 |
  2. 598 |
  3. If document associated with [=this=] is not a 599 | [=shadow-including inclusive ancestor=] of anchorNode or 600 | focusNode, abort these steps. 601 |
  4. 602 |
  5. Let anchor be the boundary point 603 | (anchorNode, anchorOffset) and let 604 | focus be the boundary point 605 | (focusNode, focusOffset). 606 |
  6. 607 |
  7. Let newRange be a new range. 608 |
  8. 609 |
  9. If anchor is [=boundary point/before=] 610 | focus, [=Range/set the start=] the newRange's 611 | [=range/start=] to anchor and its [=range/end=] to 612 | focus. Otherwise, [=Range/set the start=] them to 613 | focus and anchor respectively. 614 |
  10. 615 |
  11. Set [=this=]'s range to newRange. 616 |
  12. 617 |
  13. If focus is [=boundary point/before=] 618 | anchor, set [=this=]'s direction to 619 | backwards. Otherwise, set it to forwards 620 |
  14. 621 |
622 |
623 |
624 | selectAllChildren() method 625 |
626 |
627 |

628 | The method must follow these steps: 629 |

630 |
    631 |
  1. If node is a {{DocumentType}}, throw an 632 | {{InvalidNodeTypeError}} exception and abort these steps. 633 |
  2. 634 |
  3. If node's [=tree/root=] is not the document 635 | associated with [=this=], abort these steps. 636 |
  4. 637 |
  5. Let newRange be a new range and 638 | childCount be the number of [=tree/children=] of 639 | node. 640 |
  6. 641 |
  7. Set newRange's [=range/start=] to (node, 642 | 0). 643 |
  8. 644 |
  9. Set newRange's [=range/end=] to (node, 645 | childCount). 646 |
  10. 647 |
  11. Set [=this=]'s range to newRange. 648 |
  12. 649 |
  13. Set [=this=]'s direction to forwards. 650 |
  14. 651 |
652 |
653 |

654 | Based mostly on Firefox 9.0a2. It has a bug that I didn't 655 | reproduce, namely that if you pass a Document as the argument, 656 | the end offset becomes 1 instead of the number of children it 657 | has. It also throws a RangeException instead of DOMException, 658 | because its implementation predated their merging. 659 |

660 |

661 | IE9 behaves similarly but with glitches. It throws "Unspecified 662 | error." if the node is detached or display:none, and apparently 663 | in some random other cases too. It throws "Invalid argument." for 664 | detached comments (only!). Finally, if you pass it a comment, it 665 | seems to select the whole comment, unlike with text nodes. 666 |

667 |

668 | Chrome 16 dev behaves as you'd expect given its Selection 669 | implementation. It refuses to select anything that's not visible, 670 | so it's almost always wrong. Opera 11.50 just does nothing in all 671 | my tests, as usual. 672 |

673 |

674 | The new range replaces any existing one, doesn't mutate it. This 675 | matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 676 | 12.00 alpha can't be tested, because getRangeAt() returns a copy 677 | anyway.) 678 |

679 |
680 |
681 |
682 | modify() method 683 |
684 |
685 |

686 | The method must follow these steps: 687 |

688 |
    689 |
  1. If alter is not ASCII case-insensitive match 690 | with "extend" or "move", abort these steps. 691 |
  2. 692 |
  3. If direction is not ASCII case-insensitive 693 | match with "forward", "backward", "left", or "right", abort these 694 | steps. 695 |
  4. 696 |
  5. If granularity is not ASCII case-insensitive 697 | match with "character", "word", "sentence", "line", "paragraph", 698 | "lineboundary", "sentenceboundary", "paragraphboundary", 699 | "documentboundary", abort these steps. 700 |
  6. 701 |
  7. If this selection is empty, abort these steps. 702 |
  8. 703 |
  9. Let effectiveDirection be backwards. 704 |
  10. 705 |
  11. If direction is ASCII case-insensitive match 706 | with "forward", set effectiveDirection to 707 | forwards. 708 |
  12. 709 |
  13. If direction is ASCII case-insensitive match 710 | with "right" and [=inline base direction=] of this 711 | selection's focus is ltr, set 713 | effectiveDirection to forwards. 714 |
  14. 715 |
  15. If direction is ASCII case-insensitive match 716 | with "left" and [=inline base direction=] of this 717 | selection's focus is rtl, set 719 | effectiveDirection to forwards. 720 |
  16. 721 |
  17. Set this selection's direction to 722 | effectiveDirection. 723 |
  18. 724 |
  19. If alter is ASCII case-insensitive match with 725 | "extend", set this selection's focus to the 726 | location as if the user had requested to extend selection by 727 | granularity. 728 |
  20. 729 |
  21. Otherwise, set this selection's focus and 730 | anchor to the location as if the user had requested to move 731 | selection by granularity. 732 |
  22. 733 |
734 |

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

738 |
739 |
740 | deleteFromDocument() method 741 |
742 |
743 |

744 | The method must invoke {{Range/deleteContents()}} on [=this=]'s 745 | range if [=this=] is not empty and both focus 746 | and anchor are in the [=document tree=]. Otherwise the 747 | method must do nothing. 748 |

749 |

750 | This is the one method that actually mutates the range instead of 751 | replacing it. This matches IE9 and Firefox 12.0a1. (Chrome 17 dev 752 | and Opera Next 12.00 alpha can't be tested, because getRangeAt() 753 | returns a copy anyway.) 754 |

755 |
756 |
757 | containsNode() method 758 |
759 |
760 |

761 | The method must return false if [=this=] is 762 | empty or if node's [=tree/root=] is not the 763 | document associated with [=this=]. 764 |

765 |

766 | Otherwise, if allowPartialContainment is 767 | false, the method must return true if and 768 | only if [=range/start=] of its range is [=boundary 769 | point/before=] or visually equivalent to the first boundary 770 | point in the node and [=range/end=] 771 | of its range is [=boundary point/after=] or visually 772 | equivalent to the last boundary point in the 773 | node. 774 |

775 |

776 | If allowPartialContainment is true, the 777 | method must return true if and only if [=range/start=] 778 | of its range is [=boundary point/before=] or visually 779 | equivalent to the last boundary point in the node 780 | and [=range/end=] of its range is 781 | [=boundary point/after=] or visually equivalent to the first 782 | boundary point in the node. 783 |

784 |
785 |
786 | stringifier 787 |
788 |
789 |

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

794 |

795 | If the selection is within a textarea or input 796 | element, it must return the selected substring in its value. 797 |

798 |
799 |
800 |

801 | See also 803 | nsISelection.idl from Gecko. This spec doesn't have everything from 804 | there yet, in particular selectionLanguageChange() and containsNode() 805 | are missing. They are missing because I couldn't work out how to define 806 | them in terms of Ranges. 807 |

808 |
809 |

810 | Originally, the Selection interface was a Netscape feature. The 811 | original implementation was carried on into Gecko (Firefox), and the 812 | feature was later implemented independently by other browser engines. 813 | The Netscape implementation always allowed multiple ranges in a 814 | single selection, for instance so the user could select a column of a 815 | table However, multi-range selections proved to be an unpleasant 816 | corner case that web developers didn't know about and even Gecko 817 | developers rarely handled correctly. Other browser engines never 818 | implemented the feature, and clamped selections to a single range in 819 | various incompatible fashions. 820 |

821 |

822 | This specification follows non-Gecko engines in restricting 823 | selections to at most one range, but the API was still originally 824 | designed for selections with arbitrary numbers of ranges. This 825 | explains oddities like the coexistence of removeRange() 826 | and removeAllRanges(), and a getRangeAt() 827 | method that takes an integer argument that must always be zero. 828 |

829 |
830 |

831 | All of the members of the {{Selection}} interface are defined in terms 832 | of operations on the range object (if any) 833 | represented by the object. These operations can raise exceptions, as 834 | defined for the {{Range}} interface; this can therefore result in the 835 | members of the Selection interface raising exceptions as well, 836 | in addition to any explicitly called out above. 837 |

838 |
839 |
840 |

841 | Extensions to Other Interfaces 842 |

843 |

844 | This specification extends several interfaces to provide entry points 845 | to the interfaces defined in this specification. 846 |

847 |
848 |

849 | Extensions to Document interface 850 |

851 |

852 | The Document 853 | interface is defined in [[HTML]]. 854 |

855 |
 856 |           partial interface Document {
 857 |             Selection? getSelection();
 858 |           };
 859 |         
860 |
861 |
862 | getSelection() method 863 |
864 |
865 |

866 | The method must return the selection associated with 867 | [=this=] if [=this=] has an associated [=Document/browsing 868 | context=], and it must return `null` otherwise. 869 |

870 |
871 |
872 |
873 |
874 |

875 | Extensions to `Window` interface 876 |

877 |

878 | The Window interface is 880 | defined in [[HTML]]. 881 |

882 |
 883 |           partial interface Window {
 884 |             Selection? getSelection();
 885 |           };
 886 |         
887 |
888 |
889 | getSelection() method 890 |
891 |
892 |

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

897 |
898 |
899 |
900 |
901 |

902 | Extensions to `GlobalEventHandlers` interface 903 |

904 |

905 | The GlobalEventHandlers 907 | interface is defined in [[HTML]]. 908 |

909 |
 910 |           partial interface mixin GlobalEventHandlers {
 911 |             attribute EventHandler onselectstart;
 912 |             attribute EventHandler onselectionchange;
 913 |           };
 914 |         
915 |
916 |
917 | onselectstart 918 |
919 |
920 |

921 | The attribute must be an event handler IDL attribute for 922 | the selectstart event supported by all HTML 923 | elements, {{Document}} objects, and {{Window}} objects. 924 |

925 |
926 |
927 | onselectionchange 928 |
929 |
930 |

931 | The attribute must be an event handler IDL attribute for 932 | the selectionchange event supported by all HTML 933 | elements, {{Document}} objects, and {{Window}} objects. 934 |

935 |
936 |
937 |
938 |
939 |
940 |

941 | Responding to DOM Mutations 942 |

943 |

944 | When the user agent is to [=replace data=] or [=CharacterData/substring 945 | data=] on {{CharacterData}}, the user agent must update the 946 | range associated with selection of the [=Node/node 947 | document=] of the {{CharacterData}} as if it's a live range. 948 |

949 |

950 | When the user agent is to split a {{Text}} [=node=], the user agent 951 | must update the range associated with selection of the 952 | [=Node/node document=] of the {{Text}} as if it's a live range. 953 |

954 |

955 | When the user agent is to run steps for normalize() 956 | method, the user agent must update the range associated with 957 | selection of the [=Node/node document=] of [=this=] as if it's a 958 | live range. 959 |

960 |

961 | When the user agent is to [=remove=] or [=insert=] a [=node=], the user 962 | agent must update the range associated with selection of 963 | the [=Node/node document=] of the [=node=] as if it's a live 964 | range. 965 |

966 |
967 |
968 |

969 | User Interactions 970 |

971 |

972 | The user agent should allow the user to change the selection 973 | associated with the [=navigable/active document=]. If the user makes 974 | any modification to a selection, the user agent must create a 975 | new range with suitable [=range/start=] and [=range/end=] of the 976 | range and associate the selection with this new 977 | range (not modify the existing range), and set update 978 | selection's direction to forwards if the 979 | [=range/start=] is [=boundary point/before=] or equal to the 980 | [=range/end=], backwards if if the [=range/end=] is [=boundary 981 | point/before=] the [=range/start=], or directionless if the 982 | [=range/start=] and the [=range/end=] cannot be ordered due to the 983 | platform convention. 984 |

985 |

986 | The user agent must not make a selection empty if it was 987 | not already empty in response to any user actions (e.g. clicking 988 | on a non-editable region). 989 |

990 |

991 | See bug 992 | 15470. IE9 and Opera Next 12.00 alpha allow the user to reset the 993 | range to null after the fact by clicking somewhere; Firefox 12.0a1 and 994 | Chrome 17 dev do not. I follow Gecko/WebKit, because it lessens the 995 | chance of getRangeAt(0) throwing. 996 |

997 |
998 |

999 | selectstart event 1000 |

1001 |

1002 | When the user agent is about to associate a new range 1003 | newRange to the selection in response to a user 1004 | initiated action, the user agent must fire an event named 1005 | selectstart, which bubbles and is cancelable, at the 1006 | [=boundary point/node=] associated with the boundary point of 1007 | newRange's [=range/start=] prior to changing the selection 1008 | if the selection was previously empty or the previously 1009 | associated range was [=range/collapsed=]. 1010 |

1011 |

1012 | If the event is canceled, the user agent must not change the 1013 | selection. 1014 |

1015 |

1016 | The user agent must not fire an event when the user agent sets 1017 | the selection empty. 1018 |

1019 |
1020 |
1021 |

1022 | selectionchange event 1023 |

1024 |

1025 | When the selection is dissociated with its range, 1026 | associated with a new range, or the associated range's 1027 | boundary point is mutated either by the user or the content 1028 | script, the user agent must schedule a selectionchange event 1029 | on document. 1030 |

1031 |

1032 | When an [^input^] or [^textarea^] element provide a text selection 1033 | and its selection changes (in either extent or [=direction=]), the 1034 | user agent must schedule a selectionchange event on the 1035 | element. 1036 |

1037 |
1038 |

1039 | Scheduling selectionchange event 1040 |

1041 |

1042 | To schedule a selectionchange event on a node 1043 | target, run these steps: 1044 |

1045 |
    1046 |
  1. If target's has scheduled selectionchange 1047 | event is true, abort these steps. 1048 |
  2. 1049 |
  3. 1050 | Queue a task on the user interaction task source to 1051 | fire a selectionchange event on target. 1052 |
  4. 1053 |
1054 |
1055 |
1056 |

1057 | Firing selectionchange event 1058 |

1059 |

1060 | To fire a selectionchange event on a node 1061 | target, run these steps: 1062 |

1063 |
    1064 |
  1. Set target's has scheduled selectionchange 1065 | event to false. 1066 |
  2. 1067 |
  3. If target is an element, fire an event named 1068 | selectionchange, which bubbles and not cancelable, at 1069 | target. 1070 |
  4. 1071 |
  5. Otherwise, if target is a document, fire an 1072 | event named selectionchange, which does not bubble 1073 | and not cancelable, at target. 1074 |
  6. 1075 |
1076 |
1077 |
1078 |
1079 |
1080 |

1081 | This specification defines conformance criteria that apply to a single 1082 | product: the user agent that implements the interfaces that it 1083 | contains. 1084 |

1085 |
1086 |
1087 |

1088 | Security and Privacy considerations 1089 |

1090 |

1091 | There are no known security considerations for this standard. 1092 |

1093 |

1094 | To mitigate potential privacy risks of exposing user's use of assistive 1095 | technologies, for example, user agent may elect to emulate mouse 1096 | and keyboard events typically associated with selectstart or 1097 | selectionchange events when the user opts to modify the 1098 | selection of a document. 1099 |

1100 |
1101 |
1102 |

1103 | Acknowledgements 1104 |

1105 |

1106 | Many thanks to 1107 |

1108 | 1127 |
1128 | 1129 | 1130 | -------------------------------------------------------------------------------- /publish/FPWD-selection-api-20141007.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Selection API 5 | 6 | 7 | 8 | 392 | 495 |

Abstract

496 |

497 | This document is a preliminary draft of a specification for the Selection API and selection related functionality. 498 | It replaces a couple of old sections of the HTML specification, 499 | the selection part of the old DOM Range specification. 500 |

501 |

502 |

503 |

Status of This Document

504 | 505 | 506 | 507 |

508 | This section describes the status of this document at the time of its publication. 509 | Other documents may supersede this document. A list of current W3C publications and the 510 | latest revision of this technical report can be found in the W3C technical reports index at 511 | http://www.w3.org/TR/. 512 |

513 | 514 |

Since this specification is intended to be charted by the Web Applications Working Group, 515 | any feedback or discussion of this specification should be posted to 516 | public-webapps mailing list 517 | with "[Selection API]" in the subject.

518 | 519 |

520 | This specification is based on the HTML Editing API specification 521 | edited by Aryeh Gregor and published by the W3C Editing APIs Community Group. 522 | Copyright © 2011-2014 the Contributors to the HTML Editing APIs Specification, 523 | published by the W3C Editing APIs Community Group under the 524 | W3C Community Contributor License Agreement (CLA). 525 |

526 | 527 |

528 | This document was published by the Web Applications Working Group as a First Public Working Draft. 529 | 530 | This document is intended to become a W3C Recommendation. 531 | 532 | 533 | If you wish to make comments regarding this document, please send them to 534 | public-webapps@w3.org 535 | (subscribe, 536 | archives). 537 | 538 | 539 | 540 | 541 | All comments are welcome. 542 | 543 |

544 | 545 | 546 |

547 | Publication as a First Public Working Draft does not imply endorsement by the W3C 548 | Membership. This is a draft document and may be updated, replaced or obsoleted by other 549 | documents at any time. It is inappropriate to cite this document as other than work in 550 | progress. 551 |

552 | 553 | 554 | 555 |

556 | 557 | This document was produced by a group operating under the 558 | 5 February 2004 W3C Patent 559 | Policy. 560 | 561 | 562 | 563 | 564 | W3C maintains a public list of any patent 565 | disclosures 566 | 567 | made in connection with the deliverables of the group; that page also includes 568 | instructions for disclosing a patent. An individual who has actual knowledge of a patent 569 | which the individual believes contains 570 | Essential 571 | Claim(s) must disclose the information in accordance with 572 | section 573 | 6 of the W3C Patent Policy. 574 | 575 | 576 |

577 | 578 |

This document is governed by the 14 October 2005W3C Process Document. 579 |

580 | 581 | 582 | 583 | 584 | 585 |

Table of Contents

586 | 587 | 588 | 589 |
590 |

1. Background

This section is non-normative.

591 | 592 |

IE9 and Firefox 6.0a2 allow arbitrary ranges in the selection, which follows what this spec originally said. 593 | However, this leads to unpleasant corner cases that authors, implementers, and spec writers all have to deal with, 594 | and they don't make any real sense. Chrome 14 dev and Opera 11.11 aggressively normalize selections, 595 | like not letting them lie inside empty elements and things like that, but this is also viewed as a bad idea, 596 | because it takes flexibility away from authors.

597 | 598 |

So I changed the spec to a made-up compromise that allows some simplification but doesn't constrain authors much. 599 | See discussion. 600 | Basically it would throw exceptions in some places to try to stop the selection from containing a range that had 601 | a boundary point other than an Element or Text node, or a boundary point that didn't descend from a Document.

602 | 603 |

But this meant getRangeAt() had to start returning a copy, not a reference. 604 | Also, it would be prone to things failing weirdly in corner cases. Perhaps most significantly, all sorts of problems 605 | might arise when DOM mutations transpire, like if a boundary point's node is removed from its parent and 606 | the mutation rules would place the new boundary point inside a non-Text/Element node. 607 | And finally, the previously-specified behavior had the advantage of matching two major implementations, 608 | while the new behavior matched no one. So I changed it back.

609 | 610 |
Note
611 |

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

613 |
614 |
615 | 616 |
617 |

2. Definition

618 | 619 |

Every document ([DOM4]) with 620 | a browsing context ([HTML5]) 621 | has a unique Selection object associated with it. 622 | Selection objects are known as selections.

623 | 624 |
Note

This is a requirement of the HTML spec. 625 | IE9 and Opera Next 12.00 alpha seem to follow it, while Firefox 12.0a1 and Chrome 17 dev seem not to. 626 | See Mozilla bug, 627 | WebKit bug.

628 | 629 |

This one selection must be shared by all the content of the document 630 | (though not by nested documents), including any editing hosts in the document. 631 | Editing hosts (defined in [HTML5]) that are not 632 | inside a document cannot have a selection

633 | 634 |

Each selection can be associated with a single range (defined in [DOM4]). 635 | When there is no range associated with the selection, the selection is empty. 636 | The selection must be initially empty.

637 | 638 |
Note

A document's selection is a singleton object associated with that document, 639 | so it gets replaced with a new object when Document.open() is called. 640 | See bug 15470. 641 | IE9 and Opera Next 12.00 alpha allow the user to reset the range to null after the fact by clicking somewhere; 642 | Firefox 12.0a1 and Chrome 17 dev do not. 643 | We follow Gecko/WebKit, because it lessens the chance of getRangeAt(0) throwing.

644 | 645 |

Once a selection is associated with a given range, 646 | it must continue to be associated with that same range until this specification requires otherwise.

647 | 648 |
Note

For instance, if the DOM changes in a way that changes the range's boundary points, 649 | or a script modifies the boundary points of the range, the same range object must continue to be associated with the selection. 650 | However, if the user changes the selection or a script calls addRange(), 651 | the selection must be associated with a new range object, as required elsewhere in this specification.

652 | 653 |

If the selection's range is not null and is 654 | collapsed ([DOM4]), 655 | then the caret position must be at that range's boundary point. 656 | 657 | When the selection is not empty, this specification does not define the caret position; 658 | user agents should follow platform conventions in deciding whether the caret is at the start of the selection, 659 | the end of the selection, or somewhere else. 660 | 661 |

Note

This short-changes Mac users. See bug 13909. 662 | 663 |

Each selection has a direction, forwards, backwards, or directionless. 664 | If the user creates a selection by indicating first one boundary point of the range 665 | and then the other (such as by clicking on one point and dragging to another), 666 | and the first indicated boundary point is after ([DOM4]) the second, 667 | then the corresponding selection must initially be backwards. 668 | If the first indicated boundary point is before ([DOM4]) the second, 669 | then the corresponding selection must initially be forwards. 670 | Otherwise, it must be directionless.

671 | 672 |

Selections also have an anchor and a focus. 673 | If the selection's range is null, its anchor and focus are both null. 674 | If the selection's range is not null and its direction is forwards, its anchor is the range's 675 | start, and its focus is the end. 676 | Otherwise, its focus is the start and its anchor is the end.

677 | 678 |
interface Selection {
 679 |     readonly    attribute Node?         anchorNode;
 680 |     readonly    attribute unsigned long anchorOffset;
 681 |     readonly    attribute Node?         focusNode;
 682 |     readonly    attribute unsigned long focusOffset;
 683 |     readonly    attribute boolean       isCollapsed;
 684 |     readonly    attribute unsigned long rangeCount;
 685 |     Range       getRangeAt (unsigned long index);
 686 |     void        addRange (Range range);
 687 |     void        removeRange (Range range);
 688 |     void        removeAllRanges ();
 689 |     void        collapse (Node node, unsigned long offset);
 690 |     void        collapseToStart ();
 691 |     void        collapseToEnd ();
 692 |     void        extend (Node node, unsigned long offset);
 693 |     void        setBaseAndExtent (Node anchorNode, unsigned long anchorOffset, Node focusNode, unsigned long focusOffset);
 694 |     void        selectAllChildren (Node node);
 695 |     void        deleteFromDocument ();
 696 |     boolean     containsNode (Node node, boolean allowPartialContainment);
 697 |     stringifier DOMString ();
 698 | };

2.1 Attributes

anchorNode of type Node, readonly , nullable
699 |

The attribute must return the anchor node ([DOM4]) 700 | of the context object ([DOM4]), 701 | or null if the anchor is null.

702 |
anchorOffset of type unsigned long, readonly
703 |

The attribute must return the anchor offset ([DOM4]) 704 | of the context object, or 0 if the anchor is null.

705 |
focusNode of type Node, readonly , nullable
706 |

The attribute must return the focus node of the context object, 707 | or null if the anchor is null.

708 |
focusOffset of type unsigned long, readonly
709 |

The attribute must return the focus offset of the context object, 710 | or 0 if the focus is null.

711 |
isCollapsed of type boolean, readonly
712 |

The attribute must return true if and only if the anchor and focus are the same (including if both are null). 713 | Otherwise it must return false.

714 |
rangeCount of type unsigned long, readonly
715 |

The attribute must return 0 if the context object's range is empty, 716 | and must return 1 otherwise.

717 |

2.2 Methods

DOMString
718 | See W3C bug 10583. 719 |
No parameters.
Return type: stringifier
addRange
720 |

The method must set the context object's range to range by a strong reference (not by making a copy) 721 | if the root ([DOM4]) 722 | of the range's boundary points are the document associated with context object. 723 | Otherwise, this method must do nothing.

724 | 725 |
Note

Since range is added by reference, subsequent calls to getRangeAt(0) returns the same object, 726 | and any changes that a script makes to range after it is added must be reflected in the selection, 727 | until something else removes or replaces the context object's range. 728 | In particular, the selection will contain b as opposed to a after running the following code: 729 | var r = document.createRange(); r.selectNode(a); getSelection().addRange(r); r.selectNode(b);

730 | 731 |
Note
732 |

IE9 and Firefox 4.0 store a reference, as described here. 733 | Chrome 12 dev and Opera 11.10 appear to store a copy, so changes don't affect the selection.

734 | 735 |

Chrome 15 dev seems to ignore addRange() if there's already a range. 736 | IE9 replaces the existing range. 737 | Firefox 9.0a2, of course, just gives you a multi-range selection. 738 | IE is likely to behave closest to Firefox, and is also more useful than silent failure, so the spec goes with that.

739 |
740 |
ParameterTypeNullableOptionalDescription
rangeRange
Return type: void
collapse
741 |

The method must follow these steps:

742 |
    743 |
  1. The method must throw an IndexSizeError exception if if offset is negative or longer than node's 744 | length ([DOM4]) and abort these steps.
  2. 745 |
  3. If node's root is not the document associated with the context object, abort these steps.
  4. 746 |
  5. Otherwise, let newRange be a new range.
  6. 747 |
  7. Set ([DOM4]) 748 | the start and the end of newRange to (node, offset).
  8. 749 |
  9. Set the context object's range to newRange.
  10. 750 |
751 |
ParameterTypeNullableOptionalDescription
nodeNode
offsetunsigned long
Return type: void
collapseToEnd
752 |

The method must throw InvalidStateError exception if the context object is empty. 753 | Otherwise, it must create a new range, set both its start and end 754 | to the end of the context object's range, 755 | and then set the context object's range to the newly-created range.

756 |
No parameters.
Return type: void
collapseToStart
757 |

The method must throw InvalidStateError ([DOM4]) exception 758 | if the context object is empty. 759 | Otherwise, it must create a new range, set both its start and end 760 | to the start of the context object's range, 761 | and then set the context object's range to the newly-created range.

762 | 763 |
Note

For collapseToStart/End, IE9 mutates the existing range, while Firefox 9.0a2 and Chrome 15 dev replace it with a new one. 764 | The spec follows the majority and replaces it with a new one, leaving the old Range object unchanged.

765 |
No parameters.
Return type: void
containsNode
766 |

The method must return false if the context object is empty 767 | or if node's root is not the document associated with the context object.

768 | 769 |

Otherwise, if allowPartialContainment is false, 770 | the method must return true if and only if 771 | start of its range is before or visually equivalent to 772 | the first boundary point in the node 773 | and end of its range is after or visually equivalent to 774 | the last boundary point in the node.

775 | 776 |

If allowPartialContainment is true, 777 | the method must return true if and only if 778 | start of its range is before or visually equivalent to 779 | the first boundary point in the node 780 | or end of its range is after or visually equivalent to 781 | the last boundary point in the node.

782 | 783 |
ParameterTypeNullableOptionalDescription
nodeNode
allowPartialContainmentboolean
Return type: boolean
deleteFromDocument
784 |

The method must invoke deleteContents() ([DOM4]) 785 | on the context object's range if the context object is not empty. 786 | Otherwise the method must do nothing.

787 | 788 |
Note

This is the one method that actually mutates the range instead of replacing it. 789 | This matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 12.00 alpha can't be tested, 790 | because getRangeAt() returns a copy anyway.)

791 | 792 |
No parameters.
Return type: void
extend
793 |

The method must follow these steps:

794 |
    795 |
  1. If node's root is not the document associated with the context object, abort these steps.
  2. 796 |
  3. If the context object is empty, throw an InvalidStateError exception and abort these steps.
  4. 797 |
  5. Let oldAnchor and oldFocus be the context object's anchor and focus, 798 | and let newFocus be the boundary point (node, offset).
  6. 799 |
  7. Let newRange be a new range.
  8. 800 |
  9. If node's root is not the same as 801 | the context object's range's root, set newRange's 802 | start and end to newFocus.
  10. 803 |
  11. Otherwise, if oldAnchor is before or equal to newFocus, 804 | set newRange's start to oldAnchor, 805 | then set its end to newFocus.
  12. 806 |
  13. Otherwise, set newRange's start to newFocus, 807 | then set its end to oldAnchor.
  14. 808 |
  15. Set the context object's range to newRange.
  16. 809 |
  17. If newFocus is before oldAnchor, set the context object's direction to backwards. 810 | Otherwise, set it to forwards.
  18. 811 |
812 |
Note

Reverse-engineered circa January 2011. 813 | IE doesn't support it, so I'm relying on Firefox (implemented extend() sometime before 2000) and WebKit (implemented extend() in 2007). 814 | I'm mostly ignoring Opera, because gsnedders tells me its implementation isn't compatible. 815 | 816 | Firefox 12.0a1 seems to mutate the existing range. 817 | IE9 doesn't support extend(), and it's impossible to tell whether Chrome 17 dev or Opera Next 12.00 alpha mutate or replace, 818 | because getRangeAt() returns a copy anyway. 819 | Nevertheless, I go against Gecko here, to be consistent with collapse().

820 |
ParameterTypeNullableOptionalDescription
nodeNode
offsetunsigned long
Return type: void
getRangeAt
821 |

The method must throw an IndexSizeError ([DOM4]) exception 822 | if index is not 0, or if the context object is empty. 823 | Otherwise, it must return a reference to (not a copy of) the context object's range.

824 | 825 |
Note

Thus subsequent calls of this method returns the same range object 826 | if nothing has removed the context object's range in the meantime. 827 | In particular, getSelection().getRangeAt(0) === getSelection().getRangeAt(0) evaluates to true 828 | if the selection is not empty.

829 | 830 |
Note

IE9 and Firefox 4.0 return the same object every time, as the spec says. 831 | Chrome 12 dev and Opera 11.10 return a different object every time.

832 |
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: Range
removeAllRanges
833 |

The method must make the context object empty by disassociating its range 834 | if the context object's range is range. 835 | Otherwise, it must do nothing.

836 |
No parameters.
Return type: void
removeRange
837 |

The method must make the context object empty by disassociating its range 838 | if the context object's range is range. 839 | Otherwise, it must do nothing.

840 |
ParameterTypeNullableOptionalDescription
rangeRange
Return type: void
selectAllChildren
841 |

The method must follow these steps:

842 |
    843 |
  1. If node's root is not the document associated with the context object, abort these steps.
  2. 844 |
  3. Let newRange be a new range and nodeLength be the length of node.
  4. 845 |
  5. Set newRange's start to (node, 0).
  6. 846 |
  7. Set newRange's end to (node, nodeLength).
  8. 847 |
  9. Set the context object's range to newRange.
  10. 848 |
  11. Set the context object's direction to forwards.
  12. 849 |
850 | 851 |
Note
852 |

Based mostly on Firefox 9.0a2. It has a bug that I didn't reproduce, namely 853 | that if you pass a Document as the argument, the end offset becomes 1 instead 854 | of the number of children it has. It also throws a RangeException instead of 855 | DOMException, because its implementation predated their merging. 856 | 857 |

IE9 behaves similarly but with glitches. It throws "Unspecified error." if 858 | the node is detached or display:none, and apparently in some random other cases 859 | too. It throws "Invalid argument." for detached comments (only!). Finally, if 860 | you pass it a comment, it seems to select the whole comment, unlike with text 861 | nodes. 862 | 863 |

Chrome 16 dev behaves as you'd expect given its Selection implementation. 864 | It refuses to select anything that's not visible, so it's almost always wrong. 865 | Opera 11.50 just does nothing in all my tests, as usual. 866 | 867 |

The new range replaces any existing one, doesn't mutate it. 868 | This matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 12.00 869 | alpha can't be tested, because getRangeAt() returns a copy anyway.) 870 |

871 |
ParameterTypeNullableOptionalDescription
nodeNode
Return type: void
setBaseAndExtent
872 |

The method must follow these steps:

873 |
    874 |
  1. If anchorOffset is negative or longer than anchorNode's length 875 | or if focusOffset is negative or longer than focusNode's length, 876 | throw an IndexSizeError exception and abort these steps.
  2. 877 |
  3. If the roots of anchorNode or focusNode are not the document associated with context object, 878 | abort these steps.
  4. 879 |
  5. Let anchor be the boundary point (anchorNode, anchorOffset) 880 | and let focus be the boundary point (focusNode, focusOffset).
  6. 881 |
  7. Let newRange be a new range.
  8. 882 |
  9. If anchor is before focus, 883 | set the newRange's start to anchor 884 | and its end to focus. 885 | Otherwise, set them to focus and anchor respectively.
  10. 886 |
  11. Set the context object's range to newRange.
  12. 887 |
  13. If focus is before anchor, set context object's direction to backwards. 888 | Otherwise, set it to forwards
  14. 889 |
ParameterTypeNullableOptionalDescription
anchorNodeNode
anchorOffsetunsigned long
focusNodeNode
focusOffsetunsigned long
Return type: void
890 | 891 |
Note

See also nsISelection.idl from Gecko. 892 | This spec doesn't have everything from there yet, in particular selectionLanguageChange() and containsNode() are missing. 893 | They are missing because I couldn't work out how to define them in terms of Ranges.

894 | 895 |
Note
896 |

Originally, the Selection interface was a Netscape feature. 897 | The original implementation was carried on into Gecko (Firefox), and the feature was later implemented independently by other browser engines. 898 | The Netscape implementation always allowed multiple ranges in a single selection, for instance so the user could select a column of a table 899 | However, multi-range selections proved to be an unpleasant corner case 900 | that web developers didn't know about and even Gecko developers rarely handled correctly. 901 | Other browser engines never implemented the feature, and clamped selections to a single range in various incompatible fashions.

902 | 903 |

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

907 |
908 | 909 |

All of the members of the Selection interface are defined 910 | in terms of operations on the range object (if any) represented by the object. 911 | These operations can raise exceptions, as defined for the Range ([DOM4]) interface; 912 | this can therefore result in the members of the Selection interface raising exceptions as well, 913 | in addition to any explicitly called out below.

914 |
915 | 916 |
917 |

3. Extensions to Other Interfaces

918 | 919 |

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

921 | 922 |
923 |

3.1 Extensions to Document interface

924 |
partial interface Document {
 925 |     Selection? getSelection ();
 926 | };

3.1.1 Methods

getSelection
927 |

The method must return the selection associated with context object 928 | if the context object has an associated browsing context, 929 | and it must return null otherwise.

930 |
No parameters.
Return type: Selection, nullable
931 |
Note

932 | If we create a Document object with no browsing context 933 | (say via document.implementation.createHTMLDocument("") and call getSelection() on it), 934 | IE9 seems to return a different Selection object. 935 | Firefox 12.0a1 and Opera Next 12.00 alpha return the same object as for the current window. 936 | Chrome 17 dev returns null. 937 | See discussion. 938 | There's no meaningful selection associated with such a document, so we follow WebKit and require returning null.

939 |
940 | 941 |
942 |

3.2 Extensions to Window interface

943 |
partial interface Window {
 944 |     Selection? getSelection ();
 945 | };

3.2.1 Methods

getSelection
946 |

The method must invoke and return the result of getSelection() on the context object's 947 | document ([HTML5]) property.

948 |
No parameters.
Return type: Selection, nullable
949 |
950 | 951 |
952 | 953 |
954 |

4. User Interactions

955 | 956 |

The user agent should allow the user to change the selection associated with the active document 957 | (defined in [HTML5]). 958 | If the user makes any modification to a selection, the user agent must create a new range with suitable 959 | start and 960 | end of the range ([DOM4]) 961 | and associate the selection with this new range (not modify the existing range), 962 | and set update selection's direction to forwards if the start is before or equal to the end, 963 | backwards if if the end is before the start, 964 | or directionless if the start and the end cannot be ordered due to the platform convention.

965 | 966 |

The user agent must not make a selection empty if it was not already empty 967 | in response to any user actions (e.g. clicking on a non-editable region).

968 | 969 |
Note

See bug 15470. 970 | IE9 and Opera Next 12.00 alpha allow the user to reset the range to null after the fact by clicking somewhere; 971 | Firefox 12.0a1 and Chrome 17 dev do not. 972 | I follow Gecko/WebKit, because it lessens the chance of getRangeAt(0) throwing.

973 | 974 |
975 |

4.1 selectstart event

976 | 977 |

When the user agent is about to associate a new range newRange to the selection in response to a user initiated action, 978 | the user agent must fire ([DOM4]) 979 | an event with the name selectstart, which bubbles and is cancelable, 980 | at the node associated with the boundary point of newRange's start 981 | prior to changing the selection.

982 | 983 |

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

984 | 985 |

The user agent must not fire the event when the user agent sets the selection empty.

986 |
987 | 988 |
989 |

4.2 selectionchange event

990 | 991 |

When the selection is changed either by the user or the content script, 992 | the user agent must queue a task ([HTML5]) 993 | to fire an event with the name selectionchange, which does not bubble and is not cancelable, 994 | at the document associated with the selection.

995 |
996 | 997 |
998 | 999 |
1000 |

A. Acknowledgements

1001 |

Many thanks to

1002 |
1012 | 1013 | 1014 | 1015 |

B. References

B.1 Informative references

[DOM4]
Anne van Kesteren; Aryeh Gregor; Ms2ger; Alex Russell; Robin Berjon. W3C DOM4. 10 July 2014. W3C Last Call Working Draft. URL: http://www.w3.org/TR/dom/ 1016 |
[HTML5]
Robin Berjon; Steve Faulkner; Travis Leithead; Erika Doyle Navara; Edward O'Connor; Silvia Pfeiffer. HTML5. 16 September 2014. W3C Proposed Recommendation. URL: http://www.w3.org/TR/html5/ 1017 |
1018 | -------------------------------------------------------------------------------- /publish/WD-selection-api-2015-Oct.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Selection API 5 | 6 | 7 | 8 | 470 | 577 |

Abstract

578 |

579 | This document is a preliminary draft of a specification for the Selection API and selection related functionality. 580 | It replaces a couple of old sections of the HTML specification, 581 | the selection part of the old DOM Range specification. 582 |

583 |

584 | 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. 585 |

586 |

Status of This Document

587 | 588 | 589 | 590 |

591 | This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/. 592 |

593 | 594 | 595 | 596 |

This document was published by the Web Applications Working Group, 597 | any feedback or discussion of this specification should be posted to 598 | public-webapps mailing list 599 | with "[selection-api]" in the subject (archive). 600 |

601 | 602 |

603 | This specification is based on the HTML Editing API specification 604 | edited by Aryeh Gregor and published by the W3C Editing APIs Community Group. 605 | Copyright © 2011-2014 the Contributors to the HTML Editing APIs Specification, 606 | published by the W3C Editing APIs Community Group under the 607 | W3C Community Contributor License Agreement (CLA). 608 |

609 | 610 | 611 |

612 | This document was published by the Web Applications Working Group as a Working Draft. 613 | 614 | This document is intended to become a W3C Recommendation. 615 | 616 | 617 | If you wish to make comments regarding this document, please send them to 618 | public-webapps@w3.org 619 | (subscribe, 620 | archives). 621 | 622 | 623 | 624 | 625 | 626 | 627 | All comments are welcome. 628 | 629 | 630 |

631 | 632 | 633 | 634 |

635 | Publication as a Working Draft does not imply endorsement by the W3C 636 | Membership. This is a draft document and may be updated, replaced or obsoleted by other 637 | documents at any time. It is inappropriate to cite this document as other than work in 638 | progress. 639 |

640 | 641 | 642 | 643 |

644 | 645 | This document was produced by 646 | 647 | a group 648 | operating under the 649 | 5 February 2004 W3C Patent 650 | Policy. 651 | 652 | 653 | 654 | 655 | W3C maintains a public list of any patent 656 | disclosures 657 | 658 | made in connection with the deliverables of 659 | 660 | the group; that page also includes 661 | 662 | instructions for disclosing a patent. An individual who has actual knowledge of a patent 663 | which the individual believes contains 664 | Essential 665 | Claim(s) must disclose the information in accordance with 666 | section 667 | 6 of the W3C Patent Policy. 668 | 669 | 670 |

671 | 672 |

This document is governed by the 1 September 2015 W3C Process Document. 673 |

674 | 675 | 676 | 677 | 678 | 679 | 680 |

Table of Contents

681 | 682 | 683 | 684 |
685 |

1. Background

This section is non-normative.

686 | 687 |

IE9 and Firefox 6.0a2 allow arbitrary ranges in the selection, which follows what this spec originally said. 688 | However, this leads to unpleasant corner cases that authors, implementers, and spec writers all have to deal with, 689 | and they don't make any real sense. Chrome 14 dev and Opera 11.11 aggressively normalize selections, 690 | like not letting them lie inside empty elements and things like that, but this is also viewed as a bad idea, 691 | because it takes flexibility away from authors.

692 | 693 |

So I changed the spec to a made-up compromise that allows some simplification but doesn't constrain authors much. 694 | See discussion. 695 | Basically it would throw exceptions in some places to try to stop the selection from containing a range that had 696 | a boundary point other than an Element or Text node, or a boundary point that didn't descend from a Document.

697 | 698 |

But this meant getRangeAt() had to start returning a copy, not a reference. 699 | Also, it would be prone to things failing weirdly in corner cases. Perhaps most significantly, all sorts of problems 700 | might arise when DOM mutations transpire, like if a boundary point's node is removed from its parent and 701 | the mutation rules would place the new boundary point inside a non-Text/Element node. 702 | And finally, the previously-specified behavior had the advantage of matching two major implementations, 703 | while the new behavior matched no one. So I changed it back.

704 | 705 |
Note
706 |

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

708 |
709 |
710 | 711 |
712 |

2. Definition

713 | 714 |

Every document ([DOM4]) with 715 | a browsing context ([HTML5]) 716 | has a unique Selection object associated with it. 717 | Selection objects are known as selections.

718 | 719 |
Note

This is a requirement of the HTML spec. 720 | IE9 and Opera Next 12.00 alpha seem to follow it, while Firefox 12.0a1 and Chrome 17 dev seem not to. 721 | See Mozilla bug, 722 | WebKit bug.

723 | 724 |

This one selection must be shared by all the content of the document 725 | (though not by nested documents), including any editing hosts in the document. 726 | Editing hosts (defined in [HTML5]) that are not 727 | inside a document cannot have a selection

728 | 729 |

Each selection can be associated with a single range (defined in [DOM4]). 730 | When there is no range associated with the selection, the selection is empty. 731 | The selection must be initially empty.

732 | 733 |
Note

A document's selection is a singleton object associated with that document, 734 | so it gets replaced with a new object when Document.open() is called. 735 | See bug 15470. 736 | IE9 and Opera Next 12.00 alpha allow the user to reset the range to null after the fact by clicking somewhere; 737 | Firefox 12.0a1 and Chrome 17 dev do not. 738 | We follow Gecko/WebKit, because it lessens the chance of getRangeAt(0) throwing.

739 | 740 |

Once a selection is associated with a given range, 741 | it must continue to be associated with that same range until this specification requires otherwise.

742 | 743 |
Note

For instance, if the DOM changes in a way that changes the range's boundary points, 744 | or a script modifies the boundary points of the range, the same range object must continue to be associated with the selection. 745 | However, if the user changes the selection or a script calls addRange(), 746 | the selection must be associated with a new range object, as required elsewhere in this specification.

747 | 748 |

If the selection's range is not null and is 749 | collapsed ([DOM4]), 750 | then the caret position must be at that range's boundary point. 751 | 752 | When the selection is not empty, this specification does not define the caret position; 753 | user agents should follow platform conventions in deciding whether the caret is at the start of the selection, 754 | the end of the selection, or somewhere else. 755 | 756 |

Note

This short-changes Mac users. See bug 13909. 757 | 758 |

Each selection has a direction, forwards, backwards, or directionless. 759 | If the user creates a selection by indicating first one boundary point of the range 760 | and then the other (such as by clicking on one point and dragging to another), 761 | and the first indicated boundary point is after ([DOM4]) the second, 762 | then the corresponding selection must initially be backwards. 763 | If the first indicated boundary point is before ([DOM4]) the second, 764 | then the corresponding selection must initially be forwards. 765 | Otherwise, it must be directionless.

766 | 767 |

Selections also have an anchor and a focus. 768 | If the selection's range is null, its anchor and focus are both null. 769 | If the selection's range is not null and its direction is forwards, its anchor is the range's 770 | start, and its focus is the end. 771 | Otherwise, its focus is the start and its anchor is the end.

772 | 773 |
interface Selection {
 774 |     readonly    attribute Node?         anchorNode;
 775 |     readonly    attribute unsigned long anchorOffset;
 776 |     readonly    attribute Node?         focusNode;
 777 |     readonly    attribute unsigned long focusOffset;
 778 |     readonly    attribute boolean       isCollapsed;
 779 |     readonly    attribute unsigned long rangeCount;
 780 |     readonly    attribute DOMString     type;
 781 |     Range       getRangeAt (unsigned long index);
 782 |     void        addRange (Range range);
 783 |     void        removeRange (Range range);
 784 |     void        removeAllRanges ();
 785 |     void        empty ();
 786 |     void        collapse (Node node, unsigned long offset);
 787 |     void        setPosition (Node node, unsigned long offset);
 788 |     void        collapseToStart ();
 789 |     void        collapseToEnd ();
 790 |     void        extend (Node node, unsigned long offset);
 791 |     void        setBaseAndExtent (Node anchorNode, unsigned long anchorOffset, Node focusNode, unsigned long focusOffset);
 792 |     void        selectAllChildren (Node node);
 793 |     void        deleteFromDocument ();
 794 |     boolean     containsNode (Node node, boolean allowPartialContainment);
 795 |     stringifier DOMString ();
 796 | };

2.1 Attributes

anchorNode of type Node, readonly , nullable
797 |

The attribute must return the anchor node ([DOM4]) 798 | of the context object ([DOM4]), 799 | or null if the anchor is null.

800 |
anchorOffset of type unsigned long, readonly
801 |

The attribute must return the anchor offset ([DOM4]) 802 | of the context object, or 0 if the anchor is null.

803 |
focusNode of type Node, readonly , nullable
804 |

The attribute must return the focus node of the context object, 805 | or null if the anchor is null.

806 |
focusOffset of type unsigned long, readonly
807 |

The attribute must return the focus offset of the context object, 808 | or 0 if the focus is null.

809 |
isCollapsed of type boolean, readonly
810 |

The attribute must return true if and only if the anchor and focus are the same (including if both are null). 811 | Otherwise it must return false.

812 |
rangeCount of type unsigned long, readonly
813 |

The attribute must return 0 if the context object is empty, 814 | and must return 1 otherwise.

815 |
type of type DOMString, readonly
816 |

The attribute must return "None" if the context object is empty, 817 | "Caret" if the context object's range is collapsed, 818 | and "Range" otherwise.

819 |

2.2 Methods

DOMString
820 | See W3C bug 10583. 821 |
No parameters.
Return type: stringifier
addRange
822 |

The method must set the context object's range to range by a strong reference (not by making a copy) 823 | if the root ([DOM4]) 824 | of the range's boundary points are the document associated with context object. 825 | Otherwise, this method must do nothing.

826 | 827 |
Note

Since range is added by reference, subsequent calls to getRangeAt(0) returns the same object, 828 | and any changes that a script makes to range after it is added must be reflected in the selection, 829 | until something else removes or replaces the context object's range. 830 | In particular, the selection will contain b as opposed to a after running the following code: 831 | var r = document.createRange(); r.selectNode(a); getSelection().addRange(r); r.selectNode(b);

832 | 833 |
Note
834 |

IE9 and Firefox 4.0 store a reference, as described here. 835 | Chrome 12 dev and Opera 11.10 appear to store a copy, so changes don't affect the selection.

836 | 837 |

Chrome 15 dev seems to ignore addRange() if there's already a range. 838 | IE9 replaces the existing range. 839 | Firefox 9.0a2, of course, just gives you a multi-range selection. 840 | IE is likely to behave closest to Firefox, and is also more useful than silent failure, so the spec goes with that.

841 |
842 |
ParameterTypeNullableOptionalDescription
rangeRange
Return type: void
collapse
843 |

The method must follow these steps:

844 |
    845 |
  1. The method must throw an IndexSizeError exception if offset is longer than node's 846 | length ([DOM4]) and abort these steps.
  2. 847 |
  3. If node's root is not the document associated with the context object, abort these steps.
  4. 848 |
  5. Otherwise, let newRange be a new range.
  6. 849 |
  7. Set ([DOM4]) 850 | the start and the end of newRange to (node, offset).
  8. 851 |
  9. Set the context object's range to newRange.
  10. 852 |
853 |
ParameterTypeNullableOptionalDescription
nodeNode
offsetunsigned long
Return type: void
collapseToEnd
854 |

The method must throw InvalidStateError exception if the context object is empty. 855 | Otherwise, it must create a new range, set both its start and end 856 | to the end of the context object's range, 857 | and then set the context object's range to the newly-created range.

858 |
No parameters.
Return type: void
collapseToStart
859 |

The method must throw InvalidStateError ([DOM4]) exception 860 | if the context object is empty. 861 | Otherwise, it must create a new range, set both its start and end 862 | to the start of the context object's range, 863 | and then set the context object's range to the newly-created range.

864 | 865 |
Note

For collapseToStart/End, IE9 mutates the existing range, while Firefox 9.0a2 and Chrome 15 dev replace it with a new one. 866 | The spec follows the majority and replaces it with a new one, leaving the old Range object unchanged.

867 |
No parameters.
Return type: void
containsNode
868 |

The method must return false if the context object is empty 869 | or if node's root is not the document associated with the context object.

870 | 871 |

Otherwise, if allowPartialContainment is false, 872 | the method must return true if and only if 873 | start of its range is before or visually equivalent to 874 | the first boundary point in the node 875 | and end of its range is after or visually equivalent to 876 | the last boundary point in the node.

877 | 878 |

If allowPartialContainment is true, 879 | the method must return true if and only if 880 | start of its range is before or visually equivalent to 881 | the first boundary point in the node 882 | or end of its range is after or visually equivalent to 883 | the last boundary point in the node.

884 |
ParameterTypeNullableOptionalDescription
nodeNode
allowPartialContainmentboolean
Return type: boolean
deleteFromDocument
885 |

The method must invoke deleteContents() ([DOM4]) 886 | on the context object's range if the context object is not empty. 887 | Otherwise the method must do nothing.

888 | 889 |
Note

This is the one method that actually mutates the range instead of replacing it. 890 | This matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 12.00 alpha can't be tested, 891 | because getRangeAt() returns a copy anyway.)

892 |
No parameters.
Return type: void
empty
893 |

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

894 |
No parameters.
Return type: void
extend
895 |

The method must follow these steps:

896 |
    897 |
  1. If node's root is not the document associated with the context object, abort these steps.
  2. 898 |
  3. If the context object is empty, throw an InvalidStateError exception and abort these steps.
  4. 899 |
  5. Let oldAnchor and oldFocus be the context object's anchor and focus, 900 | and let newFocus be the boundary point (node, offset).
  6. 901 |
  7. Let newRange be a new range.
  8. 902 |
  9. If node's root is not the same as 903 | the context object's range's root, set newRange's 904 | start and end to newFocus.
  10. 905 |
  11. Otherwise, if oldAnchor is before or equal to newFocus, 906 | set newRange's start to oldAnchor, 907 | then set its end to newFocus.
  12. 908 |
  13. Otherwise, set newRange's start to newFocus, 909 | then set its end to oldAnchor.
  14. 910 |
  15. Set the context object's range to newRange.
  16. 911 |
  17. If newFocus is before oldAnchor, set the context object's direction to backwards. 912 | Otherwise, set it to forwards.
  18. 913 |
914 |
Note

Reverse-engineered circa January 2011. 915 | IE doesn't support it, so I'm relying on Firefox (implemented extend() sometime before 2000) and WebKit (implemented extend() in 2007). 916 | I'm mostly ignoring Opera, because gsnedders tells me its implementation isn't compatible. 917 | 918 | Firefox 12.0a1 seems to mutate the existing range. 919 | IE9 doesn't support extend(), and it's impossible to tell whether Chrome 17 dev or Opera Next 12.00 alpha mutate or replace, 920 | because getRangeAt() returns a copy anyway. 921 | Nevertheless, I go against Gecko here, to be consistent with collapse().

922 |
ParameterTypeNullableOptionalDescription
nodeNode
offsetunsigned long
Return type: void
getRangeAt
923 |

The method must throw an IndexSizeError ([DOM4]) exception 924 | if index is not 0, or if the context object is empty. 925 | Otherwise, it must return a reference to (not a copy of) the context object's range.

926 | 927 |
Note

Thus subsequent calls of this method returns the same range object 928 | if nothing has removed the context object's range in the meantime. 929 | In particular, getSelection().getRangeAt(0) === getSelection().getRangeAt(0) evaluates to true 930 | if the selection is not empty.

931 | 932 |
Note

IE9 and Firefox 4.0 return the same object every time, as the spec says. 933 | Chrome 12 dev and Opera 11.10 return a different object every time.

934 |
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: Range
removeAllRanges
935 |

The method must make the context object empty by disassociating its range 936 | if the context object has an associated range.

937 |
No parameters.
Return type: void
removeRange
938 |

The method must make the context object empty by disassociating its range 939 | if the context object's range is range. 940 | Otherwise, it must do nothing.

941 |
ParameterTypeNullableOptionalDescription
rangeRange
Return type: void
selectAllChildren
942 |

The method must follow these steps:

943 |
    944 |
  1. If node's root is not the document associated with the context object, abort these steps.
  2. 945 |
  3. Let newRange be a new range and nodeLength be the length of node.
  4. 946 |
  5. Set newRange's start to (node, 0).
  6. 947 |
  7. Set newRange's end to (node, nodeLength).
  8. 948 |
  9. Set the context object's range to newRange.
  10. 949 |
  11. Set the context object's direction to forwards.
  12. 950 |
951 | 952 |
Note
953 |

Based mostly on Firefox 9.0a2. It has a bug that I didn't reproduce, namely 954 | that if you pass a Document as the argument, the end offset becomes 1 instead 955 | of the number of children it has. It also throws a RangeException instead of 956 | DOMException, because its implementation predated their merging. 957 | 958 |

IE9 behaves similarly but with glitches. It throws "Unspecified error." if 959 | the node is detached or display:none, and apparently in some random other cases 960 | too. It throws "Invalid argument." for detached comments (only!). Finally, if 961 | you pass it a comment, it seems to select the whole comment, unlike with text 962 | nodes. 963 | 964 |

Chrome 16 dev behaves as you'd expect given its Selection implementation. 965 | It refuses to select anything that's not visible, so it's almost always wrong. 966 | Opera 11.50 just does nothing in all my tests, as usual. 967 | 968 |

The new range replaces any existing one, doesn't mutate it. 969 | This matches IE9 and Firefox 12.0a1. (Chrome 17 dev and Opera Next 12.00 970 | alpha can't be tested, because getRangeAt() returns a copy anyway.) 971 |

972 |
ParameterTypeNullableOptionalDescription
nodeNode
Return type: void
setBaseAndExtent
973 |

The method must follow these steps:

974 |
    975 |
  1. If anchorOffset is longer than anchorNode's length 976 | or if focusOffset is longer than focusNode's length, 977 | throw an IndexSizeError exception and abort these steps.
  2. 978 |
  3. If the roots of anchorNode or focusNode are not the document associated with context object, 979 | abort these steps.
  4. 980 |
  5. Let anchor be the boundary point (anchorNode, anchorOffset) 981 | and let focus be the boundary point (focusNode, focusOffset).
  6. 982 |
  7. Let newRange be a new range.
  8. 983 |
  9. If anchor is before focus, 984 | set the newRange's start to anchor 985 | and its end to focus. 986 | Otherwise, set them to focus and anchor respectively.
  10. 987 |
  11. Set the context object's range to newRange.
  12. 988 |
  13. If focus is before anchor, set context object's direction to backwards. 989 | Otherwise, set it to forwards
  14. 990 |
ParameterTypeNullableOptionalDescription
anchorNodeNode
anchorOffsetunsigned long
focusNodeNode
focusOffsetunsigned long
Return type: void
setPosition
991 |

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

992 |
ParameterTypeNullableOptionalDescription
nodeNode
offsetunsigned long
Return type: void
993 | 994 |
Note

See also nsISelection.idl from Gecko. 995 | This spec doesn't have everything from there yet, in particular selectionLanguageChange() and containsNode() are missing. 996 | They are missing because I couldn't work out how to define them in terms of Ranges.

997 | 998 |
Note
999 |

Originally, the Selection interface was a Netscape feature. 1000 | The original implementation was carried on into Gecko (Firefox), and the feature was later implemented independently by other browser engines. 1001 | The Netscape implementation always allowed multiple ranges in a single selection, for instance so the user could select a column of a table 1002 | However, multi-range selections proved to be an unpleasant corner case 1003 | that web developers didn't know about and even Gecko developers rarely handled correctly. 1004 | Other browser engines never implemented the feature, and clamped selections to a single range in various incompatible fashions.

1005 | 1006 |

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

1010 |
1011 | 1012 |

All of the members of the Selection interface are defined 1013 | in terms of operations on the range object (if any) represented by the object. 1014 | These operations can raise exceptions, as defined for the Range ([DOM4]) interface; 1015 | this can therefore result in the members of the Selection interface raising exceptions as well, 1016 | in addition to any explicitly called out below.

1017 |
1018 | 1019 |
1020 |

3. Extensions to Other Interfaces

1021 | 1022 |

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

1024 | 1025 |
1026 |

3.1 Extensions to Document interface

1027 |
partial interface Document {
1028 |     Selection? getSelection ();
1029 | };

3.1.1 Methods

getSelection
1030 |

The method must return the selection associated with context object 1031 | if the context object has an associated browsing context, 1032 | and it must return null otherwise.

1033 |
No parameters.
Return type: Selection, nullable
1034 |
Note

1035 | If we create a Document object with no browsing context 1036 | (say via document.implementation.createHTMLDocument("") and call getSelection() on it), 1037 | IE9 seems to return a different Selection object. 1038 | Firefox 12.0a1 and Opera Next 12.00 alpha return the same object as for the current window. 1039 | Chrome 17 dev returns null. 1040 | See discussion. 1041 | There's no meaningful selection associated with such a document, so we follow WebKit and require returning null.

1042 |
1043 | 1044 |
1045 |

3.2 Extensions to Window interface

1046 |
partial interface Window {
1047 |     Selection? getSelection ();
1048 | };

3.2.1 Methods

getSelection
1049 |

The method must invoke and return the result of getSelection() on the context object's 1050 | document ([HTML5]) property.

1051 |
No parameters.
Return type: Selection, nullable
1052 |
1053 | 1054 |
1055 | 1056 |
1057 |

4. User Interactions

1058 | 1059 |

The user agent should allow the user to change the selection associated with the active document 1060 | (defined in [HTML5]). 1061 | If the user makes any modification to a selection, the user agent must create a new range with suitable 1062 | start and 1063 | end of the range ([DOM4]) 1064 | and associate the selection with this new range (not modify the existing range), 1065 | and set update selection's direction to forwards if the start is before or equal to the end, 1066 | backwards if if the end is before the start, 1067 | or directionless if the start and the end cannot be ordered due to the platform convention.

1068 | 1069 |

The user agent must not make a selection empty if it was not already empty 1070 | in response to any user actions (e.g. clicking on a non-editable region).

1071 | 1072 |
Note

See bug 15470. 1073 | IE9 and Opera Next 12.00 alpha allow the user to reset the range to null after the fact by clicking somewhere; 1074 | Firefox 12.0a1 and Chrome 17 dev do not. 1075 | I follow Gecko/WebKit, because it lessens the chance of getRangeAt(0) throwing.

1076 | 1077 |
1078 |

4.1 selectstart event

1079 | 1080 |

When the user agent is about to associate a new range newRange to the selection in response to a user initiated action, 1081 | the user agent must fire ([DOM4]) 1082 | an event with the name selectstart, which bubbles and is cancelable, 1083 | at the node associated with the boundary point of newRange's start 1084 | prior to changing the selection if the selection was previously empty 1085 | or the previously associated range was collapsed.

1086 | 1087 |

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

1088 | 1089 |

The user agent must not fire the event when the user agent sets the selection empty.

1090 |
1091 | 1092 |
1093 |

4.2 selectionchange event

1094 | 1095 |

When the selection is dissociated with its range, associated with a new range 1096 | or the associated range's boundary point is mutated either by the user or the content script, 1097 | the user agent must queue a task ([HTML5]) 1098 | to fire an event with the name selectionchange, which does not bubble and is not cancelable, 1099 | at the document associated with the selection.

1100 |
1101 | 1102 |
1103 | 1104 |
1105 |

A. Acknowledgements

1106 |

Many thanks to

1107 |
1117 | 1118 | 1119 | 1120 |

B. References

B.1 Informative references

[DOM4]
Anne van Kesteren; Aryeh Gregor; Ms2ger; Alex Russell; Robin Berjon. W3C DOM4. 18 June 2015. W3C Last Call Working Draft. URL: http://www.w3.org/TR/dom/ 1121 |
[HTML5]
Ian Hickson; Robin Berjon; Steve Faulkner; Travis Leithead; Erika Doyle Navara; Edward O'Connor; Silvia Pfeiffer. HTML5. 28 October 2014. W3C Recommendation. URL: http://www.w3.org/TR/html5/ 1122 |
1123 | --------------------------------------------------------------------------------