Javascript 在DOM元素d3之间交换事件处理程序
我所拥有的代码意味着获取控件的事件处理程序,并将这些处理程序移动到另一个元素,并删除以前控件的事件处理程序Javascript 在DOM元素d3之间交换事件处理程序,javascript,d3.js,Javascript,D3.js,我所拥有的代码意味着获取控件的事件处理程序,并将这些处理程序移动到另一个元素,并删除以前控件的事件处理程序 \u drag.element函数应该从slider1中删除事件处理程序,并将它们放在slider2上。目前,这两个元素都在触发事件处理程序 //拖动功能 var Drag = (function(){ var _opts, _drag = d3.behavior.drag(); var eventList = ['drag','dragstart','dr
\u drag.element
函数应该从slider1中删除事件处理程序,并将它们放在slider2上。目前,这两个元素都在触发事件处理程序
//拖动功能
var Drag = (function(){
var _opts,
_drag = d3.behavior.drag();
var eventList = ['drag','dragstart','dragend'];
function attachEvents(opts){
eventList.forEach(function(e,i){
if(opts[e]){
_drag.on(e,this[e]);
}
else{
_drag.on(e,null);
}
}.bind(this));
};
function detachEvents(){
eventList.forEach(function(e,i){
if(_opts[e]){
_drag.on(e,null);
}
}.bind(this));
}
function Drag(opts){
_opts = opts;
attachEvents.call(this,opts);
_opts.element = opts.element.call(_drag);
_opts.element.attr('isDraggable',true);
}
Drag.prototype.drag = function(args){
_opts.drag(args);
};
Drag.prototype.dragstart = function(args){
_opts.dragstart(args);
}
Drag.prototype.dragend = function(args){
_opts.dragend(args);
}
Drag.prototype.element = function(el){
if(el){
detachEvents.call(this)
_opts.element = el;
attachEvents.call(this,_opts);
_opts.element.call(_drag);
}
}
Drag.prototype.config = function(opts){
_opts = opts;
attachEvents.call(this,_opts);
}
return Drag;
})();
detachEvents
没有从上一个元素中删除事件侦听器有什么问题?以下是拖动行为的工作原理
首先创建如下行为:
var _drag = d3.behavior.drag();
然后将事件处理程序附加到它:
_drag
.on('dragstart', function() { ... })
.on('drag', function() { ... })
.on('dragend', function() { ... });
然后将此行为附加到元素:
d3.select('#slider1').call(_drag);
它有效地将上面定义的处理程序附加到该元素(实际事件的名称不同)
将事件处理程序从行为中分离如下:
_drag.on('dragstart', null);
d3.select('#slider1').on('.drag', null);
它工作正常
但是
不幸的是,将此行为与禁用的事件处理程序一起应用于具有绑定(启用)事件处理程序的元素不会禁用该元素的处理程序。即
_drag.on('dragstart', null);
d3.select('#slider1').call(_drag);
不会阻止#slider1
像以前一样处理dragstart
这就是我的实验所显示的。不知道这是不是一个错误
无论如何,有一种直接的方法可以将拖动处理程序与元素分离。由于所有与拖动相关的事件都位于
.drag
名称空间内,因此可以一次禁用它们,如下所示:
_drag.on('dragstart', null);
d3.select('#slider1').on('.drag', null);
检查此项需要了解的重要一点是,拖动行为(由
d3.behavior.drag()返回的函数)可以应用于任意数量的选择,并且不知道它绑定的DOM元素
在内部,该行为有一个自定义的分派对象,它抽象了人工事件。这些事件——“drag”、“dragstart”和“dragend”——从任何源于DOM的本机事件中完全删除。内部分派对象只是一个用于
认识到这些抽象事件
管理听众对他们的依恋和疏离
提供用于触发抽象事件和
触发已注册侦听器后通知它们
这就是你从这个代码中得到的结果
var _drag = d3.behavior.drag();
_drag
.on('dragstart', function() { ... })
.on('drag', function() { ... })
.on('dragend', function() { ... });
请注意,上面的任何一个都没有对任何DOM元素或它们的事件的引用,并且它们本身不能启动任何东西。在通过selection.call(behavior)
连接到DOM UI之前,该行为实际上无法执行任何操作。这是行为连接到domui的地方。不知何故,它需要找出是否发生了dragstart事件,它需要从UI事件中提取该事件,并且它或多或少是这样做的:selection.on({“mousedown.drag”:mousedown,“touchtstart.drag”:touchtstart})
因此,这里有两个完全独立的本机事件——“mousedown”和“touchstart”——源自选择中的DOM元素——通过拖动行为“在引擎盖下”应用
(正如@dekkard所提到的,它们的名称空间是通过将“.drag”添加到
它们的标准名称。d3管理这些名称,并在
订阅。)
侦听器-mousedown
和touchstart
是由d3.behavior.drag
对象生成并在其内部关闭的对象,它们都会回调启动拖动过程的behavior对象内的进程。只要行为实例继续存在,它就会继续侦听这些DOM UI事件
因此有两个完全独立的接口:一个用于调用和管理抽象的“drag”、“dragstart”和“dragend”事件,另一个用于连接到DOM UI事件“mousedown”和“touchstart”
因此,此代码的净效果
detachEvents.call(this)
_opts.element = el;
attachEvents.call(this,_opts);
_opts.element.call(_drag);
将侦听器分离并重新附加到行为的内部分派对象,并将该行为添加到第二个选择中。所以现在,来自任一选择的“mousedown”和“touchstart”将导致相同的拖动行为
根据,@dekkard-.on('.drag',null)
-提供的方法是将行为与选择断开连接的唯一方法,因此它毕竟不是一个bug。我想这只是一个缺失的功能
注意:在d3中通过替换此代码来实现这一点是非常容易的
function drag() {
this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
}
用这个
function drag(selection, remove) {
[{e:"mousedown.drag", l: mousedown},{e: "touchstart.drag", l: touchstart}]
.reduce(function (s, b) {return s = s.on(b.e, remove ? null : b.l)}, this)
}
在d3.behavior.drag中
然后我们可以这样做
_opts.element.call(_drag, "remove")
断开这种行为。请您“重新考虑”这句话好吗?“不幸的是,将禁用事件处理程序的此行为应用于具有绑定(启用)事件处理程序的元素不会禁用元素的处理程序。”我对您的答案非常感兴趣,但我不理解您的意思。e、 g.能否定义“禁用的事件处理程序”;“绑定(已启用)事件处理程序”。。。也许这会有帮助。很抱歉不清楚。我的意思是:定义一个行为,然后将一个事件处理程序绑定到该行为,然后将该行为应用到一个对象=>该对象现在已附加了一个事件处理程序。现在您为该行为禁用该事件处理程序(.on('event',null)
),并将该行为应用于同一个对象=>该对象仍然具有以前附加的处理程序。啊,好的,我想我终于明白您的意思了。事件已从“行为”中关闭的调度程序中删除,但存储在节点上的回调仍在侦听。我认为这肯定是一个bug。这个过程的代码(在我看来)非常复杂,我很想理解它,但是我认为应该对监听器进行适当的清理。顺便说一下,对名称空间的了解很好。有趣的是,尽管可以删除名称空间中的所有侦听器,但并没有实现侦听名称空间中的所有事件。不过,在分支中有一个用于此选项的占位符prima fa