does d3 selection.on() support dynamic event bind like jQuery.on() function?

389 views
Skip to first unread message

Bruce Wang

unread,
Jan 23, 2017, 4:33:48 AM1/23/17
to d3-js
when i update(add some new datas) my data, then rebind the data to element ,will generate some new element in the page.
i wonder if d3 .on() support dynamic event bind the new element.
here is my demo
when i click "add new circle" button, if the new circles can bind with event before, like jQuery.on() direct and delegated events

or if there some d3 plugins solve the problem?




Curran

unread,
Jan 23, 2017, 10:21:36 AM1/23/17
to d3-js
Hi Bruce,


The behavior you describe is possible using D3, without any extra plugins. The key to achieving the outcome you describe is the "general update pattern", which is described quite well in the documentation for selection.merge.

In the code you shared, the logic that defines how circles should render is duplicated. It occurs once for the first render, adding the event listeners, and again for when the button is pressed, without adding the event listeners. The reason why the new circle doesn't have the event listener is because you are not invoking on() in second copy of the rendering logic.

Using the general update pattern, the rendering logic can be unified into a single render() function. This function can be called any number of times, with data that is different on each invocation. Here's one way your code can be refactored to work with the general update pattern, and also use selection.on to add event listeners to the circles:

function render(data){
  var svg = d3.select("svg");
  
  var circles = svg.selectAll("circle")
    .data(data);
  
  circles.exit().remove();
  
  circles
    .enter()
      .append("circle")
    .merge(circles)
      .attr("cx",function(d){
        return d.x;
      })
      .attr("cy",function(d){
        return d.y;
      })
      .attr("r",function(d){
        return d.r
      })
      .style("fill", function(d){
        return d.color;
      })
      .on("click", test);
}

function test(d){
  alert(d.x);
}

function main(){
  var data = [
    {x:100,y:100,r:30, color:"red"},
    {x:200,y:200,r:30, color:"red"},
    {x:300,y:300,r:30, color:"red"},
  ];
  
  render(data);
  
  var cx=400, cy=400, index=0;
  
  d3.select(".btn1")
    .on("click",function(){

      data.push({
        x:cx+index*100,
        y:cy,
        r:30,
        color: "yellow"
      });

      index++;
      render(data);
    });
}
main();


Here's a working CodePen of the above code. The even listener gets added to the new circle, which I think is what you are looking for.

Best regards,
Curran

Bruce Wang

unread,
Jan 25, 2017, 8:35:23 PM1/25/17
to d3-js
thanks.  i know the new circle should bind the "click" event again. because the sample is very simple, if every time i generate a group of (svg) elements and those elements related with some events(click drag dblclick .etc) on the same time.
in the circumstance, i will rebind those elements with the events by writing a rebinding function. maybe i think it a little tedious. so i wonder if d3 provide the delegate event bind link jQuery.on() below.
$("ul").on("click","li",function(){...})

when i generate the new "li",it will bind with "click" event.
thanks for reply.

在 2017年1月23日星期一 UTC+8下午11:21:36,Curran写道:
Reply all
Reply to author
Forward
0 new messages