2

I am using a donut chart in order to present data from a static JSON query. I now want to make it responsive. I have made other D3 graphs responsive but it is different for this one.

The idea was first to calculate the distance between two corners of the screen (hypotenuse) in order to recalculate the radius of my shape. That could be useful for later. Snippet for this calculation is shown here:

console.log('width')
console.log(window.innerWidth);
console.log('height')
console.log(window.innerHeight);
console.log('final');
console.log(Math.sqrt(Math.pow(window.innerWidth,2) + Math.pow(window.innerHeight,2)));


$("#one").html(Math.sqrt(Math.pow(window.innerWidth,2) + Math.pow(window.innerHeight,2)));
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<div id="one"></div>

 

My main snippet is here and it is responsive but the box with the values (tooltip) is shown underneath the donut.

var margin = {top: 20, right: 20, bottom: 50, left: 100},
    width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
    height = parseInt(d3.select("#chart").style("width")) - margin.top - margin.bottom,
    r = 180,
    inner = 180/2,
    color= d3.scale.ordinal()
    .range(["#124", "#214183", "#3061c2",  "#4876d1", "#87a5e1", "#c5d4f1"]);
    
data = [{"label":"ONE", "value":194}, 
        {"label":"TWO", "value":567}, 
        {"label":"THREE", "value":1314},
        {"label":"FOUR", "value":793},
        {"label":"FIVE", "value":1929},
        {"label":"SIX", "value":1383}];

var total = d3.sum(data, function(d) {
    return d3.sum(d3.values(d));
});

 
 var vis = d3.select('#chart').append("svg:svg").data([data])
         .attr("width", '100%')
         .attr("height", '100%')
         .attr('viewBox',(-width / 2 ) + ' ' + (-height/2) + ' '+width +' '+height)
         .attr('preserveAspectRatio','xMinYMin')


var textTop = vis.append("text")
    .attr("dy", ".35em")
    .style("text-anchor", "middle")
    .attr("class", "textTop")
    .text( "TOTAL" )
    .attr("y", -10),
textBottom = vis.append("text")
    .attr("dy", ".35em")
    .style("text-anchor", "middle")
    .attr("class", "textBottom")
    .text(total.toFixed(2) + "m")
    .attr("y", 10);

var arc = d3.svg.arc()
    .innerRadius(inner)
    .outerRadius(r);

var arcOver = d3.svg.arc()
    .innerRadius(inner + 5)
    .outerRadius(r + 5);
 
var pie = d3.layout.pie()
    .value(function(d) { return d.value; });
 
var arcs = vis.selectAll("g.slice")
    .data(pie)
    .enter()
        .append("svg:g")
            .attr("class", "slice")
            .on("mouseover", function(d) {
                d3.select(this).select("path").transition()
                    .duration(200)
                    .attr("d", arcOver)
                
                textTop.text(d3.select(this).datum().data.label)
                    .attr("y", -10);
                textBottom.text(d3.select(this).datum().data.value.toFixed(2))
                    .attr("y", 10);
            })
            .on("mouseout", function(d) {
                d3.select(this).select("path").transition()
                    .duration(100)
                    .attr("d", arc);
                
                textTop.text( "TOTAL" )
                    .attr("y", -10);
                textBottom.text(total.toFixed(2) + "m");
            });

arcs.append("svg:path")
    .attr("fill", function(d, i) { return color(i); } )
    .attr("d", arc);

var legend = d3.select("#chart").append("svg")
    .attr("class", "legend")
    .attr("width", r)
    .attr("height", r * 2)
    .selectAll("g")
    .data(data)
    .enter().append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

legend.append("rect")
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", function(d, i) { return color(i); });

legend.append("text")
    .attr("x", 24)
    .attr("y", 9)
    .attr("dy", ".35em")
    .text(function(d) { return d.label; }); 
*{
  font-family: 'Roboto', sans-serif;
  text-transform:capitalize;
  margin: 0 auto;
  text-align:left;
}

body {
        font-family: "Roboto"!important;
        width: 100%;
        height: 400px;
        position: relative;
    }

 

.slice path {
    stroke: #fff;
    stroke-width: 1px;
}

.textTop {
    font-size: 12pt;
    fill: #bbb;
}

.textBottom {
    fill: #444;
    font-weight: bold;
    font-size: 18pt;
}

.top {
    border: 1px solid #bbb;
    color: #777;
    padding: 5px;
    text-decoration: none;
}

.top:hover {
    border: 1px solid #555;
    color: #333;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="chart"></div>

<svg id="chart2"></svg>

How can I include the tooltip (legend - not sure about the exact terminology) on the right side of the donut?

Datacrawler
  • 2,780
  • 8
  • 46
  • 100

1 Answers1

3

You can include the legend directly into you main svg by the following :

var svg = d3.select('#chart').append("svg:svg");
var vis = svg.data([data])
(...)

Rather than :

var vis = d3.select('#chart').append("svg:svg").data([data])
(...)

Then, you can add your legend to that svg, replacing :

var legend = d3.select("#chart").append("svg")

With :

var legend = svg.append("svg")

Finally, you'll have to deal with legend placement. Here is a working JSFiddle : https://jsfiddle.net/anh9Lr1e/4/ and a snippet in case the jsfiddle url expires:

var margin = {top: 20, right: 20, bottom: 50, left: 100},
    width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
    height = parseInt(d3.select("#chart").style("width")) - margin.top - margin.bottom,
    r = 180,
    inner = 180/2,
    color= d3.scale.ordinal()
    .range(["#124", "#214183", "#3061c2",  "#4876d1", "#87a5e1", "#c5d4f1"]);
    
data = [{"label":"ONE", "value":194}, 
        {"label":"TWO", "value":567}, 
        {"label":"THREE", "value":1314},
        {"label":"FOUR", "value":793},
        {"label":"FIVE", "value":1929},
        {"label":"SIX", "value":1383}];

var total = d3.sum(data, function(d) {
    return d3.sum(d3.values(d));
});

 
 var svg = d3.select('#chart').append("svg:svg")
    .attr("width", '100%')
        .attr("height", '100%')
        .attr('preserveAspectRatio','xMinYMin');
 var vis = svg.data([data])
         .attr("width", '100%')
         .attr("height", '100%')
         .attr('viewBox',(-width / 2 ) + ' ' + (-height/2) + ' '+width +' '+height)
         .attr('preserveAspectRatio','xMinYMin')


var textTop = vis.append("text")
    .attr("dy", ".35em")
    .style("text-anchor", "middle")
    .attr("class", "textTop")
    .text( "TOTAL" )
    .attr("y", -10),
textBottom = vis.append("text")
    .attr("dy", ".35em")
    .style("text-anchor", "middle")
    .attr("class", "textBottom")
    .text(total.toFixed(2) + "m")
    .attr("y", 10);

var arc = d3.svg.arc()
    .innerRadius(inner)
    .outerRadius(r);

var arcOver = d3.svg.arc()
    .innerRadius(inner + 5)
    .outerRadius(r + 5);
 
var pie = d3.layout.pie()
    .value(function(d) { return d.value; });
 
var arcs = vis.selectAll("g.slice")
    .data(pie)
    .enter()
        .append("svg:g")
            .attr("class", "slice")
            .on("mouseover", function(d) {
                d3.select(this).select("path").transition()
                    .duration(200)
                    .attr("d", arcOver)
                
                textTop.text(d3.select(this).datum().data.label)
                    .attr("y", -10);
                textBottom.text(d3.select(this).datum().data.value.toFixed(2))
                    .attr("y", 10);
            })
            .on("mouseout", function(d) {
                d3.select(this).select("path").transition()
                    .duration(100)
                    .attr("d", arc);
                
                textTop.text( "TOTAL" )
                    .attr("y", -10);
                textBottom.text(total.toFixed(2) + "m");
            });

arcs.append("svg:path")
    .attr("fill", function(d, i) { return color(i); } )
    .attr("d", arc);

var legend = svg.append("svg")
    .attr("class", "legend")
    //.attr("width", r)
    //.attr("height", r * 2)
    .selectAll("g")
    .data(data)
    .enter().append("g")
    .attr("transform", function(d, i) { return "translate(" + (r + 20) + "," + i * 20 + ")"; });

legend.append("rect")
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", function(d, i) { return color(i); });

legend.append("text")
    .attr("x", 24)
    .attr("y", 9)
    .attr("dy", ".35em")
    .text(function(d) { return d.label; }); 
*{
  font-family: 'Roboto', sans-serif;
  text-transform:capitalize;
  margin: 0 auto;
  text-align:left;
}

body {
        font-family: "Roboto"!important;
        width: 100%;
        height: 400px;
        position: relative;
    }

 

.slice path {
    stroke: #fff;
    stroke-width: 1px;
}

.textTop {
    font-size: 12pt;
    fill: #bbb;
}

.textBottom {
    fill: #444;
    font-weight: bold;
    font-size: 18pt;
}

.top {
    border: 1px solid #bbb;
    color: #777;
    padding: 5px;
    text-decoration: none;
}

.top:hover {
    border: 1px solid #555;
    color: #333;
}
 
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="chart"></div>
 
S. Bureau
  • 146
  • 1
  • 6
  • Actually, the chart is not 100% responsive. When in an iframe, it does not resize when I resize the browser window. – Datacrawler Sep 19 '18 at 10:19
  • Hum, responsiveness has always been quite tricky with D3. I've edited the snippet ;-) – S. Bureau Sep 19 '18 at 10:24
  • It needs a resize function for every window size change. I tried using (after altering) the same function just like in my previous question but it did not work for the donut chart. I should raised another question maybe. – Datacrawler Sep 19 '18 at 10:25
  • Yes, 2 options, first as you mentionned is a resize function (may be the best option to refresh only desired elements), second is to specify "preserveAspectRatio" on main svg element... But this will make the legend become bigger as you expand your window... – S. Bureau Sep 19 '18 at 10:27
  • hi is there any way to just add stroke to inner surface of arcs so that it looks like the inner circle has a border of say blue – akshay kishore Jan 10 '19 at 12:41
  • Hi, I would recommand to follow this answer to do the job : https://stackoverflow.com/a/20272071/10380199 – S. Bureau Jan 10 '19 at 13:16