jquery’s append not working with svg element?

The accepted answer shows too complicated way. As Forresto claims in his answer, “it does seem to add them in the DOM explorer, but not on the screen” and the reason for this is different namespaces for html and svg.

The easiest workaround is to “refresh” whole svg. After appending circle (or other elements), use this:

$("body").html($("body").html());

This does the trick. The circle is on the screen.

Or if you want, use a container div:

$("#cont").html($("#cont").html());

And wrap your svg inside container div:

<div id="cont">
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px">
    </svg>
</div>

The functional example:
http://jsbin.com/ejifab/1/edit

The advantages of this technique:

  • you can edit existing svg (that is already in DOM), eg. created using Raphael or like in your example “hard coded” without scripting.
  • you can add complex element structures as strings eg. $('svg').prepend('<defs><marker></marker><mask></mask></defs>'); like you do in jQuery.
  • after the elements are appended and made visible on the screen using $("#cont").html($("#cont").html()); their attributes can be edited using jQuery.

EDIT:

The above technique works with “hard coded” or DOM manipulated ( = document.createElementNS etc.) SVG only. If Raphael is used for creating elements, (according to my tests) the linking between Raphael objects and SVG DOM is broken if $("#cont").html($("#cont").html()); is used. The workaround to this is not to use $("#cont").html($("#cont").html()); at all and instead of it use dummy SVG document.

This dummy SVG is first a textual representation of SVG document and contains only elements that are needed. If we want eg. to add a filter element to Raphael document, the dummy could be something like <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. The textual representation is first converted to DOM using jQuery’s $(“body”).append() method. And when the (filter) element is in DOM, it can be queried using standard jQuery methods and appended to the main SVG document which is created by Raphael.

Why this dummy is needed? Why not to add a filter element strictly to Raphael created document? If you try it using eg. $("svg").append("<circle ... />"), it is created as html element and nothing is on screen as described in answers. But if the whole SVG document is appended, then the browser handles automatically the namespace conversion of all the elements in SVG document.

An example enlighten the technique:

// Add Raphael SVG document to container element
var p = Raphael("cont", 200, 200);
// Add id for easy access
$(p.canvas).attr("id","p");
// Textual representation of element(s) to be added
var f="<filter id="myfilter"><!-- filter definitions --></filter>";

// Create dummy svg with filter definition 
$("body").append('<svg id="dummy" style="display:none"><defs>' + f + '</defs></svg>');
// Append filter definition to Raphael created svg
$("#p defs").append($("#dummy filter"));
// Remove dummy
$("#dummy").remove();

// Now we can create Raphael objects and add filters to them:
var r = p.rect(10,10,100,100);
$(r.node).attr("filter","url(#myfilter)");

Full working demo of this technique is here: http://jsbin.com/ilinan/1/edit.

( I have (yet) no idea, why $("#cont").html($("#cont").html()); doesn’t work when using Raphael. It would be very short hack. )

Leave a Comment