Loading external svg files and modifying their attributes in D3

2,370 views
Skip to first unread message

Federicopvs

unread,
Feb 16, 2016, 10:38:55 AM2/16/16
to d3-js

Hello everyone,


I am new to the group and I wanted to ask a question regarding an issue I am facing with D3.js and external svg files. I hope this is the right place for such questions.


I am working on this pie-chart (gist). When one clicks on a path (i.e. the pie's wedges), the div on the right shows an explanation of that function (sorry for the text being all in Italian).

Despite the fact that some icons are missing, this is how I managed to load external svg files.

First, I create a div that contains the image (i.e. svg file), the function's title, and a text that describes the function.


HTML
 <div class="description" id="funzioni">
   
<object id="descrImg" data="" type="image/svg+xml" ></object>
   
<p><strong><span id="descrTitle">example title</strong></span></p>
   
<p><span id="descrText">example description text.<span></p>
 
</div>

Second, I made an event listener that changes the div's title, image and description according to the info contained in data.json when a path is clicked.


d3.selectAll("path").on("click", function (d) {
 
//update tooltip title
 d3
.select("#descrTitle")
 
.text(d.data.descrTitle)
 
//update tooltip text
 d3
.select("#descrText")
 
.html(d.data.descrText)
 
//update tooltip image
 d3
.select("#descrImg")
 
.attr("data", function () {return d.data.descrImg;})
 
})
 
}


Images load correctly. When I inspect the element this is the structure I see:


<object>
   
<svg>
     
<circle>
       
<path></path>
     
</circle>
   
</svg>
 
</object>



The next step would be to change the path's `fill` attribute to white, and the circle's `fill` attribute with the function that assigns colors to the pie's wedges, that is:


var path = chart.datum(dataset).selectAll("path")
           
.data(pie)
 
                      .enter()
                       .append("path")
                       .attr("fill", function (d, i) {      //this is the function
                       var scale = colorMap[d.data.categoria];
                       if (scale) return scale(d.data.catIndex)
                       })
                       .style("fill-opacity", 0.75)
                       .attr("d", arc)
                       .each(function (d) { this._current = d; });
                          }




Alternatively, I could make a function that copies the fill attribute from the corresponding wedge (any idea?).


However, before I can do that, I need to "access" the the `circle` and `path` contained in the `object`. How can I do that?


In the console, I tried:


d3.select("#descrImg") //selects the object
 
.select("svg")
 
.select("circle")
 
.attr("fill", "black")



Which doesn't work. However, if I use the inspector to change circle's or path's fill attribute it works fine.

What am I doing wrong?


Hope someone can help.


Best,


Federico

Seemant Kulleen

unread,
Feb 16, 2016, 12:47:48 PM2/16/16
to d3-js
Hi Federico,

Do you get a similar result when setting .style("fill", "black") (instead of .attr)?

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+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Federicopvs

unread,
Feb 17, 2016, 6:27:28 AM2/17/16
to d3-js

Hi Seemant,
Thanks for your answer but unfortunately that does not work either.
I actually made a screen of my inspector to show what it looks like (above).

The hierarchy is actually
  • object
    • #document
      • svg
        • circle
          • path
Perhaps that's why I cannot modify the circle (or path) through the following?

d3.select("#descrImg").select("svg").select("circle").select("path")
Note: "#descrImg" is the object's ID.

I tried to add .select("#document") after the first select but it does not work either.

Federico

b.j.k...@utwente.nl

unread,
Feb 17, 2016, 8:04:35 AM2/17/16
to d3...@googlegroups.com
Any reason you add the svg within an object, and not as an <svg> straight-away...?
Also, I sem to see a superfluous </svg>...

Barend 

Pierre Massat

unread,
Feb 17, 2016, 9:00:00 AM2/17/16
to d3-js
Hi,

I don't if that helps, but I just realized that it was possible to load svg elements from an external text file.
Say you have a text file containing this :
<circle r="50" cy="100" cx="250"></circle>

Provided that you have already put an svg container somewhere called "svg", you can do :
d3.text("logo-text.txt", function(txt){
            svg.html(txt);
        });

I don't know whether that's a proper way to do it, but it seems to work.

Cheers,

Pierre.

Federicopvs

unread,
Feb 18, 2016, 11:52:26 AM2/18/16
to d3-js
That was the only method that worked for me.
Alternatively, I tried to follow this tutorial.

Accordingly, my code becomes:

descrImgURI = d.data.descrImg;
d3
.xml(descrImgURI, "image/svg+xml", function(error, xml) {
 
if (error) throw error;
  document
.body.appendChild(xml.documentElement);
});

With this I can append the svg image to the body. However I need to append it (or actually "prepend", but I can work around that) to a specific div.
Moreover, I do not know how to refer to it in order to modify its attributes dynamically, i.e. change the svg's "fill" attribute according to a dynamic color() function I previously created.

This is what I tried

d3.xml(descrImgURI, "image/svg+xml", function(error, xml) {
 
if (error) throw error;
  document
.getElementById("#myDiv").appendChild(xml.documentElement);
xml
.documentElement.attr("fill", "red")
});

Which returns:

"TypeError: document.getElementById(...) is null"

And I am pretty sure the last line wouldn't work either.

Any ideas?

Federicopvs

unread,
Feb 18, 2016, 12:29:01 PM2/18/16
to d3-js
I am sorry for the double-post, but I actually manage to get some further results shortly after posting my previous answer.

        descrImg = d3.select("#descrImg"); //creates a variable pointing to the Div where I want the image to appear
        descrImg
.select("svg").remove(); //removes previous image, if any
        descrImgURI
= d.data.descrImg; //takes the image URI from the data

        d3
.xml(descrImgURI, "image/svg+xml", function(error, xml) {
               
if (error) throw error;

               
var svg= xml.documentElement;
                descrImg
.node().appendChild(svg);
                svg
.style.backgroundColor= "red";
             
});

The last line changes the background of my svg to red, while I want to change the fill of the circle and of the path within my svg. For example purpose, I want the circle fill to be red and the path's fill to be white.

To give you an idea, I attached some files:
- svg1.png is how my svg looks now
- svg2.png is how I want it to be
- svg3.png is a screen-shot of the inspector

Cheers,

Federico
svg1.png
svg2.png
svg3.png
Reply all
Reply to author
Forward
0 new messages