删除包装的事件处理程序

时间:2014-06-20 14:09:56

标签: javascript

我有一个函数将一个函数包装在另一个函数周围,然后将它附加到一个元素。

function addCustomEvent(element, eventName, handler, useCapture) {
    var wrappedHandler = function () {
        // Do something here.
        handler.call();
    };

    element.addEventListener(eventName, wrappedHandler, useCapture);
}

这很好用,我也想实现这个功能:

removeCustomEvent(element, eventName, handler, useCapture)

所以我想做这样的事情。

var clickHandler= function () { /* ... */ };

addCustomEvent(someElement, "click", clickHandler, false);
removeCustomEvent(someElement, "click", clickHandler, false);

这是一个问题,因为我没有wrappedHandlerremoveCustomEvent的引用。

我现在能想到的唯一方法是跟踪字典中的handler及其对应的wrappedHandler,以便我可以从wrappedHandler找到handler在函数内,并将其删除。

但是我不喜欢这种方法,因为浏览器必须有关于哪些处理程序附加的信息,因此创建一个新字典似乎是多余的,浪费内存。

是否有更好,更清洁的方式?

1 个答案:

答案 0 :(得分:2)

就个人而言,我只需将addCustomEventremoveCustomEvent包装到单个模块中,并保留一个跟踪绑定处理程序的对象。您认为这“浪费资源”,但实际上,这种方法的影响可以忽略不计。
好处是:您可以轻松扩展模块的开头,处理更复杂的事件处理程序(例如使用touchstarttouchend事件模拟移动设备的标签事件)。 / p>

另一种方法是在内部取消绑定事件处理程序,具体取决于事件对象本身 然后,您将不得不重新编写removeCustomEvent函数来触发特殊事件,以便绑定的处理程序知道您要删除事件侦听器。

//in the wrappedHandler:
var wrappedHandler = function(e)
{
    e = e || window.event;
    if (e.synthetic === true)
    {
        e.preventDefault();
        e.stopPropagation();
        element.removeEventListener(eventName, wrappedHandler, useCapture);//<-- use closure vars
        return e;//or return false.
    }
    //do normal event
    handler.apply(this, [e]);//pass event object, and call handler in the same context!
};

var removeCustomEvent = function(event, node, capture)
{
    var e, eClass,
        doc = node.ownerDocument || (node.nodeType === (document.DOCUMENT_NODE || 9) ? node : document);
    if (node.dispatchEvent)
    {
        if (event === 'click' || event.indexOf('mouse') >= 0)
            eClass = 'MouseEvents';
        else
            eClass = 'HTMLEvents';
        e = doc.createEvent(eClass);
        e.initEvent(event, !(event === 'change'), true);
        //THIS IS THE TRICK:
        e.synthetic = true;
        node.dispatchEvent(e, true);
        return true;
    }
    if (node.fireEvent)
    {
        e = doc.createEventObject();
        e.synthetic = true;
        node.fireEvent('on' + event, e);
        return true;
    }
    event = 'on' + event;
    return node[event]();
};

here's a version of this code that is actually documented

我在事件对象上设置了一个synthetic属性,该属性将传递给事件处理程序。处理程序检查此属性,如果它设置为true,它将取消绑定侦听器并返回。这不需要你将DOM引用和处理程序保存在一个对象中,但是,我认为你也同意,这也是很多工作。
如果你不介意我这么说,那也感觉非常hacky ......

与:相比:

var binderModule = (function()
{
    var module = {},
        eventMap = {},
        addEvent = function (elem, eventName, handler, capture)
        {
            var i, wrappedHandler;
            if (!eventMap.hasOwnProperty(eventName))
                eventMap[eventName] = [];
            for (i=0;i<eventMap[eventName].length;++i)
            {//look for elem reference
                if (eventMap[eventName][i].node === elem)
                    break;
            }
            if (i>= eventMap[eventName].length)
            {
                i = eventMap[eventName].length;//set i to key
                eventMap[eventName].push({
                    node: elem,
                    handlers: []//keep handlers here, in array for multiple handlers
                 });
             }
             wrappedHandler = function(e)
             {
                 //stuff
                  return handler.apply(this, [e || window.event]);//pass arguments!
             };
             eventMap[eventNAme][i].handlers.push(wrappedHandler);
             return elem.addEventListener(eventName, wrappedHandler, capture);
        },
        removeEvent(elem, eventName, capture)
        {
            var i, temp;
            if (!eventMap.hasOwnProperty(eventName))
                return;//no handlers bound, end here
            for (i=0;i<eventMap[eventName].length;++i)
                if (eventMap[eventName][i].node === elem)
                    break;
             if (i < eventMap[eventName].length)
             {//found element, remove listeners!
                 //get handlers
                 temp = eventMap[eventName][i].handlers;
                 //remove element + handlers from eventMap:
                 eventMap[evetnName][i] = undefined;
                 for (i=0;i<temp.length;++i)
                     elem.removeEventListener(eventName, temp[i], capture);
             }
        };
    module.addCustomEvent = addEvent;
    module.removeCustomEvent = removeEvent;
    //or, perhaps better:
    Object.defineProperty(module, 'addCustomEvent', {value: addEvent});//read-only
    Object.defineProperty(module, 'removeCustomEvent', {value: removeEvent});
    return module;
}());

请注意,这是跟踪绑定到特定DOM节点的事件处理程序以及如何对其进行管理的基本设置。此代码未完成且未经过测试。它可能包含拼写错误,语法错误和一些一致性问题。但这应该足以让你开始。