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?
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)
}