@samitouri / QOS-React-2 / commits / cc292c1e22

Fix IE11 collapsed ranges in ReactDOMSelection

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.

Isaac Salier-Hellendag committed Apr 24, 2014 at 11:37 UTC cc292c1e229f1e4097bed2ca06ac7550e6c35df0
1 file changed +29 -2
src/browser/ui/ReactDOMSelection.js
+29 -2
@@ -23,6 +23,15 @@ var ExecutionEnvironment = require('ExecutionEnvironment');
23 var getNodeForCharacterOffset = require('getNodeForCharacterOffset');
24 var getTextContentAccessor = require('getTextContentAccessor');
25
26 +/**
27 + * While `isCollapsed` is available on the Selection object and `collapsed`
28 + * is available on the Range object, IE11 sometimes gets them wrong.
29 + * If the anchor/focus nodes and offsets are the same, the range is collapsed.
30 + */
31 +function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
32 + return anchorNode === focusNode && anchorOffset === focusOffset;
33 +}
34 +
35 /**
36 * Get the appropriate anchor and focus node/offset pairs for IE.
37 *
@@ -73,13 +82,31 @@ function getModernOffsets(node) {
82 var focusOffset = selection.focusOffset;
83
84 var currentRange = selection.getRangeAt(0);
76 - var rangeLength = currentRange.toString().length;
85 +
86 + // If the node and offset values are the same, the selection is collapsed.
87 + // `Selection.isCollapsed` is available natively, but IE sometimes gets
88 + // this value wrong.
89 + var isSelectionCollapsed = isCollapsed(
90 + selection.anchorNode,
91 + selection.anchorOffset,
92 + selection.focusNode,
93 + selection.focusOffset
94 + );
95 +
96 + var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;
97
98 var tempRange = currentRange.cloneRange();
99 tempRange.selectNodeContents(node);
100 tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);
101
82 - var start = tempRange.toString().length;
102 + var isTempRangeCollapsed = isCollapsed(
103 + tempRange.startContainer,
104 + tempRange.startOffset,
105 + tempRange.endContainer,
106 + tempRange.endOffset
107 + );
108 +
109 + var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
110 var end = start + rangeLength;
111
112 // Detect whether the selection is backward.