Question

I'm making a graph with interactive markers. Each marker begins along the side axis, and when clicked moves to its place along the line and grows in size. I got the icons to move and grow, but am having trouble resetting the graph. I can make the icons go back to their original location with a second click, but the icons won't respond to anymore clicks after they've been clicked a second time. I suspect it's something simple, but I'm not seeing it.

var coal = svg.append("svg:image")
    .attr("xlink:href", "nouns/coal.svg")
    .attr("width", 35)
    .attr("height", 35)
    .attr("x", 10)
    .attr("y", 30)
    .on("click", function() {
        coal.transition()
        .attr("x", 80)
        .attr("y", 150)
        .attr("width", 70)
        .attr("height", 70)
        .duration(750)
        .each("end", function() {
            d3.select(this)
            .on("click", function() {
                coal.transition()
                .attr("width", 35)
                .attr("height", 35)
                .attr("x", 10)
                .attr("y", 30);
            })                      
        })
    });

I have a bunch of icons, so I'd be pumped for a solution that sends whatever icon is activated back to its axis-side home whenever a new one is clicked.

Note: I'm working on a fiddle, but fiddle and I don't get along whatsoever, so fingers crossed.

Was it helpful?

Solution

You're attaching two different click handers -- the first one to move there and the second one to move back. The second one stays attached, i.e. after the icon has moved back, its click handler will move it to the same position (that's why it seems as if it isn't moving).

You can do this more elegantly (and fix the problem) by setting the attribute values in the click handler dynamically based on the current values.

var coal = svg.append("svg:image")
  .attr("xlink:href", "nouns/coal.svg")
  .attr("width", 35)
  .attr("height", 35)
  .attr("x", 10)
  .attr("y", 30)
  .on("click", function() {
    d3.select(this).transition()
      .attr("x", function() { return d3.select(this).attr("x") == 10 ? 80 : 10; })
      .attr("y", function() { return d3.select(this).attr("y") == 30 ? 150 : 30; })
      .attr("width", function() { return d3.select(this).attr("width") == 35 ? 70 : 35; })
      .attr("height", function() { return d3.select(this).attr("height") == 35 ? 70 : 35; })
      .duration(750);
  });

It would be even more elegant to base the entire thing on data, i.e. have an array that contains two elements that define the positions and size and alternate between them. In this case, you could also use the usual D3 .data() pattern.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top