Reputation: 2876
In order to have a responsive D3 multi-line chart I have added a resize function but it doesn't seem to work although the function gets called:
var data = [{
Date: "2016-10-10",
ValueOne: 1,
ValueTwo: 0
}, {
Date: "2016-10-17",
ValueOne: 23,
ValueTwo: 2
}, {
Date: "2016-10-24",
ValueOne: 32,
ValueTwo: 17
}, {
Date: "2016-10-31",
ValueOne: 57,
ValueTwo: 40
}, {
Date: "2016-11-07",
ValueOne: 74,
ValueTwo: 56
}];
var margin = {top: 10, right: 50, bottom: 100, left: 50},
// Set default width and height
widther = (window.innerWidth),
width = widther - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Determine current size, which determines vars
function set_vars() {
var width = window.innerWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
}
function drawGraphic() {
var svg = d3.select('#charts')
.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 + ")");
//Parses date for correct time format
var formatTime = d3.timeFormat("%Y-%m-%d");
data.forEach(function(d) {
d.Date = new Date(d.Date)
});
var valueOneData = data.map(function(d) {
return {
date: d.Date,
value: d.ValueOne
}
});
var valueTwoData = data.map(function(d) {
return {
date: d.Date,
value: d.ValueTwo
}
});
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.Date
}));
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return d.ValueOne
}) * 1.05]);
var lineGenerator = d3.line()
.x(function(d) {
return xScale(d.date)
})
.y(function(d) {
return yScale(d.value)
});
var gX = svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale).tickFormat(function(d) {
return formatTime(d)
}).tickValues(data.map(function(d) {
return d.Date
})))
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", "rotate(-65)")
.attr("y", 4)
.attr("x", -10)
.attr("dy", ".35em");
var gY = svg.append("g")
.call(d3.axisLeft(yScale));
var valueOneLine = svg.append("path")
.datum(valueOneData)
.attr("d", lineGenerator)
.style("fill", "none")
.style("stroke", "#124");
var valueTwoLine = svg.append("path")
.datum(valueTwoData)
.attr("d", lineGenerator)
.style("fill", "none")
.style("stroke", "#c7003b");
//RESPONSIVENESS ATTEMPT NO1
d3.select(window).on("resize", resized);
}
//Resize function
function resized() {
d3.select("svg").remove();
set_vars();
drawGraphic();
console.log("FUNCTION IS BEING CALLED")
}
set_vars();
drawGraphic();
//RESPONSIVENESS ATTEMPT NO2
window.addEventListener("resize", function(){ d3.select("svg").remove(); set_vars(); drawGraphic(); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="charts"></div>
In the snippet, I have tried two ways to do that. None of them make the chart recreate from scratch. The same issue applied in this jsfiddle.
Upvotes: 0
Views: 555
Reputation: 5660
The issue was with the parsing of the data every time the window was resized.
As the date within the data is parsed the first time, calling parseDate(d.date)
will fail on every other call as it's already been parsed to a valid date. Do you get it?
Hence, moving the parsing code so that it's executed just once:
// parse data just once
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
Fiddle link: https://jsfiddle.net/a5rqt0L1/
Suggestion: I feel this isn't the right way to make a responsive chart i.e. removing SVG and re-appending to the body with all the configuration done multiple times. Here's how I'd do it:
drawBars
(to draw the actual bars) to a separate function that will use d3's own enter, exit and update selection logic..call(xAxis)...
and just call the drawBars
function. Hope this helps.
Upvotes: 1