52

I want to create inline SVG graphics using Javascript.

However, it seems like createElementNS function applies some normalization and transforms all tags to lowercase. That is fine for HTML but not for XML/SVG. The NS I used is http://www.w3.org/2000/svg.

In particular I have problems creating a element. As it will be appended as <textpath> and thus will not work.

I did some search but could not find a solution yet.

Does anybody know a solution?

document.createElementNS("http://www.w3.org/2000/svg","textPath");

results in

<textpath></textpath>
pat
  • 2,600
  • 4
  • 20
  • 21
  • How do you find out this happens? How do you dump the created element into XML code? XML tag names should be sensitive: http://ejohn.org/blog/nodename-case-sensitivity/ – Pekka Aug 16 '10 at 10:41

4 Answers4

83

I hope, the following example will help you:

function CreateSVG() {
    var xmlns = "http://www.w3.org/2000/svg";
    var boxWidth = 300;
    var boxHeight = 300;

    var svgElem = document.createElementNS(xmlns, "svg");
    svgElem.setAttributeNS(null, "viewBox", "0 0 " + boxWidth + " " + boxHeight);
    svgElem.setAttributeNS(null, "width", boxWidth);
    svgElem.setAttributeNS(null, "height", boxHeight);
    svgElem.style.display = "block";

    var g = document.createElementNS(xmlns, "g");
    svgElem.appendChild(g);
    g.setAttributeNS(null, 'transform', 'matrix(1,0,0,-1,0,300)');

    // draw linear gradient
    var defs = document.createElementNS(xmlns, "defs");
    var grad = document.createElementNS(xmlns, "linearGradient");
    grad.setAttributeNS(null, "id", "gradient");
    grad.setAttributeNS(null, "x1", "0%");
    grad.setAttributeNS(null, "x2", "0%");
    grad.setAttributeNS(null, "y1", "100%");
    grad.setAttributeNS(null, "y2", "0%");
    var stopTop = document.createElementNS(xmlns, "stop");
    stopTop.setAttributeNS(null, "offset", "0%");
    stopTop.setAttributeNS(null, "stop-color", "#ff0000");
    grad.appendChild(stopTop);
    var stopBottom = document.createElementNS(xmlns, "stop");
    stopBottom.setAttributeNS(null, "offset", "100%");
    stopBottom.setAttributeNS(null, "stop-color", "#0000ff");
    grad.appendChild(stopBottom);
    defs.appendChild(grad);
    g.appendChild(defs);

    // draw borders
    var coords = "M 0, 0";
    coords += " l 0, 300";
    coords += " l 300, 0";
    coords += " l 0, -300";
    coords += " l -300, 0";

    var path = document.createElementNS(xmlns, "path");
    path.setAttributeNS(null, 'stroke', "#000000");
    path.setAttributeNS(null, 'stroke-width', 10);
    path.setAttributeNS(null, 'stroke-linejoin', "round");
    path.setAttributeNS(null, 'd', coords);
    path.setAttributeNS(null, 'fill', "url(#gradient)");
    path.setAttributeNS(null, 'opacity', 1.0);
    g.appendChild(path);

    var svgContainer = document.getElementById("svgContainer");
    svgContainer.appendChild(svgElem);
}
#svgContainer {
  width: 400px;
  height: 400px;
  background-color: #a0a0a0;
}
<body onload="CreateSVG()">
    <div id="svgContainer"></div>
</body>
Tobias Tengler
  • 6,848
  • 4
  • 20
  • 34
gumape
  • 2,832
  • 2
  • 18
  • 9
  • Thanks for your reply. Based on your answer and a text-to-path svg example: [http://pmast.de/svg/textToPath.svg](http://pmast.de/svg/textToPath.svg) I created the following document: [http://pmast.de/svg/textToPath.html](http://pmast.de/svg/textToPath.html) Although the resulting source seems identical and the creation of the elements follows your example, my document is not working... Am I missing something? – pat Aug 25 '10 at 11:56
  • Your example helped me get started with creating SVG elements from JS. Still had to read the W3.org spec document on Core DOM. Namespaces are rather confusing. Thanks for the tips. – CyberFonic Apr 28 '12 at 05:24
  • 3
    Does this work for people? I just get a gray box without any SVG elements. (Firefox and Chrome alike) – schlingel Feb 18 '15 at 14:58
17

Firstly, use createElementNS, as you are doing. createElement (without NS) automatically lowercases element names inside HTML documents, according to the Mozilla documentation.

Secondly, don't trust Google Chrome's "Inspect Element" feature here. It seems to display every element in lowercase, no matter what the actual nodeName is. Try this:

document.createElementNS("http://www.w3.org/2000/svg", "textPath").nodeName
// Output: "textPath"

document.createElement("textPath").nodeName
// Output: "TEXTPATH"

Your problem might be an unrelated issue. For example, this code works fine in Firefox, but breaks in Chrome (12.0.742.112):

function animateSVG() {
  var svgNS = "http://www.w3.org/2000/svg";
  var textElement = document.getElementById("TextElement");
  var amElement = document.createElementNS(svgNS, "animateMotion");
  console.log(textElement);
  console.log(amElement);
  console.log(amElement.nodeName);
  amElement.setAttribute("path", "M 0 0 L 100 100");
  amElement.setAttribute("dur", "5s");
  amElement.setAttribute("fill", "freeze");
  textElement.appendChild(amElement);
  //amElement.beginElement();
};
<body onload="animateSVG()">
  <svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <g transform="translate(100,100)">
      <text id="TextElement" x="0" y="0" style="font-family:Verdana;font-size:24">
        It's SVG!
        <!-- <animateMotion path="M 0 0 L 100 100" dur="5s" fill="freeze"/> -->
      </text>
    </g>
  </svg>
</body>

My issue probably has something to do with the broken handling of animateMotion in Chrome (Issue 13585).

Your issue might be the same, or it might be another issue, but make sure you're not being fooled by the element inspector.

Gaslan
  • 808
  • 1
  • 20
  • 27
dlitz
  • 5,679
  • 3
  • 19
  • 13
2

I have just resolved a similar problem. document.createElement (and I assume document.createElementNS), when called from a HTML page creates a HTML node (where case doesnt matter), not an xml node.

The following works in Chrome:

doc = document.implementation.createDocument(null, null, null); doc.createElementNS("http://www.w3.org/2000/svg","textPath");

you will get your mixed case node.

  • 1
    `document.createElementNS()` does not only create HTML elements. How does this answer have 5 upvotes?? – zoran404 Aug 12 '18 at 05:25
-3

the answer given is too extensive and not really useful to me and i find it to much troublesome or to simply say bogus. If i were you, i would simple have on element in html with tag say:

<b id="myElement"></b>

and when i have to create element or add information i would simply do:

document.getElementById('myElement').innerHTML = 'your stuff here'; 

I Hope that was helpful.

Johnydep
  • 6,027
  • 20
  • 57
  • 74
  • 2
    This question is about SVG, not HTML. – gilly3 Oct 22 '12 at 00:16
  • 3
    @gilly3 he's got a point though -- the accepted answer involves building an SVG DOM element basically from scratch which, while neat to know how to do and technically the more correct answer, is not entirely practical. Since the SVG syntax in an SVG file is mostly identical to the SVG definition syntax in the DOM (from the ' – CCJ Jul 09 '15 at 18:04
  • 3
    That's a pretty specific use case - to have the entire SVG drawing markup as a string, and append it as is to an HTML element. The question is specifically asking about adding an SVG element to an existing drawing. Now, today you can write the `innerHTML` of an SVG element in some browsers, but [that functionality is new, and has only been available since earlier this year](http://stackoverflow.com/questions/23275112#comment45705022_23279658). The only way to add an element to an existing drawing in IE today, and in all browsers at the time this question was asked, is via `createElementNS()`. – gilly3 Jul 09 '15 at 19:03