简体   繁体   English

与Hammer.js的事件代表团

[英]Event delegation with Hammer.js

How would I do jQuery-style event delegation with plain JavaScript in Hammer.js? 如何在Hammer.js中使用纯JavaScript进行jQuery样式的事件委派? Eg: 例如:

Hammer(document).on('tap', '.item', function () {
  console.log('tapped')
})

Is this directly possible or do I have to do the delegation myself? 这是直接可能还是我自己必须做代表团?

I use the following function to test if the target is a delegate. 我使用以下函数来测试目标是否是委托。

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;

}

I'm using the jquery filter because I use it a lot and with some more complex selectors. 我正在使用jquery过滤器,因为我经常使用它和一些更复杂的选择器。

Usage: 用法:

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
   }
});

This won't work with all selectors, but I'd say it's better than André's in that it will still work if your "target" has child elements. 这不适用于所有选择器,但我认为它比André更好,因为如果你的“目标”具有子元素,它仍然可以工作。 Eg 例如

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

André's will fail here because target is the [div.text] and doesn't have your class. 安德烈将在这里失败,因为target[div.text]而没有你的班级。

For the above usage of _getDelegate, lets' say $element = $("#parent") . 对于_getDelegate的上述用法,让我们说' $element = $("#parent") If you tapped the word "Test" event.target would be [div.text] (where you tapped) and event.currentTarget would be [div.parent] (where the handler is registered). 如果您点击了“Test”一词event.target将是[div.text] (您点击的地方),而event.currentTarget将是[div.parent] (处理程序已注册)。 When you called _getDelegate with these parameters, you would receive [div.child] and know that you should run your handler code. 当您使用这些参数调用_getDelegate时,您将收到[div.child]并知道您应该运行处理程序代码。

For a pure JS version, you'd want something like this, where you loop up through the parents looking to see if you hit anything. 对于一个纯粹的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');
}

There are still plenty of flaws with this. 这还有很多瑕疵。 The whole indexof className is a bad idea because classes can be sub-strings of one another eg "myClass" and "myClass2". className的整个索引是一个坏主意,因为类可以是彼此的子字符串,例如“myClass”和“myClass2”。 Also, my code makes the assumption that all of your subelements are positioned within their parents. 此外,我的代码假设您的所有子元素都位于其父元素中。

Inspired by Jools' answer, here is what I've come up with. 灵感来自Jools的回答,这就是我想出的。 I didn't bother with a pure-JS solution--in fact, this is intended for use with a Backbone.View , but is easily adaptable to other situations. 我没有使用纯JS解决方案 - 实际上,这是用于Backbone.View ,但很容易适应其他情况。

It will stop climbing the node tree when it reaches the View's root element ( this.el ). 当它到达View的根元素( this.el )时,它将停止攀爬节点树。 Starting from the target identified by the HammerJS event, it will seek the first element matching the specified selector, and return undefined if no such element is found. 从HammerJS事件标识的目标开始,它将寻找与指定选择器匹配的第一个元素,如果没有找到这样的元素,则返回undefined

To use in a non-Backbone environment, just pass in a reference to the limiting/containing element as a 3rd argument (as in Jools' solution) and use it to replace this.el . 要在非Backbone环境中使用,只需this.el元素的引用作为第三个参数传递(如在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;
  }

If you have an onTap callback function, it might look something like this: 如果你有一个onTap回调函数,它可能看起来像这样:

/**
 * @param {Event}ev
 */
onTap: function (ev) {
   var target = this.getEventDelegate(ev.target, "#desiredTarget");
   if (target) {
      // TODO something with the target...
   }
}

And to hook it all up, you'd want something like this... 把它全部搞定,你想要这样的东西......

this._hammer = new Hammer(this.el);
this._hammer.on('tap', _.bind(this.onTap, this));

(and don't forget to call this._hammer.destroy() when your view is destroyed!) (当你的视图被销毁时别忘了调用this._hammer.destroy() !)

You simply need to inspect the target of the Event object that's passed into your handler. 您只需要检查传递给处理程序的Event对象的target

Here's a demo . 这是一个演示

Continuing @Lambarr's solution - Typescript: 继续@ 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);
    }
  }

Usage : 用法:

private registerDocumentclick()
  {

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

    );
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM