smcs
smcs

Reputation: 2004

D3: How to append a pre-loaded svg?

in my D3 code, I'm using external SVGs to represent data points. I can embed them like this:

group.append("svg:image")
  .attr("xlink:href","https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg")

However, they are displayed on mouseover and need a short time to load, so I'd like to preload them at the start of the script:

var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.setAttribute("xlink:href", "https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg");

I haven't been able to append that object to a group element with D3 though. I've tried group.append(icon) as well as giving the object an id and then saying group.append("#id"). Is there a way? Sample code below:

var icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
icon.setAttribute("xlink:href", "https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg");
icon.setAttribute("id","icon");

var width = 400;
var height = 200;
var svg = d3.select("body")
	.append("svg");
svg.attr("width", width)
	.attr("height", height);
group = svg.append("g");

group.append("svg:image")
	.attr("x",0)
  .attr("y",0)
  .attr("xlink:href","https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg")
  .attr("height",200)
  .attr("width",200);
  
/*group.append(icon)
	.attr("x",0)
  .attr("y",0)
  .attr("height",200)
  .attr("width",200);*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 1

Views: 584

Answers (2)

altocumulus
altocumulus

Reputation: 21578

There are two issues with your code:

  1. The first one has already been addressed by Robert Longson's answer. You have to adjust the newly created element's type to image and set the namespace argument when setting the attribute on this element.

    var icon = document.createElementNS("http://www.w3.org/2000/svg", "image");
    icon.setAttributeNS('http://www.w3.org/1999/xlink',
      "xlink:href",
      "https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg");
    
  2. When appending the element to the DOM using D3's selection.append() you have to provide a callback which returns the element to be appended. From the docs:

    selection.append(name)

    The name may be specified either as a constant string or as a function that returns the DOM element to append. [...] To append an arbitrary element based on the bound data it must be created in the function.

    Of course, it doesn't need to be created in the function, but it must be returned by the function, no matter where it was created in the first place. In your code this requires

    group.append(function() { return icon; })
    

var icon = document.createElementNS("http://www.w3.org/2000/svg", "image");
icon.setAttributeNS('http://www.w3.org/1999/xlink', "xlink:href", "https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg");
icon.setAttribute("id","icon");

var width = 400;
var height = 200;
var svg = d3.select("body")
	.append("svg");
svg.attr("width", width)
	.attr("height", height);
group = svg.append("g");

/*
group.append("svg:image")
  .attr("x",0)
  .attr("y",0)
  .attr("xlink:href","https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg")
  .attr("height",200)
  .attr("width",200);
*/  

group.append(function() { return icon; })
  .attr("x",0)
  .attr("y",0)
  .attr("height",200)
  .attr("width",200);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 2

Robert Longson
Robert Longson

Reputation: 124249

You need to fix your loading bugs with namespaces and elements

var icon = document.createElementNS('http://www.w3.org/2000/svg', 'image');
icon.setAttributeNS('http://www.w3.org/1999/xlink', "xlink:href", "https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg");

then you can do this

group.append("svg:image")
    .attr("x",0)
  .attr("y",0)
  .attr("xlink:href", icon.getAttributeNS('http://www.w3.org/1999/xlink', "href"))
  .attr("height",200)
  .attr("width",200);

Alternatively you could store the images in Javascript image elements, the result would be the same. I.e.

var icon = new Image();
icon.src = "https://upload.wikimedia.org/wikipedia/commons/a/ac/SVG-Logo2.svg"

and set .attr("xlink:href", icon.src)

Upvotes: 3

Related Questions