question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Namespacing wrong when adding children to SVG tag

See original GitHub issue

I have an app which makes random svg circles (test case for a bigger project). Initially there are three circles, but a button lets you add more.

The new circle elements are added, but with the wrong namespace: xhtml rather than svg (tested in Chrome 59.0.3071.115). This can be seen by opening the developer tools, going to elements, and then clicking “Add Circle”. A new circle tag is added but the namespace URI is wrong, so svg ignores it and it is not rendered.

The “Move” button is not part of the bug report; it was added to see if altering attributes (position) works - it does.

The code is below:

[Note also usage of Object.assign in h( ) call - if I just pass this.attr, the change in attributes isn’t picked up]

<script src="https://unpkg.com/hyperapp"></script>
<body></body>
<script>
var h = hyperapp.h;
var app = hyperapp.app;

function randInt(a,b) { // random integer in [a,b]
    return Math.floor(Math.random()*(b+1-a))+a;
}

// random circle object

function Circle() {
    this.attr = {
        cx: randInt(1,400),
        cy: randInt(1,400),
        r:  randInt(10,30)
    }
}

Circle.prototype = {
    h: function() {
        // a change in attributes isn't picked up unless Object.assign is used!
        return h("circle", Object.assign({}, this.attr))
    },
    move: function() {
        this.attr.cx = randInt(1,400);
        this.attr.cy = randInt(1,400);
        return this;
    }
}

// the app

app({
    state: { // just a list of objects
        objects: [],
    },

    view: function v(state, actions) {
        return h("div",{},[
            // buttons
            h("button", {onclick: actions.addObj}, 'Add circle'),
            h("button", {onclick: actions.move}, 'Move'),
            h("hr"),
            // svg area
            h("svg", {width:400, height:400, style:{backgroundColor: 'lavender'}}, 
                state.objects.map(obj => obj.h())
            )
        ])
    },

    actions: {
        addObj: function(state) {
            // add a new object
            return {
                objects: state.objects.concat(new Circle())
            }
        },
        move: function(state) {
            // moves all the objects
            return {
                objects: state.objects.map(obj => obj.move())
            }
        }
    },

    events: {
        init: function(state, actions) {
            // put 3 random objects in the state
            actions.addObj(state)
            actions.addObj(state)
            actions.addObj(state)
        }
    }
})

</script>

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:19 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
jorgebucarancommented, Jul 15, 2017

@w-mcilhagga Thanks! I’ll refactor this patch a bit and create a commit soon with other small changes I am currently working on (unless you really want to send me a PR 😏).

Anyway, I’ll make sure to thoroughly thank you in the commit message!

Good job! Also got the SVG fractal example to finally work too! 😅 🎉

2017-07-15 22 01 26

1reaction
w-mcilhaggacommented, Jul 15, 2017

OK, patched the patch function. This works:

      function patch(parent, element, oldNode, node, isSVG) {
        if (oldNode == null) {
          element = parent.insertBefore(createElement(node, isSVG), element)
        } else if (node.tag != null && node.tag === oldNode.tag) {
          isSVG = isSVG || (node.tag==='svg')
          updateElementData(element, oldNode.data, node.data)
          
          var len = node.children.length
          var oldLen = oldNode.children.length
          var reusableChildren = {}
          var oldElements = []
          var newKeys = {}

          for (var i = 0; i < oldLen; i++) {
            var oldElement = element.childNodes[i]
            oldElements[i] = oldElement

            var oldChild = oldNode.children[i]
            var oldKey = getKey(oldChild)

            if (null != oldKey) {
              reusableChildren[oldKey] = [oldElement, oldChild]
            }
          }

          var i = 0
          var j = 0

          while (j < len) {
            var oldElement = oldElements[i]
            var oldChild = oldNode.children[i]
            var newChild = node.children[j]

            var oldKey = getKey(oldChild)
            if (newKeys[oldKey]) {
              i++
              continue
            }

            var newKey = getKey(newChild)

            var reusableChild = reusableChildren[newKey] || []

            if (null == newKey) {
              if (null == oldKey) {
                patch(element, oldElement, oldChild, newChild, isSVG)
                j++
              }
              i++
            } else {
              if (oldKey === newKey) {
                patch(element, reusableChild[0], reusableChild[1], newChild, isSVG)
                i++
              } else if (reusableChild[0]) {
                element.insertBefore(reusableChild[0], oldElement)
                patch(element, reusableChild[0], reusableChild[1], newChild, isSVG)
              } else {
                patch(element, oldElement, null, newChild, isSVG)
              }

              j++
              newKeys[newKey] = newChild
            }
          }

          while (i < oldLen) {
            var oldChild = oldNode.children[i]
            var oldKey = getKey(oldChild)
            if (null == oldKey) {
              removeElement(element, oldElements[i], oldChild)
            }
            i++
          }

          for (var i in reusableChildren) {
            var reusableChild = reusableChildren[i]
            var reusableNode = reusableChild[1]
            if (!newKeys[reusableNode.data.key]) {
              removeElement(element, reusableChild[0], reusableNode)
            }
          }
        } else if (
          element != null &&
          node !== oldNode &&
          node !== element.nodeValue
        ) {
          var i = element
          parent.replaceChild((element = createElement(node, isSVG)), i)
        }

        return element
      }
    }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Namespaces crash course - SVG: Scalable Vector Graphics
This parameter says that the <svg> element and its child elements belong to whichever XML dialect has the namespace name http://www.w3.org/2000/ ...
Read more >
Document Structure — SVG 2
An SVG document fragment can consist of a stand-alone SVG document, or a fragment of a parent document enclosed by an 'svg' element....
Read more >
HTML 5, inline SVG, and namespace awareness for SVG DOM
XHTML is namespace aware and XHTML documents apply namespaces according to the rules of XML, so all namespaces must be assigned to each...
Read more >
Webstorm not recognizing namespaced svg elements in inline ...
One solution is to wrap all templates in 'svg:g' elements and leave children without namespaces, but that is not ideal. I found other...
Read more >
Docs • Svelte
Readonly props can be accessed as properties on the element, tied to the ... This works by adding a class to affected elements,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found