Skip to content

Commit

Permalink
Fix #6950, work around IE missing innerHTML on SVG nodes (#6982)
Browse files Browse the repository at this point in the history
* Workaround IE lacking innerHTML on SVG elements

* Add tests for setInnerHTML

* Correctly check if node has innerHTML property

* Ensure tests for setInnerHTML actually tests both codepaths

* Provide mock element for setInnerHTML tests

* Only use SVG setInnerHTML workaround for SVG elements

(cherry picked from commit 99d8524)
  • Loading branch information
joshhunt authored and zpao committed Jun 14, 2016
1 parent 92bda14 commit 05c133a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 3 deletions.
5 changes: 3 additions & 2 deletions src/renderers/dom/client/utils/DOMLazyTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'use strict';

var DOMNamespaces = require('DOMNamespaces');
var setInnerHTML = require('setInnerHTML');

var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
var setTextContent = require('setTextContent');
Expand Down Expand Up @@ -50,7 +51,7 @@ function insertTreeChildren(tree) {
insertTreeBefore(node, children[i], null);
}
} else if (tree.html != null) {
node.innerHTML = tree.html;
setInnerHTML(node, tree.html);
} else if (tree.text != null) {
setTextContent(node, tree.text);
}
Expand Down Expand Up @@ -96,7 +97,7 @@ function queueHTML(tree, html) {
if (enableLazy) {
tree.html = html;
} else {
tree.node.innerHTML = html;
setInnerHTML(tree.node, html);
}
}

Expand Down
42 changes: 42 additions & 0 deletions src/renderers/dom/client/utils/__tests__/setInnerHTML.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/

'use strict';

var setInnerHTML = require('setInnerHTML');
var DOMNamespaces = require('DOMNamespaces');

describe('setInnerHTML', function() {
describe('when the node has innerHTML property', () => {
it('sets innerHTML on it', function() {
var node = document.createElement('div');
var html = '<h1>hello</h1>';
setInnerHTML(node, html);
expect(node.innerHTML).toBe(html);
});
});

describe('when the node does not have an innerHTML property', () => {
it('sets innerHTML on it', function() {
// Create a mock node that looks like an SVG in IE (without innerHTML)
var node = {
namespaceURI: DOMNamespaces.svg,
appendChild: jasmine.createSpy(),
};

var html = '<circle></circle><rect></rect>';
setInnerHTML(node, html);

expect(node.appendChild.calls.argsFor(0)[0].outerHTML).toBe('<circle></circle>');
expect(node.appendChild.calls.argsFor(1)[0].outerHTML).toBe('<rect></rect>');
});
});
});
18 changes: 17 additions & 1 deletion src/renderers/dom/client/utils/setInnerHTML.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
'use strict';

var ExecutionEnvironment = require('ExecutionEnvironment');
var DOMNamespaces = require('DOMNamespaces');

var WHITESPACE_TEST = /^[ \r\n\t\f]/;
var NONVISIBLE_TEST = /<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/;

var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');

// SVG temp container for IE lacking innerHTML
var reusableSVGContainer;

/**
* Set the innerHTML property of a node, ensuring that whitespace is preserved
* even in IE8.
Expand All @@ -28,7 +32,19 @@ var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunc
*/
var setInnerHTML = createMicrosoftUnsafeLocalFunction(
function(node, html) {
node.innerHTML = html;
// IE does not have innerHTML for SVG nodes, so instead we inject the
// new markup in a temp node and then move the child nodes across into
// the target node
if (node.namespaceURI === DOMNamespaces.svg && !('innerHTML' in node)) {
reusableSVGContainer = reusableSVGContainer || document.createElement('div');
reusableSVGContainer.innerHTML = '<svg>' + html + '</svg>';
var newNodes = reusableSVGContainer.firstChild.childNodes;
for (var i = 0; i < newNodes.length; i++) {
node.appendChild(newNodes[i]);
}
} else {
node.innerHTML = html;
}
}
);

Expand Down

0 comments on commit 05c133a

Please sign in to comment.