How to make grid lines when axes are aligned at the middle?

I am working on creating a 4 quadrant line chart with gridlines. However, the grid does not seem to work properly. It does not go on both sides when I translate the axes. I have provided a fiddle here too.

const f = (x) => {
  return Math.sin(x)
}

const data = d3.ticks(-5, 5, 100).map((x) => {
  const container = {
    x: x,
    y: f(x)
  }
  return container
})

const margins = { top: 20, right: 20, bottom: 30, left: 30 }
const width = 750 - margins.left - margins.right
const height = 750 - margins.top - margins.bottom

const svg = d3.select('#graph')
  .append('svg')
  .attr('width', width + margins.left + margins.right)
  .attr('height', height + margins.top + margins.bottom)
  .append('g')
  .attr('transform', `translate(${margins.left}, ${margins.top})`)

const x = d3.scaleLinear([-5, 5], [0, width])
const y = d3.scaleLinear([-5, 5], [height, 0])

const xAxis = svg.append('g')
  .attr('transform', `translate(0, ${y(0)})`)
  .attr('class', 'x-axis')

const yAxis = svg.append('g')
  .attr('transform', `translate(${x(0)}, 0)`)
  .attr('class', 'y-axis')

xAxis.call(d3.axisBottom(x))
yAxis.call(d3.axisLeft(y))

d3.selectAll('g.x-axis g.tick')
  .append('line')
  .attr('class', 'gridline')
  .attr('x1', 0)
  .attr('y1', -height)
  .attr('x2', 0)
  .attr('y2', 0)

d3.selectAll('g.y-axis g.tick')
  .append('line')
  .attr('class', 'gridline')
  .attr('x1', 0)
  .attr('y1', 0)
  .attr('x2', width)
  .attr('y2', 0)

svg.append('path')
  .datum(data)
  .attr('fill', 'none')
  .attr('stroke', 'steelblue')
  .attr('stroke-width', 1.5)
  .attr('d', d3.line(d => x(d.x), d => y(d.y)))
#graph {
  text-align: center;
}

.gridline{
  stroke: black;
  shape-rendering: crispEdges;
  stroke-opacity: 0.5;
}
<div id="graph"></div>
<script src="https://d3js.org/d3.v6.min.js"></script>

I use this method for creating the gridline.

I want the grid to expand to the side that I translate from. Thank you.

1 thought on “How to make grid lines when axes are aligned at the middle?”

  1. I’m the author of the answer you linked. The problem with that approach, which I don’t make clear in the answer, is that all the lines are created at the same position of the axis. Therefore, you have to translate them.

    For the x axis:

    .attr("transform", `translate(0, ${y.range()[0] - y(0)})`)
    

    And for the y axis:

    .attr("transform", `translate(${x.range()[0] - x(0)})`, 0)
    

    Here is your code with those changes:

    const f = (x) => {
      return Math.sin(x)
    }
    
    const data = d3.ticks(-5, 5, 100).map((x) => {
      const container = {
        x: x,
        y: f(x)
      }
      return container
    })
    
    const margins = {
      top: 20,
      right: 20,
      bottom: 30,
      left: 30
    }
    const width = 750 - margins.left - margins.right
    const height = 750 - margins.top - margins.bottom
    
    const svg = d3.select('#graph')
      .append('svg')
      .attr('width', width + margins.left + margins.right)
      .attr('height', height + margins.top + margins.bottom)
      .append('g')
      .attr('transform', `translate(${margins.left}, ${margins.top})`)
    
    const x = d3.scaleLinear([-5, 5], [0, width])
    const y = d3.scaleLinear([-5, 5], [height, 0])
    
    const xAxis = svg.append('g')
      .attr('transform', `translate(0, ${y(0)})`)
      .attr('class', 'x-axis')
    
    const yAxis = svg.append('g')
      .attr('transform', `translate(${x(0)}, 0)`)
      .attr('class', 'y-axis')
    
    xAxis.call(d3.axisBottom(x))
    yAxis.call(d3.axisLeft(y))
    
    d3.selectAll('g.x-axis g.tick')
      .append('line')
      .attr('class', 'gridline')
      .attr('x1', 0)
      .attr('y1', -height)
      .attr('x2', 0)
      .attr('y2', 0)
      .attr("transform", `translate(0, ${y.range()[0] - y(0)})`)
    
    d3.selectAll('g.y-axis g.tick')
      .append('line')
      .attr('class', 'gridline')
      .attr('x1', 0)
      .attr('y1', 0)
      .attr('x2', width)
      .attr('y2', 0)
      .attr("transform", `translate(${x.range()[0] - x(0)})`, 0)
    
    svg.append('path')
      .datum(data)
      .attr('fill', 'none')
      .attr('stroke', 'steelblue')
      .attr('stroke-width', 1.5)
      .attr('d', d3.line(d => x(d.x), d => y(d.y)))
    #graph {
      text-align: center;
    }
    
    .gridline {
      stroke: black;
      shape-rendering: crispEdges;
      stroke-opacity: 0.5;
    }
    <div id="graph"></div>
    <script src="https://d3js.org/d3.v6.min.js"></script>
    Reply

Leave a Comment