Reputation: 2101
I have a D3 bar chart and I'm trying to resize its width according to the browser window size. Here is the chart script where the width variable is being set according to the SVG's parent div's width (#chart1), which has a percentage width set in the css:
var $chartWidth= $('#chart1').width();
var margin = {top:20, right:35, bottom:30, left:35};
var height = 180 - margin.top - margin.bottom;
var width = $chartWidth - margin.left - margin.right;
var parseDate = d3.time.format("%m/%Y").parse;
var yScaleBar = d3.scale.linear()
.domain([0,100])
.range([height, 0]);
var xBarScale = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var yAxisBar = d3.svg.axis()
.scale(yScaleBar)
.ticks(4)
.tickSize(-width, 0, 0)
.orient("left");
var xBarAxis = d3.svg.axis()
.scale(xBarScale)
.ticks(4)
.orient("bottom");
var canvasBars = d3.select("#chart1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate("+margin.left+","+margin.top+")");
var data = [
{date:"01/2009",bar2:"50",bar:"10",q:"1Q 2009"},
{date:"04/2009",bar2:"56",bar:"32",q:"2Q"},
{date:"07/2009",bar2:"57",bar:"70",q:"3Q"},
{date:"10/2009",bar2:"58",bar:"60",q:"4Q"},
{date:"01/2010",bar2:"52",bar:"45",q:"1Q '10"}
];
data.forEach(function(d) {
d.date = parseDate(d.date);
d.bar2 = +d.bar2;
d.bar = +d.bar;
});
xBarScale.domain(d3.range(data.length));
xBarAxis.tickValues([data[0].q,data[1].q,data[2].q,data[3].q,data[4].q]);
canvasBars.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + height + ")")
.call(xBarAxis);
canvasBars.append("g")
.attr("class", "yaxis")
.call(yAxisBar);
canvasBars.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr({
"x": function(d) {return xBarScale(d.date);},
"y": function(d) {return yScaleBar(d.bar);},
"height": function(d) {return height -yScaleBar(d.bar);},
"width": xBarScale.rangeBand(),
"fill": "steelblue"
});
When the window resizes, the width variable changes and calls resize();
$( window ).resize(function() {
$chartWidth = $('#chart1').width();
width = $chartWidth - margin.left - margin.right;
resize();
});
Then my resize function should change the width attribute of the SVG, the width of the yAxisBar tick marks, and the xBarScale rangeRoundBands..... I then think I need to select all the bars (rects) and change their width attributes according the to the new xBarScale.rangeRoundBands. but I'm not sure how to correctly select these 3 things. So far, just the bar width's resize along with the window - but not the tick marks or the SVG. Here is what I've tried:
function resize(){
canvasBars.attr("width", width + margin.left + margin.right);
yAxisBar.tickSize(-width, 0, 0);
xBarScale.rangeRoundBands([0, width], .1);
canvasBars.selectAll("rect").attr("width", xBarScale.rangeBand());
}
Upvotes: 1
Views: 5567
Reputation: 101
There is a much easier way of doing this in D3 You can make the chart resize using a combination of viewBox and preserveAspectRatio attributes on the SVG element.
const svg = d3
.select('div#chart-container')
.append('svg')
.style(
'padding',
marginRatio.top +
' ' +
marginRatio.right +
' ' +
marginRatio.bottom +
' ' +
marginRatio.left +
' '
)
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr(
'viewBox',
'0 0 ' +
(width + margin.left + margin.right) +
' ' +
(height + margin.top + margin.bottom)
)
See here for reference: https://eddyerburgh.me/create-responsive-bar-chart-d3-js
Upvotes: 0
Reputation: 67
There is a much better way to do this with the Bootstrap 3 framework. I'm already implementing that for a website so I can show you how I do it for me and maybe you can adapt it to your specific needs.
HTML code
<div class="container>
<div class="row">
<div class='col-sm-6 col-md-4' id="month-view" style="height:345px;">
<div id ="responsivetext">Something to write</div>
</div>
</div>
</div>
I have set up a fixed height because of my needs, but you can leave the size auto as well. The "col-sm-6 col-md-4" makes the div responsive. You can learn more at http://getbootstrap.com/css/#grid-example-basic
We can access the graph with the help of the id month-view which can be seen below in the following code implemented with d3.js.
doing the magic with d3:
var visualize = function(data){
// Initialize and select div, get the width and height that you want to use for the bar
var width = document.getElementById('month-view').offsetWidth;
var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;
var total_map = data.map(function (i){ return parseInt(i.total);});
var heightScale = d3.scale.linear()
.domain([0, d3.max(total_map)])
.range([0, height-5]);
var x = d3.scale.ordinal()
.rangeRoundBands([0, width-30], .1);
var canvas = d3.select("#month-view").append("svg")
.attr("width", width - 13)
.attr("height", height + 20)
.append("g")
.attr("transform","translate(5,-20)");
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Add first bar (total)
canvas.selectAll(".bar2")
.data(data)
.enter()
.append('rect')
.attr('width', width/12 - 5)
.attr('height', function (d){return heightScale(d.total)})
.attr('x', function (d, i) {return i * (width/12 -3 )})
.attr('y', function (d) {return height + 10 - heightScale(d.total)})
.attr('class', 'bar2')
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .95);
div.html(monthNames(d.month) + "<br/>" + d.total.toFixed(0)+" kr total<br/> <span class='green'>"+ ( (d.total * d.eco)/100 ).toFixed(0) + " kr eko</span>")
.style("left", (d3.event.pageX - 45) + "px")
.style("top", (d3.event.pageY - 55) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
// Add second bar (money)
canvas.selectAll(".bar")
.data(data)
.enter()
.append('rect')
.attr('width', width/12 - 5)
.attr('height', function (d){ return heightScale(( (d.total * d.eco)/100).toFixed(0))})
.attr('x', function (d, i) {return i * (width/12 -3 )})
.attr('y', function (d) {return height + 10 - heightScale(( (d.total * d.eco)/100).toFixed(0) )})
.attr('class', 'bar')
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .95);
div.html(monthNames(d.month) + "<br/>" + d.total.toFixed(0)+" kr total<br/> <span class='green'>"+ ( (d.total * d.eco)/100).toFixed(0) + " kr eko</span>")
.style("left", (d3.event.pageX - 45) + "px")
.style("top", (d3.event.pageY - 55) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
canvas.append("g")
.attr("transform", "translate(0,0)");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
x.domain(data.map(function(d) { return monthNames(d.month).substring(0, 3); }));
}
Since you already are using d3, I won't spend the time to explain everything that I've used in this code, but it's a working code at least. What you need is the part:
var width = document.getElementById('month-view').offsetWidth;
var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;
The width is set by getting the width of the div with the id month-view.
The height in my case should not include the entire area. I also have some text above the bar so I need to calculate that area as well. That's why I identified the area of the text with the id responsivetext. For calculating the allowed height of the bar, I subtracted the height of the text from the height of the div.
This allows you to have a bar that will adopt all the different screen/div sizes.
Upvotes: 1
Reputation: 10588
The most straight forward way of doing it would be to just set the width
of your element to 100% in the css.
Upvotes: 1