score:6

Accepted answer

What you're looking for is to create a separation between your core application code and code that can be reused. This is a good idea because it keeps your application's code small, making it easier for you to change it and move it forward. In a reusable chart you want to send a very generic events, e.g. clicked, while in your application you need the events to be very specific to your domain, e.g. addSugar.

There are two ingredients you need: d3.dispatch and d3.rebind. The former helps you create a clear internal API, while the latter helps you expose the dispatched events to the outside world.

Go here to find an example of this. What you want to look for is three things:

  1. Inside the reusable chart you create a dispatcher with events you want to publish to the outside world: var myDispatch = d3.dispatch('myclick', 'mydrag')
  2. Then in your reusable chart's event handlers you publish these events like so: myDispatch.myclick(argumentsyouwanttopass)
  3. As a last step you make these events available to the outside: return d3.rebind(_chart, myDispatch, "on");
  4. Then you can bind to these events on the outside: myChart.on('myclick', function(){})

So your example, rewritten could look like this:

function chart() {
  var dispatch = d3.dispatch('click');

  function _chart(selection) {
    selection.each(function(d, i) {

      selection.selectAll('rect')
        .data(d)
        .enter()
        .append('rect')
        .attr('x', 0)
        .attr('y', function(d, i) {return i * 11;})
        .attr('width', function(d, i) {return d;})
        .attr('height', 10);
        .on('click', dispatch.click);

    });
  }

  return d3.rebind(_chart, dispatch, 'on');
}

Additional note: to register multiple event handlers, you need to namespace them (like you have to for regular d3 events), e.g. chart.on('click.foo', handlerFoo), chart.on('click.bar', handlerBar) etc.

score:2

One idea to make it more reusable and applicable to any possible event is to define a handler array. This is similar to d3 itself. Here is an updated fiddle

function chart() {
  var event_handlers = {}; //key value pairs

  function _chart(selection) {
    selection.each(function(d, i) {

      var rects = selection.selectAll('rect')
        .data(d)
        .enter()
        .append('rect')
        .attr('x', 0)
        .attr('y', function(d, i) {return i * 11;})
        .attr('width', function(d, i) {return d;})
        .attr('height', 10);

        for(var event in event_handlers){
          rects.on(event, event_handlers[event])
        }

    });
  }

  _chart.on = function(event, fun) {
    if(arguments.length==1) return event_handlers[event];
    event_handlers[event] = fun;
    return _chart;
  }

  return _chart;
}

You can use your chart the same way you use HTML elements.

chart.on("click", clickHandler);

Related Query

More Query from same tag