使用Hammer.js的事件委托

5
我该如何使用纯JavaScript在Hammer.js中实现类似于jQuery风格的事件委托?例如:
Hammer(document).on('tap', '.item', function () {
  console.log('tapped')
})

这是直接可能的,还是我需要自己委托?

检查目标 - 仅当您单击具有 .item 的元素时才有效。但是,如果在该 .item 中存在另一个元素并且您单击该内部元素,则检查目标将无法帮助您。您需要扫描其父级(这就是其他答案提供的内容)- 这正是您在问题中想要的 $.on('tap', '.item', function () {...}。使用jquery - 使用您在问题中的代码,即使您单击了 .item 的内部元素,也会提供正确的信息。 - Royi Namir
正如您在这里所看到的 - 检查目标将向您显示不是.item的按钮 http://jsbin.com/hobenunale/1/edit?html,js,output - 而jQuery会向您显示委托的元素。 - Royi Namir
4个回答

3

受Jools答案的启发,以下是我的解决方案。我没有费心提供纯JS的解决方案——实际上,这是用于Backbone.View的,但可以轻松适应其他情况。

当它到达View的根元素(this.el)时,它将停止爬升节点树。从由HammerJS事件标识的目标开始,它将寻找与指定选择器匹配的第一个元素,并且如果找不到这样的元素,则返回undefined

要在非Backbone环境中使用,只需将限制/包含元素的引用作为第三个参数传入(如Jools的解决方案),并用它替换this.el即可。

/**
 * @param {Node}target - the target of the received event
 * @param {String}selector - any jQuery-compatible selector
 */
getEventDelegate: function (target, selector) {
     var delegate;
     while (target && target != this.el) {
        delegate = $(target).filter(selector)[0];
        if (delegate) {
           return delegate;
        }
        target = target.parentNode;
     }
     return undefined;
  }

如果您有一个onTap回调函数,它可能看起来像这样:
/**
 * @param {Event}ev
 */
onTap: function (ev) {
   var target = this.getEventDelegate(ev.target, "#desiredTarget");
   if (target) {
      // TODO something with the target...
   }
}

并且要将所有这些连接起来,您需要像这样的东西...
this._hammer = new Hammer(this.el);
this._hammer.on('tap', _.bind(this.onTap, this));

(并且在销毁视图时不要忘记调用this._hammer.destroy()!)

奇怪的是,OP接受了一个并没有解决问题的答案。目标不同于委托事件的元素。 - Royi Namir

3
我用下面的函数来测试目标是否为委托对象。
function _getDelegate(selector, target, currentTarget) {

   var delegate = null;

   while (target && target != currentTarget) {
      delegate = $(target).filter(selector)[0];
      if (delegate) 
         return delegate;
      target = target.parentNode;
   }

   return delegate;

}

我使用jQuery过滤器,因为我经常使用它,并且使用一些更复杂的选择器。
用法:
var delegationSelector = ".child";
$element.hammer()
$element.on("tap", function handler(event){
   var delegate = _getDelegate(delegationSelector, event.target, event.currentTarget);
   if(delegate){ // or if(!delegate) return;
      // Your handler code
   }
});

这种方法并不适用于所有选择器,但我认为它比André的方法更好,因为如果你的“目标”有子元素,它仍然可以工作。例如:

<div id="parent">
    <div class="child">
        <div class="text">Test</div>
    </div>
</div>

由于target[div.text]并且没有您的类,因此André在这里会失败。

对于上述使用_getDelegate的情况,假设$element = $("#parent")。如果您点击了单词“Test”,event.target将是您点击的位置[div.text],而event.currentTarget将是处理程序注册的位置[div.parent]。当您使用这些参数调用_getDelegate时,您将收到[div.child]并知道应该运行处理程序代码。

对于纯JS版本,您需要像这样循环遍历父级以查看是否击中任何内容。

var delegate;
var target = e.target; // e.g. Inner div
var currentTarget = e.currentTarget // e.g. #parent

while(target && target != currentTarget){
   if(target.className.indexOf('child')!=-1){
      delegate = target;
      break;
   }
   target = target.parentNode;
}

if( delegate ) {
   console.log('delegated tap received');
}

这还存在很多缺陷。整个className的indexOf是一个坏主意,因为类可以是彼此的子字符串,例如"myClass"和"myClass2"。另外,我的代码假设所有子元素都在其父元素内定位。


很好,但是为您的_isDelegate方法提供一个使用示例和/或参数文档会更有帮助。另外,_getDelegate作为名称是否更准确,因为它不返回布尔值? :) - Lambart
谢谢。我添加了一个使用示例,只是使用jquery on处理程序,虽然你已经添加了使用hammer的on()。我也改变了函数名。我相当确定它是isDelegate,因为当我编写它时,我没有在处理程序中实际使用元素。当我想要元素时,很容易将其更改为返回元素,但忘记更改函数名称。 - Jools

2
您只需检查传递到处理程序的Event对象的target即可。

这里是演示


这不是 OP 想要的。 - Royi Namir
@Royi,真的吗?因为他在4年前就接受了这个答案... - André Dion
有关编程的内容:有什么关系吗?我需要使用Hammer(document).on('tap', '.item', function () { console.log('tapped') }) - 而目标不是帮手 :-) - Royi Namir
正如您在这里所看到的 - 检查目标将向您显示不是.item的按钮 http://jsbin.com/hobenunale/1/edit?html,js,output - 而jQuery会向您显示委托的元素。 - Royi Namir

-1

继续@Lambarr的解决方案 - TypeScript:

private delegateBind(rootElement:Node,e, selector:string, handler: (target:any) => any)
  {

    let target=e.target;
    let o = {

      getEventDelegate: function ()
      {
        var delegate;

        while (target && target != rootElement)
        {
          delegate = jQuery(target).filter(selector)[0];
          if (delegate)
          {
            return delegate;
          }

          target = target.parentNode;
        }
        return undefined;
      }

    };

     target = o.getEventDelegate();
    if (target)
    {
      handler(target);
    }
  }

使用方法:

private registerDocumentclick()
  {

     Hammer(this.document).on('tap', e =>
        this.delegateBind(this.document,e,'.article', (elm) => console.log(elm.classList))

    );
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接