javascriptsvgxml-namespaces

Why is it necessary to use `document.createElementNS` when adding `svg` tags to an HTML document via JS?


This won't work:

const svg = document.createElement('svg')
svg.setAttribute('height', '100')
svg.setAttribute('width', '100')
document.body.appendChild(svg)

const rect = document.createElement('rect')
rect.setAttribute('height', '100%')
rect.setAttribute('width', '100%')
rect.setAttribute('fill', 'red')
svg.appendChild(rect)

This will work:

const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svg.setAttribute('height', '100')
svg.setAttribute('width', '100')
document.body.appendChild(svg)

const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('height', '100%')
rect.setAttribute('width', '100%')
rect.setAttribute('fill', 'red')
svg.appendChild(rect)

Apparently I need to explicitely specify the namespace each time I create an svg tag or any of its descendants via JS.

Q1: Why is this necessary? Quoting MDN docs on the svg tag:

Note: The xmlns attribute is only required on the outermost svg element of SVG documents. It is unnecessary for inner svg elements or inside HTML documents.

Well I'm nesting the svg inside HTML here so the xmlns attribute shouldn't be necessary should it?

Q2: Can this be avoided?

Typing document.createElementNS('http://www.w3.org/2000/svg', 'rect') by hand each time is annoying.

Is there any shorter way?


Solution

  • If you call document.createElement("a") which <a> should it create? The HTML one or the SVG one? There lies the problem. Ditto script etc. You're not giving the browser any way to guess till the appendChild step and by then it's too late.

    You can do this without namespaces via innerHTML because that takes a string with all the markup so the browser can parse and add in one step and thereby infer the correct namespace.

    Or just create a wrapper function

    function createSVGElement(tag) {
      return document.createElementNS('http://www.w3.org/2000/svg', tag)
    }