在D3中:
我正在尝试使用可重复使用的图表模式来实现一个图表,该模式允许我添加在实际图表之外处理的事件。例如,我有一个简单的条形图,可以点击每个单独的条形图。现在,我希望能够在创建图表实例的地方处理点击事件,而不仅仅是在图表组件中处理点击事件。例如,我想编辑与单击的条形元素相对应的数据,或者我想根据我单击的条形的位置更新其他元素的位置。为了使图表组件尽可能模块化和独立,我不希望在其中完成某些任务。
我提出了一个非常直接的解决方案,但我觉得必须有一个更好的解决方案。通过这种方式,我必须考虑到外界可能感兴趣的每一个可能的事件
function clickHandler(d, i) {
// do something with the element/event/data
}
var svg = d3.select('#container')
.append('svg');
var myChart = chart()
.onClick(clickHandler);
svg.append('g')
.datum(data)
.call(chart);
function chart() {
var onClick = function() {};
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', _onClick);
});
}
function _onClick(d, i) {
d3.select(this)
.attr('fill', 'red');
onClick(d, i);
}
_chart.onClick = function(_) {
if(!arguments.length) return onClick;
onClick = _;
return _chart;
}
return _chart;
}
有什么标准的方法/最佳实践来处理这种情况吗?或者这真的不是一种常见的情况?
感谢您的帮助!
您想要的是在核心应用程序代码和可重用代码之间创建一个分离。这是一个好主意,因为它使应用程序的代码保持较小,使您更容易更改并向前推进。在可重复使用的图表中,您希望发送非常通用的事件,例如clicked
,而在应用程序中,您需要这些事件非常特定于您的域,例如addSugar
。
您需要两种成分:d3.dispatch和d3.rebind。前者帮助您创建一个清晰的内部API,而后者帮助您将调度的事件暴露给外部世界。
请单击此处查找此示例。你想找的是三件事:
- 在可重复使用的图表中,您可以创建一个调度器,其中包含要发布到外部世界的事件:
var myDispatch = d3.dispatch('myclick', 'mydrag')
- 然后,在可重用图表的事件处理程序中,发布这些事件,如下所示:
myDispatch.myclick(argumentsyouwanttopass)
- 作为最后一步,您可以向外部提供这些事件:
return d3.rebind(_chart, myDispatch, "on");
- 然后您可以在外部绑定到这些事件:
myChart.on('myclick', function(){})
所以你的例子,重写后可能看起来像这样:
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');
}
附加说明:要注册多个事件处理程序,您需要为它们命名名称(就像您必须为常规d3事件命名一样),例如chart.on('click.foo', handlerFoo)
、chart.on('click.bar', handlerBar)
等。
使其更可重用并适用于任何可能的事件的一个想法是定义handler
数组。这与d3本身类似。这是一个更新的小提琴
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;
}
您可以像使用HTML元素一样使用chart
。
chart.on("click", clickHandler);