3

I have trouble composing an inline-SVG using Javascript.
The problem can be reduced to the following code (live example here):

Somewhere inside the body:

<svg id="drawing" xmlns="http://www.w3.org/2000/svg" version="1.1">
</svg>

Inside onReady:

$("#drawing").append($("<rect style='fill: blue' width='100' height='100' />"));

I expected to see a blue rectangle now. However, Chrome and Firefox don't show anything.

Using Firebug, I found out that Firefox interprets the "rect" as a HTMLUnknownElement (and not as a SVGElement).
If I choose "Edit SVG" on the SVG element (using Firebug) and insert a whitespace somewhere, the SVG seems to be reparsed and the rectangle appears.

How can I tell the parser to parse this fragment correctly?

Matthias
  • 12,053
  • 4
  • 49
  • 91
  • 1
    Have you tried an explicit namespace on the rect? Alternatively, could you create the node programmatically and insert it? – Marcin May 20 '11 at 17:05
  • You mean like http://jsfiddle.net/Jkjaa/3/ ? No, doesn't work, either. Well - I am getting the SVG fragment as text from a GET-request. I'd like to avoid parsing it manually. – Matthias May 20 '11 at 17:15
  • That jsFiddle does nothing for me. Also, I don't see how it implements either suggestion. – Marcin May 20 '11 at 17:18
  • Yes, it does nothing, but it should in my opinion. I added the "xmlns='http://www.w3.org/2000/svg'" to the rect - is that what you meant with namespace? The second suggestion (as said in the first response) is no options for me. – Matthias May 20 '11 at 17:22
  • I've fixed your jsfiddle. It now seems to work for me in the sense that a node is being added (it wasn't before). I'm not seeing anything drawn in the result pane, though. – Marcin May 20 '11 at 17:24
  • I meant adding it with the xml namespace prefix, but yes, same idea. – Marcin May 20 '11 at 17:27
  • This might help: http://plugins.jquery.com/project/svg – Marcin May 20 '11 at 18:34

4 Answers4

1

I'm afraid it's not that easy:

  1. jsfiddle is not the place to test svg, it sends wrong content-type
  2. references to external js-files can't be created the html-way(always keep in mind, svg doesn't have to do anything with html)
  3. jquery uses some dummy-div's for creating the elements when using append(), but svg doesn't know div-elements
  4. also note: binding's to the load-event of a svg-document with jQuery doesn't seem to work

Here an example-code, works for me in FF when delivered as image/svg+xml


<svg id="drawing" 
     xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.1"  
     onload="fx()">
  <script type="text/ecmascript" xlink:href="http://code.jquery.com/jquery-latest.js" />
  <script type="text/ecmascript">
  function fx()
  {
   $(document.createElementNS('http://www.w3.org/2000/svg', 'rect'))
     .css('fill','blue')
     .attr({'width':100,'height':100})
     .appendTo('#drawing');
   }
  </script>  
</svg>

But like Marcin I would suggest to use a plugin.

To add from the parent document you may use an object containing the properties of the element, basic example:


<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script  type="text/javascript">
/*<![CDATA[*/

function fx(obj,params)
{
  var svgDoc=obj.contentDocument;
  if(typeof params.name!='string')return;
  var props=$.extend({'attrs':{},'style':{},'selector':null},params);
      props.target=(!props.selector)?svgDoc.documentElement:$(svgDoc).find(props.selector)

    $(svgDoc.createElementNS('http://www.w3.org/2000/svg', props.name))
     .css(props.style)
     .attr(props.attrs)
     .appendTo(props.target);
}

/*]]>*/
</script>
</head>
<body>
  <object onload="fx(this,{'name':'rect','attrs':{'width':100,'height':100},'style':{'fill':'blue'},'selector':'#drawing'})" 
          data="my.svg" 
          type="image/svg+xml" 
          width="200" 
          height="200">
    <param name="src" value="my.svg">
  </object>
</body> 
</html>

The structure of the object:

  • name:tagName(string)
  • attrs:attributes(object)
  • style:style(object)
  • selector:selector(string, if omitted the root-element will be selected)
Dr.Molle
  • 116,463
  • 16
  • 195
  • 201
  • I don't want to use standalone SVG but embed it into HTML. However, some parts of the SVG should be loaded dynamically. Any idea on how to transport and parse them? The approach of sending them as plaintext doesn't seem to work according to your answer. Is it really not possible to parse SVG fragments somehow? – Matthias May 20 '11 at 19:13
  • added another example, maybe it's useful. – Dr.Molle May 20 '11 at 22:52
1

This example shows how to embed SVG in XHTML, including the programmatic creation of new SVG elements: http://phrogz.net/svg/svg_in_xhtml5.xhtml

This example shows how to use XHR to fetch SVG as XML, find a fragment of it, and two ways convert it into the local document before appending the node to the existing SVG document: http://phrogz.net/svg/fetch_fragment.svg

In general:

  1. Don't use jQuery with SVG directly.
  2. Dynamically-created SVG elements must be created using createElementNS, supplying the SVG namespace URI 'http://www.w3.org/2000/svg'. (Note, however, that the SVG attributes should not be created with a namespace.)
  3. You need to be sure to serve your XHTML as XML (content-type: application/xhtml+xml) and not as text/html.

Here's a general-purpose function I use on occasion for creating SVG elements conveniently. It works both within SVG documents as well as SVG-in-XHTML, allows for text content to be created directly, and supports namespaced attributes (such as xlink:href).

// Example usage:
//   var parentNode = findElementInTheSVGDocument();
//   var r = createOn( parentNode, 'rect', {
//     x:12, width:10, height:10, 'fill-opacity':0.3
//   });
function createOn(root,name,attrs,text){
  var doc = root.ownerDocument;
  var svg = root;
  while (svg.tagName!='svg') svg=svg.parentNode;
  var svgNS = svg.getAttribute('xmlns');
  var el = doc.createElementNS(svgNS,name);
  for (var attr in attrs){
    if (attrs.hasOwnProperty(attr)){
      var parts = attr.split(':');
      if (parts[1]) el.setAttributeNS(svg.getAttribute('xmlns:'+parts[0]),parts[1],attrs[attr]);
      else el.setAttributeNS(null,attr,attrs[attr]);
    }
  }
  if (text) el.appendChild(document.createTextNode(text));
  return root.appendChild(el);
} 
Phrogz
  • 296,393
  • 112
  • 651
  • 745
0

See: Convert svg into base64

I always make sure a svg fragment is inside a svg element or just force refresh the svg.

Community
  • 1
  • 1
seahorsepip
  • 4,519
  • 1
  • 19
  • 30
0

I assume you'd like to get a fragment of SVG parsed, without having to turn it into JSON and whatnot. Here's some CoffeeScript that does it. I tested the code on SVG embedded in HTML, but I think it should work in any circumstance.

https://github.com/pwnall/ddr/blob/master/javascripts/graphics/pwnvg.coffee#L169

I wrap the SVG fragment in an tag to build a stand-alone SVG document, then I use a DOMParser to parse the SVG document, and I pull out the children of the root element (the wrapper) one by one, and stick them into the original SVG's DOM.

In theory, there is an easier (and faster approach), but it doesn't work right now. http://crbug.com/107982

pwnall
  • 6,634
  • 2
  • 23
  • 30