Best way to load svg file from d3

20,332 views
Skip to first unread message

Chris Viau

unread,
Jun 8, 2011, 11:49:11 AM6/8/11
to d3...@googlegroups.com
Is this the most compatible way to load an external SVG file from d3?

<!DOCTYPE html>
<html >
<head>
<script type="text/javascript" src=".//d3.js"></script>
</head>
<body>
<script type="text/javascript">
d3.select("body")
.append("object")
.attr("data", "tux.svg")
.attr("width", 335)
.attr("height", 394)
.attr("type", "image/svg+xml");
</script>
</body>
</html>

Mike Bostock

unread,
Jun 8, 2011, 12:05:09 PM6/8/11
to d3...@googlegroups.com
> Is this the most compatible way to load an external SVG file from d3?

This is not strictly a D3 question; anyway your browser supports
loading external SVG, you can use with D3. One way is to embed an img
tag in your HTML:

<body>
<img src="tux.svg" width="335" height="394" />
</body>

You could also do this in JavaScript:

var tux = new Image;
tux.onload = function() { console.log("Hooray, Tux is here!"); };
tux.src = "tux.svg";

However, I'm not sure that you get access to the SVG dom when you load
it inside an img element.

Another thing you could try would be using d3.xml, and then embedding
the document fragment in the page. I have no idea if that will work
but it seems possible.

https://github.com/mbostock/d3/wiki/Requests#d3_xml

Mike

Chris Viau

unread,
Jun 8, 2011, 12:48:04 PM6/8/11
to d3...@googlegroups.com
You are right. But I was just wondering if an object tag was more appropriate to inline svg or iframe to load an external svg to be able to manipulate it with D3. As I understand, D3 typically generate inline svg (browser support). I will test to see if I can manipulate a svg in an image tag with d3 (browser support) But I don't think it will be difficult to play in an object tag with D3. But maybe more within an iframe.

Mike Bostock

unread,
Jun 8, 2011, 1:12:14 PM6/8/11
to d3...@googlegroups.com
Working d3.xml + SVG example here:

http://bl.ocks.org/1014829

Mike

Chris Viau

unread,
Jun 8, 2011, 7:23:35 PM6/8/11
to d3...@googlegroups.com
Thanks a lot for your generosity. Your gist viewer is an excellent way to share code. I will certainly use it soon.

Michael Kontogiannis

unread,
Aug 1, 2012, 5:52:32 AM8/1/12
to d3...@googlegroups.com, mbos...@cs.stanford.edu
Hi Mike,

Unfortunately this is not working on ie9, it gives me that DOM exception : "SCRIPT5022: DOM Exception: HIERARCHY_REQUEST_ERR (3)" 
Any workarounds would be much appreciated.

Cheers
Michael

Chris Viau

unread,
Aug 1, 2012, 1:43:07 PM8/1/12
to d3...@googlegroups.com, mbos...@cs.stanford.edu
Can you try this slightly modified example (I don't have IE9)? http://bl.ocks.org/1238376
I have another solution if it's not working.

Michael Kontogiannis

unread,
Aug 2, 2012, 5:51:49 AM8/2/12
to d3...@googlegroups.com, mbos...@cs.stanford.edu
Hi Chris,

Thanks for your reply. Unfortunately importNode() is not implemented on ie9 and your example does not work. I know ie is a pain but I have to support it. I am interested to listen to your other solution.

Michael

Chris Viau

unread,
Aug 2, 2012, 11:44:36 AM8/2/12
to d3...@googlegroups.com, mbos...@cs.stanford.edu
This one is supposed to work in IE9. You can find 2 other variants here:
http://stackoverflow.com/questions/1811116/ie-support-for-dom-importnode
If it works, it could be very useful if you could post a working example on bl.ocks


d3.xml(imgPath, 'image/svg+xml', function(xml) {
    try{
        importedNode = document.importNode(xml.documentElement, true);
    }catch(e){
    // IE case
        importedNode = importNode(xml.documentElement, document);
    }
});

    function importNode(node, allChildren) {
        // summary:
        // Manually imports node to the provided document
        switch (node.nodeType) {
        case document.ELEMENT_NODE:
            var newNode = document.createElementNS(node.namespaceURI, node.nodeName);
            if(node.attributes && node.attributes.length > 0){
                for(var i = 0, il = node.attributes.length; i < il; i++){
                    newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName));
                }
            }
            if(allChildren && node.childNodes && node.childNodes.length > 0){
                for(var i = 0, il = node.childNodes.length; i < il; i++){
                    newNode.appendChild(importNode(node.childNodes[i], allChildren));
                }
            }
            
            return newNode;
            break;
            
        case document.TEXT_NODE:
        case document.CDATA_SECTION_NODE:
        case document.COMMENT_NODE:
          return document.createTextNode(node.nodeValue);
          break;

Brandon Flowers

unread,
Jul 26, 2013, 9:06:22 PM7/26/13
to d3...@googlegroups.com, mbos...@cs.stanford.edu
This works well but if you're loading multiple svg documents, one could end up with a collection of svg tags within a parent svg

#1

After each svg completes loading, I append each svg via:

d3.select('#nav').node().appendChild(importedNode); 

I get this result with nested svg tags:

<svg id="nav">
<svg></svg>
<svg></svg>
<svg></svg>
<svg></svg>
</svg>


But...  I think I would rather just have the g tags from each doc.  I think I want to load each svg doc and then strip out the svg tags so I'm just left with g tags within one svg:

#2

<svg id="nav">
<g></g>
<g></g>
<g></g>
</svg>

After each svg loads, I can loop through each importedNode and log out the nested g tags but I can't seem to extract and append them to my single parent svg tag

var nodes = d3.select(importedNode).selectAll("g");

_.each(nodes, function(node) {
         console.log(node); <-- logs the g I want to append  
         d3.select("#nav").node().appendChild(node) <--- doesn't work - I'm thinking I may need to clone it or something... or recreate the g somehow?!
});

How would one append g fragment from a loaded svg doc ? Or is option #1 the better approach?

Brandon Flowers

unread,
Jul 26, 2013, 9:17:20 PM7/26/13
to d3...@googlegroups.com, mbos...@cs.stanford.edu
I came across this post from another developer apparently having a similar issue... he also demonstrates how you can append the whole svg but not a g 


"without <svg> the fragment created is an HTMLUnkownElement and for some reason we can't do a selection on the fragment"

is this true?

Brandon Flowers

unread,
Jul 27, 2013, 5:46:24 PM7/27/13
to d3...@googlegroups.com, mbos...@cs.stanford.edu
I did a bit more research and stumbled across this post by a developer who seems to think its just fine to nest SVG elements within one parent SVG

http://tutorials.jenkov.com/svg/svg-element.html  <-- quite a few good SVG examples on his site too 

I also discovered by accident [aka the console screamed at me] that the importedNode is an SVGSVGElement, obviously so nice...

Instead of using my looping underscore approach, I learned I could just do the following to change the color of all the nest g elements

var nodes = d3.select(importedNode).selectAll("g")

nodes.attr("fill", "#990000");

And then set the overall size of each svg like so:

importedNode.width.baseVal.value = 50;

importedNode.height.baseVal.value = 50;

This is basically what I was looking for when I found this thread ;-D  the ability to load SVG documents and then manipulate them. The SVGSVGElement mentions matrix transforms which seems pretty complex - does D3 offer something similar? easier?

Michael Neutze

unread,
Jul 28, 2013, 1:57:46 PM7/28/13
to d3...@googlegroups.com
On Wednesday, June 8, 2011 6:48:04 PM UTC+2, Chris Viau wrote:
You are right. But I was just wondering if an object tag was more appropriate to inline svg or iframe to load an external svg to be able to manipulate it with D3. As I understand, D3 typically generate inline svg (browser support). I will test to see if I can manipulate a svg in an image tag with d3 (browser support) But I don't think it will be difficult to play in an object tag with D3. But maybe more within an iframe.

I am using the object tag for precisely this matter (svg images can't be scripted), especially since I've a map geometry prepared as static svg, which I am more accustomed to than using topojson. I was also hoping to get a better caching behavior this way.

However browsers object tag implementation is still poor. There are some pretty annoying bugs in Safari, as webkit treats object tags as "plugin" content (imagine that in 2013!), see here for the respective issue

Also the onload sequence is more difficult to control. You can check out some real world examples in thematic mapping that use this approach


a more reduced example

Finally you should be aware of the following issue in IE9 ging this route 

Michael

Christophe Viau

unread,
Jul 28, 2013, 2:16:11 PM7/28/13
to d3...@googlegroups.com
What's the problem with scripting an inlined imported svg? http://jsfiddle.net/christopheviau/XnG6r/
Chris
--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/-qYDy71c_lA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Michael Neutze

unread,
Jul 28, 2013, 3:50:35 PM7/28/13
to d3...@googlegroups.com
there's none

Christophe Viau

unread,
Jul 28, 2013, 5:39:05 PM7/28/13
to d3...@googlegroups.com
Excellent examples BTW.
Chris

Marcus Crowley

unread,
Mar 2, 2014, 10:52:05 PM3/2/14
to d3...@googlegroups.com
I can't (yet) get <img src="whatever.svg"/> to load in Chrome. Neither can I get the <object> tag approach recommended at http://stackoverflow.com/questions/4476526/do-i-use-img-object-or-embed-for-svg-files to work. Is anyone else finding this?

The code proposed by Chris Viau (above) looks like a lot of hoops to jump through just to load an external SVG which you want to annotate interactively.

MC

Marcus Crowley

unread,
Mar 2, 2014, 11:27:53 PM3/2/14
to d3...@googlegroups.com
It's OK now. As I am on IIS, I needed to add SVG MIME type to the server. And to load from external SVG, I am using the rather straightforward:

var loader = d3.xml("data/map.svg", function(xml) {
  document.body.appendChild(xml.documentElement);
});

MC

nick

unread,
May 2, 2014, 2:50:11 PM5/2/14
to d3...@googlegroups.com
Hey folks, have some more embedded SVG weirdness to share with the group.

I've made this thing that tries to make using SVGs in reveal.js nicer: scaling to fit the screen, etc.
http://bl.ocks.org/bollwyvl/fe1d2806449487cdf88a

It uses iframes to avoid the colliding ids issues (sneaky svg:use), and viewBox to do the scaling stuff. Yuck. But it mostly works. But yuck.

Anyhow...

Chrome works like a charm, including printing.

Firefox works pretty well for my hand-made examples, but can't handle some "off-the-shelf" svg very well: the d3 logo doesn't show up at all, and the inkscape logo is really weak... just the black background. These things work great when brought in via img or background-image, but obviously i can't do anything with them, though i might end up doing that as a fallback for mozilla. sigh.

Here is the code of interest:
http://bl.ocks.org/bollwyvl/fe1d2806449487cdf88a#reveal-svg-fragment.js

And the specific fragment:
item.idoc.select("body")
  .append("div")
  .attr({width: "100%", height: "100%"})
  .style({position: "relative"})
  .node()
  .appendChild(document.adoptNode(xml.documentElement));
I've tried plain-old appendChild, importNode and all kinds of other things, and it has no impact on Firefox rendering.

Any insight?

cheers,
nick

nick

unread,
May 2, 2014, 11:14:12 PM5/2/14
to d3...@googlegroups.com
Doing some more digging, it appears that the problems I was seeing was due to Firefox's not being into anything involving references, such as gradients, svg:use, etc.

However, loading the SVG directly into the iframe fixes this in Firefox. I thought I had tried this earlier, but perhaps hadn't been doing the viewBox fix to get scaling to work.

Here's the current version
http://bl.ocks.org/bollwyvl/fe1d2806449487cdf88a/9692895eef9f7ac077819441cb199187ed4025aa

New fun thing: the default namespace I used throughout the examples (i.e. [*|label=foo]) doesn't work in Safari 5.1. I guess you can't fool all of the people all of the time.

Chris Viau

unread,
May 3, 2014, 11:37:00 AM5/3/14
to d3...@googlegroups.com
I see that you have multiple gradients with the same id and I know that it easily confuses browsers.
Chris

Arun Kumar

unread,
Aug 25, 2016, 10:24:27 AM8/25/16
to d3-js
Is it possible to access the dom of the svg if I add this way. Also I tried to add the svg but got this error: net::ERR_FILE_NOT_FOUND.

Arun Kumar

unread,
Aug 28, 2016, 10:15:06 AM8/28/16
to d3-js
Here is the function code that I used:

function add_part ()
{
d3.select("svg")
.append("object")
.attr("data", "/parts/IC-8pin.svg")
.attr("type", "image/svg+xml");
}

On Wednesday, 8 June 2011 21:19:11 UTC+5:30, Chris Viau wrote:

Seemant Kulleen

unread,
Aug 28, 2016, 2:38:17 PM8/28/16
to d3-js
Happy Sunday Arun,

Wondering if you've tried the technique in this block: http://bl.ocks.org/mbostock/1014829 ?

Cheers,
Seemant

--
Oakland Finish Up Weekend
Be Amazed.  Be Amazing.
Get Mentored | Get Inspired | Finish Up
http://oaklandfinishup.com


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Curran

unread,
Aug 29, 2016, 3:18:27 AM8/29/16
to d3-js
Hi Arun,

The net::ERR_FILE_NOT_FOUND error you are seeing is a result of using the file:// protocol, instead of using the http:// protocol. I would recommend to use a local HTTP server such as https://www.npmjs.com/package/http-server, then the relative path of the SVG should be resolved correctly.

Also, be aware that the leading "/" in the path here

.attr("data", "/parts/IC-8pin.svg")

will become the root of the domain (e.g. "http://localhost:8080/parts/IC-8pin.svg"). If you want to load the SVG relative to the HTML page path, you can leave out the leading "/" like this:

.attr("data", "parts/IC-8pin.svg")

Best regards,
Curran
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages