Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #6950, work around IE missing innerHTML on SVG nodes #6982

Merged
merged 6 commits into from
Jun 8, 2016

Conversation

joshhunt
Copy link
Contributor

@joshhunt joshhunt commented Jun 7, 2016

Fixes #6950, where dangerouslySetInnerHTML doesn't work on any SVG tag. I updated setInnerHTML to use the method hashed out by @spicyj, and updated DOMLazyTree's use of node.innerHTML to use setInnerHTML instead.

I was hesitant about updating DOMLazyTree to use the setInnerHTML util, but it seemed to make the most sense. If it's not appropriate I'll just make the workaround local to DOMLazyTree.

If this looks good I'll go ahead and add some tests to make sure it continues to work.

  1. Fork the repo and create your branch from master. ✅
  2. If you've added code that should be tested, add tests! ✅
  3. If you've changed APIs, update the documentation.
  4. Ensure the test suite passes (grunt test). ✅
  5. Make sure your code lints (grunt lint) - we've done our best to make sure these rules match our internal linting guidelines. ✅
  6. If you haven't already, complete the CLA. ✅

@sophiebits
Copy link
Collaborator

This looks generally correct. Can we detect when .innerHTML is missing and only do this workaround in that case? Otherwise I think this is right. It might be hard to cover this with our unit test setup.

@joshhunt
Copy link
Contributor Author

joshhunt commented Jun 7, 2016

That sounds a lot better, and makes things neater. I only added the typeof SVGElement check because SVGElement is actually not present in the testing environment, so at least that should be easier to work with. I'll have a go at adding tests and report back.

// the target node
if (typeof SVGElement !== 'undefined' && node instanceof SVGElement) {
reusableSVGContainer = reusableSVGContainer || document.createElement('div');
reusableSVGContainer.innerHTML = '<svg>' + html + '</svg>';
Copy link
Contributor

@syranide syranide Jun 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missing something, why not create the svg directly and drop the div? EDIT: It seems to have innerHTML, but perhaps it doesn't actually work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IE does not support innerHTML on SVG Elements, so we wrap the markup to make it a 'complete' svg string, pop that into a temp container node, then copy across the children of the <svg>.

@facebook-github-bot
Copy link

@joshhunt updated the pull request.

@facebook-github-bot
Copy link

@joshhunt updated the pull request.

@@ -28,7 +31,20 @@ var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunc
*/
var setInnerHTML = createMicrosoftUnsafeLocalFunction(
function(node, html) {
node.innerHTML = html;
if ('innerHTML' in node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spicyj Perhaps it makes sense to check for the SVG namespace as well?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let's do…

if (node.namespaceURI === DOMNamespaces.svg && !('innerHTML' in node)) {
  // workaround
} else {
  node.innerHTML = html;
}

Then I think this looks good if you have done manual testing that reproduces the original issue and confirms this fixes it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... is it possible that this could break due to improper namespaces or does HTML enforce the SVG namespace on descendants of an SVG-element? I think not? But perhaps the element does not work correctly either without a proper namespace?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe SVG elements work with an incorrect namespace, and the parser should assign it automatically in most cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would be the advantage of checking the namespace as opposed to just checking if the node is a SVGElement?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either would be fine. Namespace works better in jsdom but if you prefer instanceof SVGElement that seems fine too.

@ghost
Copy link

ghost commented Jun 8, 2016

@joshhunt updated the pull request.

@ghost
Copy link

ghost commented Jun 8, 2016

@joshhunt updated the pull request.

@ghost
Copy link

ghost commented Jun 8, 2016

@joshhunt updated the pull request.

@sophiebits sophiebits added this to the 15-next milestone Jun 8, 2016
@sophiebits sophiebits merged commit 99d8524 into facebook:master Jun 8, 2016
@sophiebits
Copy link
Collaborator

Looks great, thank you!

zpao pushed a commit to zpao/react that referenced this pull request Jun 8, 2016
…ebook#6982)

* 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)
zpao pushed a commit that referenced this pull request Jun 14, 2016
* 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)
@zpao zpao modified the milestones: 15-next, 15.2.0 Jun 14, 2016
var newNodes = reusableSVGContainer.firstChild.childNodes;
for (var i = 0; i < newNodes.length; i++) {
node.appendChild(newNodes[i]);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @jp7837 pointed out in #7358 (comment) - this is going to get every other child - childNodes is a live NodeList. This should be a while loop. I can confirm this is what is happening in IE11.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants