在DOM元素d3之间交换事件处理程序

时间:2015-05-15 21:25:23

标签: javascript d3.js

我拥有的代码意味着使用控件的事件处理程序并将这些处理程序移动到另一个元素并删除之前控件的事件处理程序http://jsfiddle.net/pydty4bq/

var slider = d3.select('#slider1');
        var slider1Config = {
            element: slider,
            drag:function(e){
                if(d3.event.toElement)
                    console.log('has toElement',d3.event.toElement.valueAsNumber);
                else
                    console.log(d3.event.sourceEvent.srcElement.valueAsNumber);
            },
            dragstart:function(){console.log('dragstart!');},
            dragend: function(){ console.log('dragend!');}
        }
        var _drag = new Drag(slider1Config);
        _drag.element(d3.select('#slider2'));

_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有哪些错误,它没有从上一个元素中删除事件侦听器?

2 个答案:

答案 0 :(得分:0)

以下是拖动行为的工作原理。

首先,您可以创建以下行为:

var _drag = d3.behavior.drag();

然后您将事件处理程序附加到它:

_drag
    .on('dragstart', function() { ... })
    .on('drag', function() { ... })
    .on('dragend', function() { ... });

然后将此行为附加到元素:

d3.select('#slider1').call(_drag);

有效地将上面定义的处理程序附加到该元素(实际事件的名称不同)。

从行为中分离事件处理程序就像这样:

 _drag.on('dragstart', null);

并且它可以正常工作。

<强> BUT

不幸的是,将具有禁用事件处理程序的此行为应用于具有绑定(启用)事件处理程序的元素不会禁用该元素的处理程序。即。

_drag.on('dragstart', null);
d3.select('#slider1').call(_drag);

不会像以前那样阻止#slider1处理dragstart

这是我的实验所表明的。不知道它是不是一个错误。

无论如何,可以直接从一个元素中分离拖动处理程序 由于所有与拖动相关的事件都位于.drag命名空间内,因此您可以一次性禁用它们:

d3.select('#slider1').on('.drag', null);

选中此JSFiddle

答案 1 :(得分:0)

要理解的重要一点是,拖动行为,即d3.behavior.drag()返回的函数,可以应用于任意数量的选择,并且不知道DOM元素也是受约束的。

在内部,行为具有自定义 dispatch 对象,该对象抽象出人为事件。这些事件 - “drag”,“dragstart”和“dragend” - 完全从源自DOM的任何本机事件中删除。内部 dispatch 对象只是

的系统
  1. 实现这些抽象事件
  2. 管理听众对他们的依恋和分离
  3. 提供触发抽象事件的界面和
  4. 触发后通知已注册的听众
  5. 这就是你得到的代码......

    var _drag = d3.behavior.drag();
    
    _drag
        .on('dragstart', function() { ... })
        .on('drag', function() { ... })
        .on('dragend', function() { ... });
    

    请注意,上述内容均未对任何DOM元素或其事件进行任何引用,并且本身无法启动任何内容。 行为selection.call(behavior)连接到DOM UI之前实际上无法执行任何操作。这是行为连接到DOM UI的地方。不知何故,它需要弄清楚是否已经发生了一个dragstart事件,它需要从UI事件中抽象出来,它或多或少地完成了这样的事情:selection.on({ "mousedown.drag": mousedown, "touchstart.drag": touchstart })

    所以这里有两个,完全独立的原生事件 - “mousedown”和“touchstart” - 源自选择中的DOM元素 - 通过拖动“在引擎盖下”应用< EM>行为。

      

    (如@dekkard所述,它们通过附加“.drag”来命名   他们的标准名称。 d3管理它并在之前剥离它们   订阅。)

    侦听器 - mousedowntouchstartd3.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);
    

    是将侦听器分离并重新附加到行为的内部 dispatch 对象,并将行为添加到第二个选择中。所以现在,来自任一选择的“mousedown”和“touchstart”将导致相同的拖动行为。

    根据documentation,@ dekkard提供的方法 - .on('.drag', null) - 是断开行为与选择的唯一方法,所以这不是一个错误所有。我猜这只是一个缺失的功能。

    注意:通过替换此代码在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")  
    

    断开行为