Reputation: 1187
page.html:
<!DOCTYPE html>
<!-- https://bl.ocks.org/mbostock/3886208 -->
<style>
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
.legend {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60%;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
g[class="col_1"] rect:hover {
fill:#80061b;
}
g[class="col_2"] rect:hover {
fill:#008394;
}
</style>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
<body>
<div id="tooltip" class="hidden">
<p><strong>Month: </strong><span id="month"></span><p>
<p><strong>Value: </strong><span id="count"></span></p>
</div>
<script>
var margin = {top: 20, right: 20, bottom: 50, left: 80},
width = 1300 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// parse the date / time
// look at the .csv in Notepad! DO NOT LOOK AT EXCEL!
var parseDate = d3.timeParse("%m/%d/%Y");
var x = d3.scaleTime()
.range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal()
.range(["#CE1126", "#00B6D0"]); // red and blue
var xMonthAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat("%b")); // label every month
var xYearAxis = d3.axisBottom(x)
.ticks(d3.timeYear.every(1))
.tickFormat(d3.timeFormat("%Y")); // label every year
var yAxis = d3.axisLeft(y).tickFormat(d3.format('.2s'));
var formatNum = d3.format(",")
// load .csv file
d3.csv("test_data.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data){
if (error) throw error;
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var keys = data.columns.slice(1);
var barWidth = (width - margin.right- margin.left)/(data.length+1);
data.sort(function(a, b) { return b.date - a.date; });
x.domain(d3.extent( data, function(d){ return d.date }) );
var max = x.domain()[1];
var min = x.domain()[0];
var datePlusOneMonth = d3.timeDay.offset(d3.timeMonth.offset(max, 1), -1); // last day of current month: move up one month, back one day
x.domain([min,datePlusOneMonth]);
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
// the bars
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr('class', function(d) { return d.key; })
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.data.date); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", barWidth)
.on("mousemove", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.mouse(this)[0]) + 88;
var yPosition = parseFloat(d3.mouse(this)[1]) - 29;
var value = d.data[d3.select(this.parentNode).attr('class')]; // differentiating between col1 and col2 values
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#count")
.text(formatNum(value)); // return the value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#month")
.text(d3.timeFormat("%B %Y")(d.data.date)); // return the value
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// x-axis
var monthAxis = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xMonthAxis);
const firstDataYear = x.domain()[0];
xYearAxis.tickValues([firstDataYear].concat(x.ticks()));
var yearAxis = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (height + 25) + ")")
.call(xYearAxis);
var valueAxis = g.append("g")
.attr("class", "axis")
.call(yAxis);
monthAxis.selectAll("g").select("text")
.attr("transform","translate(" + barWidth/2 + ",0)");
var options = d3.keys(data[0]).filter(function(key) { return key !== "date"; }).reverse();
var legend = svg.selectAll(".legend")
.data(options.slice().filter(function(type){ return type != "total"}))
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", z);
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return capitalizeFirstLetter(d); });
});
</script>
</body>
test_data.csv:
date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575
Output:
Desired Output:
I would like to insert a slider above this image outlining the number of months that will be shown in the graph above, something like so:
The user needs to be able to select an integer number of months between 1 and 24 inclusive using the slider. The latest month (in this case, Nov 2017
) should always be shown. The slider will show only the number of months prior to the latest month as given by the value selected from the slider. (E.g., if 1
is selected from the slider, the graph will only consist of Oct 2017
and Nov 2017
.)
I have already tried playing around with the code at https://bl.ocks.org/officeofjane/9b9e606e9876e34385cc4aeab188ed73, but this doesn't quite meet my needs for the following reason: the slider values and those in the data don't necessarily correspond for filtering (i.e., number of months vs. dates to be filtered). I do understand the basic idea of what I'll need to have the bar chart work with the slider, though - having to work with the exit
and enter
selections.
I don't need a complete solution, but any guidance on how to start on this would be appreciated.
Upvotes: 2
Views: 2217
Reputation: 5660
Okay. So here's a code snippet with a simple jQuery slider (as I suggested in the comments) that filters out data for the last 24 months. To show the initial data i.e. for the original data, the slider has to be set to 0 (couldn't find any other way to do that). You can play around with the slider code.
var dataAsCsv = `date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575`;
// Initialize jQuery slider
$('div#month-slider').slider({
min: 0,
max: 24,
create: function() {
// add value to the handle on slider creation
$(this).find('.ui-slider-handle').html($( this ).slider( "value" ));
},
slide: function(e, ui) {
// change values on slider handle and label based on slider value
$('div.slider-container span.value').html(ui.value);
$(e.target).find('.ui-slider-handle').html(ui.value);
// calculate offset date based on slider value
var offsetDate = ui.value ? d3.timeMonth.offset(datePlusOneMonth, -ui.value) : min;
// set x domain and re-render xAxis
x.domain([offsetDate, datePlusOneMonth]);
g.select('.x.axis').call(xMonthAxis);
g.select('.yearaxis.axis').call(xYearAxis);
// calcuate filtered data based on new offset date, set y axis domain and re-render y axis
var filteredData = data.filter(function(d) { return d.date >= offsetDate; });
y.domain([0, d3.max(filteredData, function(d) { return d.total; })]).nice();
g.select('.y.axis').transition().duration(200).call(yAxis);
// re-render the bars based on new filtered data
// the bars
var bars = g.select("g.bars")
.selectAll("g")
.data(d3.stack().keys(keys)(filteredData));
var barRects = bars.enter().append("g").merge(bars)
.attr('class', function(d) { return d.key; })
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; });
barRects.exit().remove();
barRects.enter()
.append("rect");
barWidth = (width - margin.right- margin.left)/(filteredData.length+1);
g.select("g.bars").selectAll('g rect')
.attr("x", function(d) { return x(d.data.date); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", barWidth)
.on("mousemove", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.mouse(this)[0]) + 88;
var yPosition = parseFloat(d3.mouse(this)[1]) - 29;
var value = d.data[d3.select(this.parentNode).attr('class')]; // differentiating between col1 and col2 values
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#count")
.text(formatNum(value)); // return the value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#month")
.text(d3.timeFormat("%B %Y")(d.data.date)); // return the value
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
}
})
var margin = {top: 20, right: 20, bottom: 50, left: 80},
width = 1300 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// parse the date / time
// look at the .csv in Notepad! DO NOT LOOK AT EXCEL!
var parseDate = d3.timeParse("%m/%d/%Y");
var x = d3.scaleTime()
.range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal()
.range(["#CE1126", "#00B6D0"]); // red and blue
var xMonthAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat("%b")); // label every month
var xYearAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(6))
.tickFormat(d3.timeFormat("%Y")); // label every year
var yAxis = d3.axisLeft(y).tickFormat(d3.format('.2s'));
var formatNum = d3.format(",")
var data = d3.csvParse(dataAsCsv, function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
});
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var keys = data.columns.slice(1);
var barWidth = (width - margin.right- margin.left)/(data.length+1);
data.sort(function(a, b) { return b.date - a.date; });
x.domain(d3.extent( data, function(d){ return d.date }) );
var max = x.domain()[1];
var min = x.domain()[0];
var datePlusOneMonth = d3.timeDay.offset(d3.timeMonth.offset(max, 1), -1); // last day of current month: move up one month, back one day
x.domain([min,datePlusOneMonth]);
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
// the bars
g.append("g").classed('bars', true)
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr('class', function(d) { return d.key; })
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.data.date); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", barWidth)
.on("mousemove", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.mouse(this)[0]) + 88;
var yPosition = parseFloat(d3.mouse(this)[1]) - 29;
var value = d.data[d3.select(this.parentNode).attr('class')]; // differentiating between col1 and col2 values
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#count")
.text(formatNum(value)); // return the value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#month")
.text(d3.timeFormat("%B %Y")(d.data.date)); // return the value
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// x-axis
var monthAxis = g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xMonthAxis);
const firstDataYear = x.domain()[0];
// xYearAxis.tickValues([firstDataYear].concat(x.ticks()));
var yearAxis = g.append("g")
.attr("class", "yearaxis axis")
.attr("transform", "translate(0," + (height + 25) + ")")
.call(xYearAxis);
var valueAxis = g.append("g")
.attr("class", "y axis")
.call(yAxis);
monthAxis.selectAll("g").select("text")
.attr("transform","translate(" + barWidth/2 + ",0)");
var options = d3.keys(data[0]).filter(function(key) { return key !== "date"; }).reverse();
var legend = svg.selectAll(".legend")
.data(options.slice().filter(function(type){ return type != "total"}))
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", z);
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return capitalizeFirstLetter(d); });
#tooltip {
position: absolute;
width: 200px;
z-index: 2;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
.legend {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60%;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
g[class="col_1"] rect:hover {
fill:#80061b;
}
g[class="col_2"] rect:hover {
fill:#008394;
}
div.slider-container {
margin: 20px auto;
}
div#month-slider {
width: 50%;
margin: 0 auto;
}
div#month-slider .ui-slider-handle {
text-align: center;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script
src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"
integrity="sha256-eGE6blurk5sHj+rmkfsGYeKyZx3M4bG+ZlFyA7Kns7E="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
<div id="tooltip" class="hidden">
<p><strong>Month: </strong><span id="month"></span><p>
<p><strong>Value: </strong><span id="count"></span></p>
</div>
<div class="slider-container">
<span>Number of months: <span class="value"></span></span>
<div id="month-slider">
</div>
</div>
Important code changes:
jQuery slider with a min
value of 0
and max
value of 24
(where 0 will show up the original data and 1-24
will be the data for the last number of months from the most recent date (Nov 17).
// Initialize jQuery slider
$('div#month-slider').slider({
min: 0,
max: 24,
create: function() {
// add value to the handle on slider creation
$(this).find('.ui-slider-handle').html($( this ).slider( "value" ));
},
slide: function(e, ui) {
// change values on slider handle and label based on slider value
$('div.slider-container span.value').html(ui.value);
$(e.target).find('.ui-slider-handle').html(ui.value);
// calculate offset date based on slider value
var offsetDate = ui.value ? d3.timeMonth.offset(datePlusOneMonth, -ui.value) : min;
// set x domain and re-render xAxis
x.domain([offsetDate, datePlusOneMonth]);
g.select('.x.axis').call(xMonthAxis);
g.select('.yearaxis.axis').call(xYearAxis);
// calcuate filtered data based on new offset date, set y axis domain and re-render y axis
var filteredData = data.filter(function(d) { return d.date >= offsetDate; });
y.domain([0, d3.max(filteredData, function(d) { return d.total; })]).nice();
g.select('.y.axis').transition().duration(200).call(yAxis);
// re-render the bars based on new filtered data
// the bars
var bars = g.select("g.bars")
.selectAll("g")
.data(d3.stack().keys(keys)(filteredData));
var barRects = bars.enter().append("g").merge(bars)
.attr('class', function(d) { return d.key; })
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; });
barRects.exit().remove();
barRects.enter()
.append("rect");
barWidth = (width - margin.right- margin.left)/(filteredData.length+1);
g.select("g.bars").selectAll('g rect')
.attr("x", function(d) { return x(d.data.date); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", barWidth)
.on("mousemove", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.mouse(this)[0]) + 88;
var yPosition = parseFloat(d3.mouse(this)[1]) - 29;
var value = d.data[d3.select(this.parentNode).attr('class')]; // differentiating between col1 and col2 values
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#count")
.text(formatNum(value)); // return the value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#month")
.text(d3.timeFormat("%B %Y")(d.data.date)); // return the value
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
}
})
Details:
d3.timeMonth.offset
based on the slider value.Other changes:
Added additional classes to the axes to differentiate between them. X-Axis:
.attr("class", "x axis")
X-year axis:
.attr("class", "yearaxis axis")
Y-Axis:
.attr("class", "y axis")
Added some CSS to the jQuery slider:
div.slider-container {
margin: 20px auto;
}
div#month-slider {
width: 50%;
margin: 0 auto;
}
div#month-slider .ui-slider-handle {
text-align: center;
}
And a z-index
to div#tooltip
. Try removing that and you'll know why.
Let me know if you have any questions. Hope this helps. :)
Edit:
Added the following code within the slider once the barWidth
is calcuated
monthAxis.selectAll("g").select("text")
.attr("transform","translate(" + barWidth/2 + ",0)");
This took me a while to figure out but I came up with a different approach than the one answered here. With that approach, try taking off few rows from the data, you'll observe the mess of ticks.
So in the new approach, the tickValues
for xYearAxis
is computed as follows:
const firstDataYear = x.domain()[0];
var tickValues = x.ticks().filter(function(d) { return !d.getMonth()});
if(firstDataYear.getFullYear() !== tickValues[0].getFullYear()) {
tickValues = [firstDataYear].concat(tickValues);
}
xYearAxis.tickValues(tickValues);
i.e. fetch all the years from the x domain
and prepend the firstDataYear
if absent. I find this to be a better approach.
Here's a demo link:
Let me know if this solves the issues.
Upvotes: 2