Use feature detection and more robust recovering of whitespace for innerHTML in IE8
Andreas Svensson committed
Dec 20, 2013 at 20:52 UTC
1f2d57d6a4ad58e36e80dbffb6fd74c27f0b4357
2 files changed
+34
-6
src/core/ReactDOMIDOperations.js
+29
-3
@@ -49,7 +49,7 @@ var INVALID_PROPERTY_ERRORS = {
49
*/
50
var textContentAccessor = getTextContentAccessor() || 'NA';
51
52
-var LEADING_SPACE = /^ /;
52
+var useWhitespaceWorkaround;
53
54
/**
55
* Operations used to process updates to DOM nodes. This is made injectable via
@@ -124,9 +124,35 @@ var ReactDOMIDOperations = {
124
*/
125
updateInnerHTMLByID: function(id, html) {
126
var node = ReactMount.getNode(id);
127
- // HACK: IE8- normalize whitespace in innerHTML, removing leading spaces.
127
+
128
+ // IE8: When updating a just created node with innerHTML only leading
129
+ // whitespace is removed. When updating an existing node with innerHTML
130
+ // whitespace in root TextNodes is also collapsed.
131
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
129
- node.innerHTML = html.replace(LEADING_SPACE, ' ');
132
+
133
+ if (useWhitespaceWorkaround === undefined) {
134
+ // Feature detection; only IE8 is known to behave improperly like this.
135
+ var temp = document.createElement('div');
136
+ temp.innerHTML = ' ';
137
+ useWhitespaceWorkaround = temp.innerHTML === '';
138
+ }
139
+
140
+ if (useWhitespaceWorkaround) {
141
+ // Magic theory: IE8 supposedly differentiates between added and updated
142
+ // nodes when processing innerHTML, innerHTML on updated nodes suffers
143
+ // from worse whitespace behavior. Re-adding a node like this triggers
144
+ // the initial and more favorable whitespace behavior.
145
+ node.parentNode.replaceChild(node, node);
146
+ }
147
+
148
+ if (useWhitespaceWorkaround && html.match(/^[ \r\n\t\f]/)) {
149
+ // Recover leading whitespace by temporarily prepending any character.
150
+ // \uFEFF has the potential advantage of being zero-width/invisible.
151
+ node.innerHTML = '\uFEFF' + html;
152
+ node.firstChild.deleteData(0, 1);
153
+ } else {
154
+ node.innerHTML = html;
155
+ }
156
},
157
158
/**
src/core/__tests__/ReactDOMIDOperations-test.js
+5
-3
@@ -47,19 +47,21 @@ describe('ReactDOMIDOperations', function() {
47
).toBe(0);
48
});
49
50
- it('should update innerHTML and special-case whitespace', function() {
50
+ it('should update innerHTML and preserve whitespace', function() {
51
var stubNode = document.createElement('div');
52
spyOn(ReactMount, "getNode").andReturn(stubNode);
53
54
+ var html = '\n \t <span> \n testContent \t </span> \n \t';
55
+
56
ReactDOMIDOperations.updateInnerHTMLByID(
57
'testID',
56
- ' testContent'
58
+ html
59
);
60
61
expect(
62
ReactMount.getNode.argsForCall[0][0]
63
).toBe('testID');
64
63
- expect(stubNode.innerHTML).toBe(' testContent');
65
+ expect(stubNode.innerHTML).toBe(html);
66
});
67
});