Assuming this:
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
});
</script>
</head>
<body>
<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>
</body>
Why don't I see anything?
When you pass a markup string into
$
, it's parsed as HTML using the browser'sinnerHTML
property on a<div>
(or other suitable container for special cases like<tr>
).innerHTML
can't parse SVG or other non-HTML content, and even if it could it wouldn't be able to tell that<circle>
was supposed to be in the SVG namespace.innerHTML
is not available on SVGElement—it is a property of HTMLElement only. Neither is there currently aninnerSVG
property or other way(*) to parse content into an SVGElement. For this reason you should use DOM-style methods. jQuery doesn't give you easy access to the namespaced methods needed to create SVG elements. Really jQuery isn't designed for use with SVG at all and many operations may fail.HTML5 promises to let you use
<svg>
without anxmlns
inside a plain HTML (text/html
) document in the future. But this is just a parser hack(**), the SVG content will still be SVGElements in the SVG namespace, and not HTMLElements, so you'll not be able to useinnerHTML
even though they look like part of an HTML document.However, for today's browsers you must use XHTML (properly served as
application/xhtml+xml
; save with the .xhtml file extension for local testing) to get SVG to work at all. (It kind of makes sense to anyway; SVG is a properly XML-based standard.) This means you'd have to escape the<
symbols inside your script block (or enclose in a CDATA section), and include the XHTMLxmlns
declaration. example:*: well, there's DOM Level 3 LS's parseWithContext, but browser support is very poor. Edit to add: however, whilst you can't inject markup into an SVGElement, you could inject a new SVGElement into an HTMLElement using
innerHTML
, then transfer it to the desired target. It'll likely be a bit slower though:**: I hate the way the authors of HTML5 seem to be scared of XML and determined to shoehorn XML-based features into the crufty mess that is HTML. XHTML solved these problems years ago.
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:
This does the trick. The circle is on the screen.
Or if you want, use a container div:
And wrap your svg inside container div:
The functional example:
http://jsbin.com/ejifab/1/edit
The advantages of this technique:
$('svg').prepend('<defs><marker></marker><mask></mask></defs>');
like you do in jQuery.$("#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:
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. )I can see circle in firefox, doing 2 things:
1) Renaming file from html to xhtml
2) Change script to
The increasingly popular D3 library handles the oddities of appending/manipulating svg very nicely. You may want to consider using it as opposed to the jQuery hacks mentioned here.
HTML
Javascript
Based on @chris-dolphin 's answer but using helper function: