Search code examples
javascriptd3.jsbar-chartaxis-labels

Can't set the number of ticks on time-based x-axis to custom amount for bar chart


I'm currently using the following code to set the number of ticks on my time-based x-axis to 5; however, every single tick continues to display regardless of what number I insert within "ticks()".

The code I'm using to set the number of ticks to 5

let xAxisLower = d3.axisBottom(x)
        .ticks(5)
        .tickFormat(d3.timeFormat("%m-%d"))
        .tickPadding(15);

How do I set the number of ticks on the x-axis to 5 and why can't I do so with .ticks(5)?

The JavaScript code (refer to JSFiddle for further detail)

tradeArr = [
    {date: "2017-06-15" , bps: 0},
    {date: "2017-06-14" , bps: 0},
    {date: "2017-06-13" , bps: 0},
    {date: "2017-06-12" , bps: 0},
    {date: "2017-06-09" , bps: 0},
    {date: "2017-06-08" , bps: 0},
    {date: "2017-06-07" , bps: 0},
    {date: "2017-06-06" , bps: 0},
    {date: "2017-06-05" , bps: 0},
    {date: "2017-06-02" , bps: 0},
    {date: "2017-06-01" , bps: 13.9},
    {date: "2017-05-31" , bps: 0},
    {date: "2017-05-30" , bps: 0},
    {date: "2017-05-29" , bps: 0},
    {date: "2017-05-26" , bps: 0},
    {date: "2017-05-25" , bps: 0},
    {date: "2017-05-24" , bps: 0},
    {date: "2017-05-23" , bps: 0},
    {date: "2017-05-22" , bps: 0},
    {date: "2017-05-19" , bps: 0},
    {date: "2017-05-18" , bps: 0},
    {date: "2017-05-17" , bps: 0},
    {date: "2017-05-16" , bps: 1.95},
    {date: "2017-05-15" , bps: 0},
    {date: "2017-05-12" , bps: 0}
];

tradeArr.forEach(obj => {
    obj.date = d3.timeParse("%Y-%m-%d")(obj.date);
})

generateBarChart(tradeArr);

function generateBarChart(data) {
    console.log(data);

    // chart configuration
    let margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
    };

    let width;
    let height;

    let x = d3.scaleBand()
        .domain(data.map(function(d) { return d.date; }));

    let y = d3.scaleLinear()
        .domain([d3.min(data, function(d) { return d.bps; }), d3.max(data, function(d) { return d.bps; })])

    let xAxisLower = d3.axisBottom(x)
        .ticks(5)
        .tickFormat(d3.timeFormat("%m-%d"))
        .tickPadding(15);

    let yAxis = d3.axisLeft(y)
        .ticks(2)
        .tickSize(0)
        .tickPadding(15);

    let svg = d3
        .select("#price-chart-graph")
            .style("position", "relative") // strictly for the tooltip
        .append("svg") // append an svg to the div
            .style("overflow", "hidden")
            .style("position", "relative");

    let artboard = svg
                    .append("g")
                        .attr("transform", "translate(" + margin.left + ", " + margin.right + ")");

    let xAxisLowerEl = artboard.append("g") // x-axis
        .attr("class", "x-axis")
        .style("font-size", "14px");

    let yAxisEl = artboard.append("g") // y-axis
        .attr("class", "y-axis")
        .style("font-size", "14px");

    let bars = artboard.selectAll(".bar")
                    .data(data)
                    .enter().append("rect")
                    .style("fill", "black")

    function drawChart() {
        width = parseInt(d3.select("#price-chart-graph").style("width")) - margin.left - margin.right;
        height = parseInt(d3.select("#price-chart-graph").style("height")) - margin.top - margin.bottom;

        svg.attr("width", width + margin.left + margin.right) // give it width
        svg.attr("height", height + margin.top + margin.bottom) // give it height

        x.rangeRound([0, width], .05).padding(0.1);
        y.range([height, 0]);
        // call new x axis
        xAxisLowerEl.attr("transform", "translate(0," + height + ")").call(xAxisLower);
        yAxisEl.call(yAxis);

        bars
            .attr("x", function(d) { return x(d.date); })
            .attr("width", function(d) { return x.bandwidth()})
            .attr("y", function(d) { return y(d.bps); })
            .attr("height", function(d) { return height - y(d.bps); })

    }

    drawChart();

    window.addEventListener('resize', drawChart);
}

Solution

  • Your x-axis is of type scaleBand, and ticks() doesn't work on banded axes. From the documentation:

    This method has no effect if the scale does not implement scale.ticks, as with band and point scales

    You can however explicitly set the values with tickValues(), so you can try filtering the domain down to five values and setting manually. This may get you in the right direction:

    let tickInterval = 5;
    let tickValues = data.map(function(d) { return d.date; }).filter(function(d, i) { return i % tickInterval === 0 });
    let xAxisLower = d3.axisBottom(x)
        .tickFormat(d3.timeFormat("%m-%d"))
        .tickValues(tickValues)
        .tickPadding(15);
    

    You might need to tweak tickInterval to fit the length of your data, but that should be a start.