この記事ではdelegateの説明は省略しますが、興味のある方は昔書いた記事を読んでいただけると幸いです。
これまでは自作したdelegate関数を使って、deleateを設定してきました。
自作のdelegate
function delegate(parent, eventName, selector, callback) { parent.addEventListener(eventName, function(evt) { (function checkTarget(target) { evt.delegateTarget = target; if (target.matches && target.matches(selector, parent)) { callback.call(target, evt); } else { if (target === parent) { return; } else { checkTarget(target.parentNode); } } })(evt.target); }, false); }
が。最近、npmでdelegateを発見。
早速、乗り換えようかと思いましたが、折角なのでコードを見比べてみることにしました。
npmのdelegate
closest.js
var DOCUMENT_NODE_TYPE = 9; /** * A polyfill for Element.matches() */ if (typeof Element !== 'undefined' && !Element.prototype.matches) { var proto = Element.prototype; proto.matches = proto.matchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector || proto.oMatchesSelector || proto.webkitMatchesSelector; } /** * Finds the closest parent that matches a selector. * * @param {Element} element * @param {String} selector * @return {Function} */ function closest (element, selector) { while (element && element.nodeType !== DOCUMENT_NODE_TYPE) { if (typeof element.matches === 'function' && element.matches(selector)) { return element; } element = element.parentNode; } } module.exports = closest;
delegate.js
var closest = require('./closest'); /** * Delegates event to a selector. * * @param {Element} element * @param {String} selector * @param {String} type * @param {Function} callback * @param {Boolean} useCapture * @return {Object} */ function _delegate(element, selector, type, callback, useCapture) { var listenerFn = listener.apply(this, arguments); element.addEventListener(type, listenerFn, useCapture); return { destroy: function() { element.removeEventListener(type, listenerFn, useCapture); } } } /** * Delegates event to a selector. * * @param {Element|String|Array} [elements] * @param {String} selector * @param {String} type * @param {Function} callback * @param {Boolean} useCapture * @return {Object} */ function delegate(elements, selector, type, callback, useCapture) { // Handle the regular Element usage if (typeof elements.addEventListener === 'function') { return _delegate.apply(null, arguments); } // Handle Element-less usage, it defaults to global delegation if (typeof type === 'function') { // Use `document` as the first parameter, then apply arguments // This is a short way to .unshift `arguments` without running into deoptimizations return _delegate.bind(null, document).apply(null, arguments); } // Handle Selector-based usage if (typeof elements === 'string') { elements = document.querySelectorAll(elements); } // Handle Array-like based usage return Array.prototype.map.call(elements, function (element) { return _delegate(element, selector, type, callback, useCapture); }); } /** * Finds closest match and invokes callback. * * @param {Element} element * @param {String} selector * @param {String} type * @param {Function} callback * @return {Function} */ function listener(element, selector, type, callback) { return function(e) { e.delegateTarget = closest(e.target, selector); if (e.delegateTarget) { callback.call(element, e); } } } module.exports = delegate;
https://github.com/zenorocha/delegate より引用
基本的な方針は大体同じですね。matchesを使った判定方法も大体同じです。
引数を含めてbindしているところは「なるほど」と思いました。
大きな違いとして、npmの方には、delegateで設定したコールバックの削除(destroy)が提供されていることです。
いままでdelegateで設定したイベントを剥がそうと思ったことはなかったですが、確かにあった方が便利そうです。
というわけで、これからはnpmのdelegateを使っていこうと思います。