Cannot insert elements into a SVG in a loop #1199

Closed
joshcartme opened this Issue Sep 10, 2015 · 9 comments

Projects

None yet

5 participants

@joshcartme

I'm trying to build a scatterplot using riot and have found that adding svg elements using a loop doesn't work. The elements are added to the dom (I can see them in the inspector) but the svg isn't redrawn (repainted?) so the looped elements do not visibly show in the browser. Here's a very simple example that shows the behavior:

https://jsfiddle.net/g59ump4a/5/

The svg in the above example should just be 3 black dots.

In an actual use case the data will most likely be loaded asynchronously requiring dynamic svg creation.

@joshcartme joshcartme changed the title from SVG's don't work with loops to SVGs don't work with loops Sep 10, 2015
@nikbelikov

May be it's the same problem? https://www.npmjs.com/package/riot-ss

@GianlucaGuarini GianlucaGuarini added the bug label Sep 10, 2015
@rwu823
rwu823 commented Sep 10, 2015

@nikbelikov

I think both are not similarity problem. I writ the riot-ss was resolve SVG Sprite problem with <use> tag.

@nikbelikov

I know. But may be both of those problems has the same root?

@joshcartme

A bit old and possibly completely unrelated but maybe it's a namespace issue similar to the svg namespacing issue mentioned here, http://stackoverflow.com/a/3642265

It seems that because svgs aren't normal HTMLElements they don't get re rendered when their nodes are manipulated in the same way that normal html elements would.

@joshcartme joshcartme changed the title from SVGs don't work with loops to Cannot insert elements into a SVG in a loop Sep 14, 2015
@joshcartme

tl,dr: the following commit fixes my original jsfiddle in the OP (and any case of an each in a <g>) but will need to be made more generic to handle arbitrary <svg> elements.

joshcartme@c490c0c

The problem is that document.createElement does not properly namespace svg elements so we need to use document.createElementNS

In the above commit I've only applied the fix to <svg> and <g> which fixes my exact case but I think this would need to be more generic to handle any valid svg element, https://developer.mozilla.org/en-US/docs/Web/SVG/Element

So far I couldn't figure out a clever way for mkdom to figure out if the html it was looking at should be svg namespaced or not, hence the svgEls object. I think something more clever may be necessary since some elements are both valid svg and non svg elements <a> for example. Hopefully someone more familiar than me with the codebase could figure it out, like once we encounter an <svg> any node under it needs to use document.createElementNS.

This stackoverflow issue got me started down the path of looking into namespacing, http://stackoverflow.com/a/13691861

@joshcartme

Please feel free to take my commit and run with this! The company I work for is still evaluating riot so I'm not sure how much more time I'll get to put into this.

@GianlucaGuarini
Member

@joshcartme riot is a tiny javascript library to manage UIs build mostly with html. What you need is probably more something like http://svgjs.com/ or http://d3js.org/. Even if we could try to find a solution to render correctly svg nodes, this is not our primary goal. This does not mean that you can not combine the above libraries with riot, it just means that you should decide for the right tool for the job.
I think we will solve this issue in future but at moment we have other priorities.

@joshcartme

@GianlucaGuarini Thanks for the reply! Besides this issue Riot has been great so far.

If there's some easy way to look up the dom tree inside _mkdom to find out if an element is inside an <svg> then it would be trivial to use document.createElementNS instead of document.createElement. That's the only hangup with my solution, I'm not sure how to know if the chunk of html _mkdom gets is meant to be inside an SVG or not in every case. We can look it up by tag name as I've done in my commit, but that's not bullet proof since some tags are valid SVG and valid not SVG.

SVGs are more and more being used for ui elements and I think should be able to live inside custom tags in any framework that supports some sort of custom tags.

I know you have other priorities, but I think I've done a lot of the work of figuring out the issue. One short term possibility would be to take my commit and just document that you can only use each inside an <svg> with <g> tags. I think the namespacing only has to happen on the looped elements.

@nippur72
Contributor

@joshcartme I tried to have a look at this problem and it seems to me there is a rather easy way to discern namespace. Simply, the namespace is in .namespaceURI as a string, so it would be enough to extend mkdom() and mkel() with an extra parameter namespace (that could be undefined for the normal case).

In code:

// in Tag() creation:
dom = mkdom(impl.tmpl, conf.root.namespaceURI) 

this way namespace would be propagated down to:

function mkEl(name, ns) {
  return ns===undefined || ns==='http://www.w3.org/1999/xhtml' ? 
      document.createElement(name) : 
      document.createElementNS(ns, name)
}

I tried to implement it, but other difficult issues appeared, so I had to give up. I am mentioning it in case someone else wants to step in and give a try to it.

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