Display multiple d3.js charts in a single html page

27,444

Solution 1

There's no problem at all using multiple SVGs on the same page. Here's an example:

var svg1 = d3.select("#svg1");
svg1.append("circle")
       .attr("cx",100)
       .attr("cy", 100)
       .attr("r", 90)
       .attr("fill", "red");
var svg2 = d3.select("#svg2");
svg2.append("circle")
       .attr("cx",100)
       .attr("cy", 100)
       .attr("r", 90)
       .attr("fill", "blue");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="200" height="200" id="svg1"></svg>
<svg width="200" height="200" id="svg2"></svg>

Solution 2

There is no need for repeating all the code, as you're doing right now. Don't repeat yourself.

An easy alternative is wrapping all your D3 code in a function that has two parameters, selector and url:

function draw(selector, url){
    //code here
};

Then, inside that function draw, you set the position of your SVG:

var svg = d3.select(selector).append("svg")...

And the URL you get the data:

d3.json(ulr, function(error, root) {...

After that, just call the draw function twice, with different arguments:

draw(selector1, url1);
draw(selector2, url2);

Here is a demo, read it carefully to see how it works:

draw("#svg1", "#data1");
draw("#svg2", "#data2");

function draw(selector, url){

var data = d3.csvParse(d3.select(url).text())

var width = 500,
    height = 150;

var svg = d3.select(selector)
    .append("svg")
    .attr("width", width)
    .attr("height", height);

var xScale = d3.scalePoint()
    .domain(data.map(function(d) {
        return d.name
    }))
    .range([50, width - 50])
    .padding(0.5);

var yScale = d3.scaleLinear()
    .domain([0, d3.max(data, function(d) {
        return d.value
    }) * 1.1])
    .range([height - 20, 6]);

var line = d3.line()
	.x(function(d){ return xScale(d.name)})
	.y(function(d){ return yScale(d.value)});
	
svg.append("path")
	.attr("d", line(data))
	.attr("stroke", "teal")
	.attr("stroke-width", "2")
	.attr("fill", "none");

var xAxis = d3.axisBottom(xScale);
var yAxis = d3.axisLeft(yScale);

svg.append("g").attr("transform", "translate(0,130)")
    .attr("class", "xAxis")
    .call(xAxis);

svg.append("g")
    .attr("transform", "translate(50,0)")
    .attr("class", "yAxis")
    .call(yAxis);

}
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div>First SVG</div>
<div id="svg1"></div>
<div>Second SVG</div>
<div id="svg2"></div>
<pre id="data1">name,value
foo,8
bar,1
baz,7
foobar,9
foobaz,4</pre>
<pre id="data2">name,value
foo,1
bar,2
baz,3
foobar,9
foobaz,8</pre>
Share:
27,444
kingmakerking
Author by

kingmakerking

Updated on July 05, 2022

Comments

  • kingmakerking
    kingmakerking almost 2 years

    I have the the d3.js code which is pasted here.

    I am trying to display more than one graphs in the same page. Though the d3.js code is same. Say one from data1.json and the other from data2.json. Following is the snippet which is bothering me.

    <svg width="960" height="960"></svg>
    
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script>
    var svg2 = d3.select("svg"),
        margin = 20,
        diameter = +svg2.attr("width"),
        g = svg2.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
    

    As per different answers in SO here, here, here, here or here, the solution seems to be one of the following:

    • Use different variable name to hold svgs such as svg1, svg2.. etc.. which I have done.
    • Use a method as described here.

         var chart1 = d3.select("#area1")
             .append("svg")
      

    Method two is not working for me, as it shows blank page.

    How to resolve this. I am sure that I am not getting the syntax correctly.

  • kingmakerking
    kingmakerking over 7 years
    Ok. I get it! But how do I convert var svg2 = d3.select("svg"), margin = 20, diameter = +svg2.attr("width"), g = svg2.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")"); to similar format.
  • r3mainer
    r3mainer over 7 years
    d3.select("svg") selects every SVG object in the DOM. You don't want to do that. Instead, give each SVG a unique id attribute (svg1 and svg2 in my example), and use those to refer to the SVG objects individually with d3.select("#svg1") and d3.select("#svg2").
  • kingmakerking
    kingmakerking over 7 years
    pastebin.com/evFYrn1f Still not working. I feel I am missing some silly syntax error.
  • r3mainer
    r3mainer over 7 years
    @kingmakerking It's d3.select("#svg1"), not d3.select("svg1").
  • Gerardo Furtado
    Gerardo Furtado over 7 years
    @squeamishossifrage you said "d3.select("svg") selects every SVG object in the DOM". No, it doesn't. That code selects only one SVG, which is the first one in the DOM.
  • r3mainer
    r3mainer over 7 years
    @GerardoFurtado Ah, you're right. I was thinking jQuery. Thanks for pointing that out.
  • Gerardo Furtado
    Gerardo Furtado over 7 years
    No worries. In d3 three is select and selectAll. The latter would do what you said in your comment.
  • RobertMyles
    RobertMyles over 6 years
    Buggeringly good answer, squeamish! Helped one out of a jiffy, I do say.
  • JdeMello
    JdeMello over 5 years
    Olá @Gerardo_Furtardo. Could you point me to examples with different kind of graphs in a single page (e.g. bar plots, scatter plots, lines...)? I am working on a project with multiple graphs in a page, it gets messy quickly. Any tips on this? Thanks!
  • Gerardo Furtado
    Gerardo Furtado over 5 years
    Olá @JdeMello, the easiest approach is encapsulating each code in a separate function, which selects a given container in the page., as I explain here: codereview.stackexchange.com/a/194948/143592