<!doctype html>
<html>

  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  </head>

  <body>
    <svg id="svg"></svg>
    <button id="update">update</button>
    <button id="reset">reset</button>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script>

      const defaults = {
        target: '#chart',
        width: 640,
        height: 480,
        margin: {
          top: 20,
          right: 20,
          bottom: 30,
          left: 40
        }
      }

      class LineChart {

        constructor(config) {
          Object.assign(this, defaults, config)
          this.init()
        }

        init() {
          const {target, width, height, margin} = this
          const w = width - margin.left - margin.right
          const h = height - margin.top - margin.bottom

          this.svg = d3.select(target)
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', `translate(${margin.left}, ${margin.top})`)

          this.xScale = d3.scalePoint()
            .range([0, w])

          this.yScale = d3.scaleLinear()
            .range([h, 0])

          this.xAxis = d3.axisBottom(this.xScale)

          this.svg
            .append('g')
            .attr('class', 'x axis')
            .attr('transform', `translate(0, ${h})`)
            .call(this.xAxis)

          this.yAxis = d3.axisLeft(this.yScale)

          this.svg
            .append('g')
            .attr('class', 'y axis')
            .call(this.yAxis)
        }

        render(data) {
          const {xScale, yScale, xAxis, yAxis, svg} = this

          xScale.domain(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'])

          svg.select('.x.axis')
            .call(xAxis)

          yScale.domain([0, 10])

          svg.select('.y.axis')
            .call(yAxis)

          svg
            .selectAll('.dot')
            // .data(data, d => d.key)
            .data(data)
            .enter()
            .append('circle')
            .attr('class', 'dot')
            .attr('cx', d => xScale(d.key))
            .attr('cy', d => yScale(d.value))
            .attr('r', 10)
        }

        update(data) {
          const {svg, xScale, yScale} = this

          // join new data with old elements
          const selection = svg
            .selectAll('.dot')
            // .data(data, d => d.key)
            .data(data)

          // update old elements
          selection
            .transition()
            .attr('cx', d => xScale(d.key))
            .attr('cy', d => yScale(d.value))

          // add new elements
          selection.enter()
            .append('circle')
            .attr('class', 'dot')
            .attr('cx', d => xScale(d.key))
            .attr('cy', d => yScale(d.value))
            .attr('r', 0)
            .transition()
            .attr('r', 10)
            .attr('fill', 'orange')

          // remove old elements
          selection.exit()
            .transition()
            .attr('r', 0)
            .attr('fill', 'red')
            .remove()
        }

      }

      // draw chart
      let chart
      const draw = () => {
        const config = {
          target: 'svg',
          width: document.body.clientWidth
        }

        chart = new LineChart(config)
        const data = [
          {key: 'a', value: 1},
          {key: 'b', value: 0},
          {key: 'c', value: 3},
          {key: 'd', value: 2},
          {key: 'e', value: 5},
          {key: 'f', value: 4},
          {key: 'g', value: 7},
          {key: 'h', value: 6},
          {key: 'i', value: 9},
          {key: 'j', value: 8}
        ]
        chart.render(data)
      }
      draw()

      // update button
      const button = document.getElementById('update')
      button.addEventListener('click', () => {
        const data = [
          // remove a
          // {key: 'a', value: 1},
          // update b, c, d, e, f, g, h, i, j
          {key: 'b', value: 1},
          {key: 'c', value: 4},
          {key: 'd', value: 3},
          {key: 'e', value: 6},
          {key: 'f', value: 3},
          {key: 'g', value: 6},
          {key: 'h', value: 7},
          {key: 'i', value: 3},
          {key: 'j', value: 4},
          // add k
          {key: 'k', value: 9}
        ]
        chart.update(data)
      })

      // reset button
      const reset = document.getElementById('reset')
      reset.addEventListener('click', () => {
        document.getElementById('svg').firstChild.remove()
        draw()
      })

    </script>
  </body>

</html>