Search code examples
d3.jsgraphbar-chartd3-graphviz

Unable to show positive and negative values together using D3 js


I am using d3.js but I am unable to show both values together on graph that is positive values above the x axis and negative value below x axis ...

I tried below code

var bar = g.selectAll(".bar").data(data);
        var barColorClass = 'blue-bar';
        var transitionOrigin =  D3Graph.height - 1;
        
        var rect = bar.enter().append("rect")
          .attr("class", "bar "+barColorClass)
          .attr("x", function(d, i) { return i * unit; })
          .attr("y", function(d) { return transitionOrigin;})
          .attr("width", unit);
        
        rect.transition(t)
        .attr("height", function(d) {
            
                return (y(d.amount) < 0) ? (D3Graph.height + 3) : (D3Graph.height - y(d.amount));
        
        }).attr("y", function(d) {
            
                return (y(d.amount) < 0) ? (-1 * 3) : y(d.amount);
                
        });
        
        

Solution

  • In a canonical example you would adjust the y and height of the rects based on if they are positive or negative:

    .attr('y', (d) => d.y > 0 ? y(d.y) : y(0))
    .attr('height', (d) => d.y > 0 ? y(0) - y(d.y) : y(d.y) - y(0))
    

    Full example:

    <!DOCTYPE html>
    
    <html>
    
    <head>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.js"></script>
    </head>
    
    <body>
      <div id="chart"></div>
      <script>
        const margin = {
            top: 30,
            right: 30,
            bottom: 70,
            left: 60
          },
          width = 460 - margin.left - margin.right,
          height = 400 - margin.top - margin.bottom;
    
        const svg = d3
          .select('#chart')
          .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 + ')');
    
        const data = [{
            x: 'one',
            y: 10,
          },
          {
            x: 'two',
            y: -10,
          },
          {
            x: 'three',
            y: 5,
          },
          {
            x: 'four',
            y: -5,
          },
          {
            x: 'five',
            y: 15,
          },
          {
            x: 'six',
            y: -15,
          },
        ];
    
        // X axis
        const x = d3
          .scaleBand()
          .range([0, width])
          .domain(
            data.map((d) => d.x)
          )
          .padding(0.2);
    
        svg
          .append('g')
          .attr('transform', 'translate(0,' + height + ')')
          .call(d3.axisBottom(x))
          .selectAll('text')
          .attr('transform', 'translate(-10,0)rotate(-45)')
          .style('text-anchor', 'end');
    
        // Add Y axis
        const y = d3.scaleLinear().domain(d3.extent(data.map((d) => d.y))).range([height, 0]);
        svg.append('g').call(d3.axisLeft(y));
    
        // Bars
        svg
          .selectAll(null)
          .data(data)
          .enter()
          .append('rect')
          .attr('x', (d) => x(d.x))
          .attr('y', (d) => d.y > 0 ? y(d.y) : y(0))
          .attr('width', x.bandwidth())
          .attr('height', (d) => d.y > 0 ? y(0) - y(d.y) : y(d.y) - y(0))
          .attr('fill', 'steelblue');
      </script>
    </body>
    
    </html>