Search code examples
javascripthtmljsond3.js

Rotated x-axis labels do not show when applying brush functionality


I am using D3 to create an area chart with brush functionality on rotated x-axis to an angle of 45 degrees, since they were overlapping, but it is not working. When the graph is initially displayed, the x-axis labels are rotated. While applying brush to the chart, the brush is functional but the x-axis labels disappear and do not show anymore, even if I double click to reinitialize the chart.

  <script>
    var margin = { top: 10, right: 30, bottom: 30, left: 60 },
      width = 999 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var svg = d3
      .select("#input_viz")
      .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 = <?php echo $jsonInput; ?>;

    var parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");

    data.forEach(function (d) {
      d.date = parseDate(d[0]);
      d.value = +d[1];
    });

    var customTimeFormat = d3.timeFormat("%Y-%m-%d %H:%M:%S");

    var x = d3.scaleTime()
      .domain(d3.extent(data, function (d) { return d.date; }))
      .range([0, width]);
    var xAxis = svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).tickFormat(customTimeFormat))
      .selectAll("text") // Select all the text elements for the x-axis ticks
      .style("text-anchor", "end") // Set the text anchor to end
      .attr("dx", "-1em") // Adjust the x position to move the labels left
      .attr("dy", "-0.5em") // Adjust the y position to move the labels up
      .attr("transform", "rotate(-45)"); // Rotate the labels by -45 degrees

    var y = d3.scaleLinear()
      .domain([0, d3.max(data, function (d) { return d.value; })])
      .range([height, 0]);
    var yAxis = svg.append("g")
      .call(d3.axisLeft(y));

    var clip = svg.append("defs").append("svg:clipPath")
      .attr("id", "clip")
      .append("svg:rect")
      .attr("width", width)
      .attr("height", height)
      .attr("x", 0)
      .attr("y", 0);

    var brush = d3.brushX()
      .extent([[0, 0], [width, height]])
      .on("end", updateChart);

    var area = svg.append('g')
      .attr("clip-path", "url(#clip)");

    var areaGenerator = d3.area()
      .x(function (d) { return x(d.date); })
      .y0(y(0))
      .y1(function (d) { return y(d.value); });

    area.append("path")
      .datum(data)
      .attr("class", "myArea") // I add the class myArea to be able to modify it later on.
      .attr("fill", "#009901")
      .attr("fill-opacity", .3)
      .attr("stroke", "#006601")
      .attr("stroke-width", 1)
      .attr("d", areaGenerator);

    area.append("g")
      .attr("class", "brush")
      .call(brush);

    var idleTimeout;

    function idled() {
      idleTimeout = null;
    }

    function updateChart() {
      // What are the selected boundaries?
      extent = d3.event.selection;

      if (!extent) {
        if (!idleTimeout) return idleTimeout = setTimeout(idled, 350);
        x.domain(d3.extent(data, function (d) { return d.date; }));
      } else {
        x.domain([x.invert(extent[0]), x.invert(extent[1])]);
        area.select(".brush").call(brush.move, null);
      }

      xAxis.transition().duration(1000).call(d3.axisBottom(x).tickFormat(customTimeFormat))
        .selectAll("text") // Select all the text elements for the x-axis ticks
        .style("text-anchor", "end") // Set the text anchor to end
        .attr("dx", "-1em") // Adjust the x position to move the labels left
        .attr("dy", "-0.5em") // Adjust the y position to move the labels up
        .attr("transform", "rotate(-45)"); // Rotate the labels by -45 degrees
      area
        .select('.myArea')
        .transition()
        .duration(1000)
        .attr("d", areaGenerator);
    }

    svg.on("dblclick", function () {
      x.domain(d3.extent(data, function (d) { return d.date; }));
      xAxis.transition().call(d3.axisBottom(x).tickFormat(customTimeFormat))
        .selectAll("text") // Select all the text elements for the x-axis ticks
        .style("text-anchor", "end") // Set the text anchor to end
        .attr("dx", "-1em") // Adjust the x position to move the labels left
        .attr("dy", "-0.5em") // Adjust the y position to move the labels up
        .attr("transform", "rotate(-45)"); // Rotate the labels by -45 degrees
      area
        .select('.myArea')
        .transition()
        .attr("d", areaGenerator);
    });

  </script>


Solution

  • Here you go https://codepen.io/lary-Test/pen/RwqvqpV

    The only probleme was that xAxis variable value was not the <g> tag. You were trying to update from the texts selection.

     var xAxis = svg.append("g")
          .attr('class','xAxis')
          .attr("transform", "translate(0," + height + ")"); 
     // here now xAxis is the original <g> containing the whole axis rendering
     xAxis.call(d3.axisBottom(x).tickFormat(customTimeFormat))
          .selectAll("text") // Select all the text elements for the x-axis ticks
          .style("text-anchor", "end") // Set the text anchor to end
          .attr("dx", "-1em") // Adjust the x position to move the labels left
          .attr("dy", "-0.5em") // Adjust the y position to mofve the labels up
          .attr("transform", "rotate(-45)"); // Rotate the labels by -45 degrees
    

    Be careful I'm using d3 7.8.5 so I had to change

     function updateChart({selection}) {
      extent = selection;
      ...
    

    Because I didn't know which d3 version you were using.

    Hope this help.