d3 Click to create circle and click to remove

那年仲夏 提交于 2019-12-02 00:39:40

The problem with your code right now is that, if you click on a blue circle, it will both delete that circle and create a new, red circle. That happens because the event bubbles up to the SVG parent element.

An easy solution is doing this inside the removeElement function:

d3.event.stopPropagation();

As the documentation explains, stopPropagation...

... prevents further propagation of the current event in the capturing and bubbling phases.

Here is the code with that change:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .active {
    stroke: #000;
    stroke-width: 2px;
  }
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    radius = 32;

  var data = [{
      x: 100,
      y: 200
    },
    {
      x: 200,
      y: 300
    },
    {
      x: 300,
      y: 200
    },
    {
      x: 400,
      y: 300
    }
  ];

  var xScale = d3.scaleLinear()
    .domain([0, d3.max(data, function(d) {
      return d.x_pos
    })]).range([0, width]);

  svg.selectAll("circle")
    .data(data)
    .enter().append("circle")
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    })
    .attr("r", radius)
    .style("fill", "lightblue")
    .attr('id', function(d, i) {
      return 'rect_' + i;
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended))
    .on("click", removeElement);

  svg.on("click", function() {
    var coords = d3.mouse(this);

    var newData = {
      x: d3.event.x,
      y: d3.event.y
    };

    data.push(newData);

    svg.selectAll("circle") // For new circle, go through the update process
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .attr("r", radius)
      .style("fill", "red")
      .attr('id', function(d, i) {
        return 'circle_' + i;
      })
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended))
      .on("click", removeElement);
  })

  function dragstarted(d) {
    d3.select(this).raise().classed("active", true);
  }

  function dragged(d) {
    d3.select(this)
      .attr("cx", d.x = d3.event.x)
      .attr("cy", d.y = d3.event.y);
  }

  function dragended(d) {
    d3.select(this)
      .classed("active", false);
  }

  function removeElement(d) {
    d3.event.stopPropagation();
    // need to remove this object from data
    d3.select(this)
      .remove();
  }
</script>

PS: As you probably know by now, I'm not a big fan of answering different problems in the same question (which is something that I always advise OPs to avoid). Therefore, I'm not dealing with the data issue, that is, removing the data associated with the clicked element. You can always ask another question... it's free!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!