Skip to content

Commit

Permalink
Fix IE11 collapsed ranges in ReactDOMSelection
Browse files Browse the repository at this point in the history
In IE10/11, it is apparently possible to have a Selection or Range object that has the following properties:

  - `anchorNode` === `focusNode` (Selection) or `startContainer` === `endContainer` (Range)
  - `anchorOffset` === `focusOffset` (Selection) or `startOffset` === `endOffset` (Range)
  - `isCollapsed` === `false` (Selection) or `collapsed` === `false` (Range)

As defined in http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html, this doesn't really make sense. Since the nodes and offsets are the same, the "collapsed" value should be `true`.

Moreover, when calling `selection.toString()` in this case, it appears that the entire text contents of `body` -- including `<script>` tag contents -- are considered within the selection. I thought maybe the selected nodes were missing from the DOM or something, but no, they're there.

Sidestep all of this in `ReactDOMSelection` by calculating the `collapsed` property manually and setting the selection length directly to zero if it is actually collapsed.

Side note: I think that for selection restoration on contenteditables, we shouldn't try to do this offset calculation. We should just use the structure provided natively (nodes and offsets) since we can restore using that structure as well.
  • Loading branch information
Isaac Salier-Hellendag authored and zpao committed Apr 24, 2014
1 parent 1b022e9 commit cc292c1
Showing 1 changed file with 29 additions and 2 deletions.
31 changes: 29 additions & 2 deletions src/browser/ui/ReactDOMSelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ var ExecutionEnvironment = require('ExecutionEnvironment');
var getNodeForCharacterOffset = require('getNodeForCharacterOffset');
var getTextContentAccessor = require('getTextContentAccessor');

/**
* While `isCollapsed` is available on the Selection object and `collapsed`
* is available on the Range object, IE11 sometimes gets them wrong.
* If the anchor/focus nodes and offsets are the same, the range is collapsed.
*/
function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
return anchorNode === focusNode && anchorOffset === focusOffset;
}

/**
* Get the appropriate anchor and focus node/offset pairs for IE.
*
Expand Down Expand Up @@ -73,13 +82,31 @@ function getModernOffsets(node) {
var focusOffset = selection.focusOffset;

var currentRange = selection.getRangeAt(0);
var rangeLength = currentRange.toString().length;

// If the node and offset values are the same, the selection is collapsed.
// `Selection.isCollapsed` is available natively, but IE sometimes gets
// this value wrong.
var isSelectionCollapsed = isCollapsed(
selection.anchorNode,
selection.anchorOffset,
selection.focusNode,
selection.focusOffset
);

var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;

var tempRange = currentRange.cloneRange();
tempRange.selectNodeContents(node);
tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);

var start = tempRange.toString().length;
var isTempRangeCollapsed = isCollapsed(
tempRange.startContainer,
tempRange.startOffset,
tempRange.endContainer,
tempRange.endOffset
);

var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
var end = start + rangeLength;

// Detect whether the selection is backward.
Expand Down

0 comments on commit cc292c1

Please sign in to comment.