[英]Interactivity with the d3 reusable pattern
In D3:在 D3 中:
I'm trying to implement a chart using the reusable charts pattern which allows me to add events which are handled outside the actual chart.我正在尝试使用可重用图表模式来实现图表,该模式允许我添加在实际图表之外处理的事件。 For instance I have a simple barchart where I can click on each individual bar.例如,我有一个简单的条形图,我可以在其中单击每个条形图。 Now instead of just handling the click event inside the chart component I want to be able to handle it from where I create a chart instance.现在,我希望能够从创建图表实例的位置处理它,而不是仅仅处理图表组件内的点击事件。 For example I want to edit the data which corresponds to the clicked bar element or I want to update the position of some other element depending on the position of the bar I clicked on.例如,我想编辑与单击的条形元素相对应的数据,或者我想根据我单击的条形的位置更新其他元素的位置。 To keep the chart component as modular and independent as possible I don't want certain tasks to be done inside it.为了使图表组件尽可能模块化和独立,我不希望在其中完成某些任务。
I came up with a pretty straight forward solution but I feel like there must be a better solution to this.我想出了一个非常直接的解决方案,但我觉得必须有更好的解决方案。 This way I have to account for every possible event that the outside world might be interested in. JSFiddle这样我就必须考虑到外界可能感兴趣的每一个可能的事件。JSFiddle
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;
}
Is there any kind of standard way/best practice to handle this type of situation?是否有任何标准方法/最佳实践来处理这种情况? Or is this just not really a common situation?或者这只是不是一个普遍的情况?
Any help appreciated!任何帮助表示赞赏!
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, eg clicked
, while in your application you need the events to be very specific to your domain, eg addSugar
.在可重用图表中,您希望发送非常通用的事件,例如clicked
,而在您的应用程序中,您需要非常特定于您的域的事件,例如addSugar
。
There are two ingredients you need: d3.dispatch and d3.rebind .您需要两种成分: d3.dispatch和d3.rebind 。 The former helps you create a clear internal API, while the latter helps you expose the dispatched events to the outside world.前者帮助您创建清晰的内部 API,而后者帮助您将调度的事件暴露给外部世界。
Go here to find an example of this.去这里找到一个例子。 What you want to look for is three things:你要寻找的是三件事:
var myDispatch = d3.dispatch('myclick', 'mydrag')
在可重用图表中,您创建一个调度程序,其中包含要发布到外部世界的事件: var myDispatch = d3.dispatch('myclick', 'mydrag')
myDispatch.myclick(argumentsyouwanttopass)
然后在可重用图表的事件处理程序中发布这些事件,如下所示: myDispatch.myclick(argumentsyouwanttopass)
return d3.rebind(_chart, myDispatch, "on");
作为最后一步,您将这些事件提供给外部: return d3.rebind(_chart, myDispatch, "on");
myChart.on('myclick', function(){})
然后就可以在外面绑定这些事件了: 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), eg chart.on('click.foo', handlerFoo)
, chart.on('click.bar', handlerBar)
etc.附加说明:要注册多个事件处理程序,您需要为它们命名(就像常规 d3 事件一样),例如chart.on('click.foo', handlerFoo)
, chart.on('click.bar', handlerBar)
等。
One idea to make it more reusable and applicable to any possible event is to define a handler
array.使其更具可重用性和适用于任何可能的事件的一个想法是定义一个handler
数组。 This is similar to d3 itself.这类似于 d3 本身。 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.您可以像使用 HTML 元素一样使用chart
。
chart.on("click", clickHandler);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.