Skip to content

Instantly share code, notes, and snippets.

@ambar
Created October 26, 2012 16:06
Show Gist options
  • Save ambar/3959655 to your computer and use it in GitHub Desktop.
Save ambar/3959655 to your computer and use it in GitHub Desktop.
delegate proto
  • 核心是 matchSelector 方法,可以借用了 jQuery.fn.is 方法;
  • 选择器匹配了多个元素时,事件触发顺序应该从底向上;
  • 绑定多个监听函数时并多时匹配到多个元素时,依次发生顺序要正确;
  • 匹配目标应该包含 event.target,不包含委托的顶层元素;
  • 反注册时,清理代理函数;
JSON.stringify(delegateMap, function(k, v) {
 return typeof v === 'function' ? 'fn' : v
}, '  ')
// =>
{
  "1": {
    "click .a": [],
    "click span": [
      "fn"
    ],
    "click section": [
      "fn"
    ]
  }
}
var delegateMap = {
// expando: {
// 'type selector' : [fn...]
// }
}
var ep = Element.prototype
var matchesSelector = ep.matchesSelector || ep.webkitMatchesSelector || ep.mozMatchesSelector || ep.oMatchesSelector || ep.msMatchesSelector
if (!matchesSelector) {
matchesSelector = function(selector) {
return $(this).is(selector)
}
}
var delegate = function (src, type, selector, listener, opt_capt, opt_handler) {
var uid = goog.getUid(src), map = delegateMap[uid]
if (!map) {
map = delegateMap[uid] = {}
}
var key = type + ' ' + selector, listeners = map[key]
if (!listeners) {
listeners = map[key] = []
listeners.delegateProxy__ = delegateProxy
goog.events.listen(src, type, delegateProxy, opt_capt, opt_handler)
}
listeners.push(listener)
listeners = null
function delegateProxy(e) {
var node = e.target, nodes = []
while ( node !== src ) {
if (matchesSelector.call(node, selector)) {
nodes.push(node)
}
node = node.parentNode
}
nodes.forEach(function(node) {
e.currentTarget = node
map[key].forEach(function(f) {
f.call(this, e)
}, this)
}, this)
}
}
var undelegate = function (src, type, selector, listener, opt_capt, opt_handler) {
var uid = goog.getUid(src), map = delegateMap[uid]
if (!map) return
var key = type + ' ' + selector, listeners = map[key]
if (listeners && listeners.length) {
var delegateProxy = listeners.delegateProxy__
listeners = map[key] = listeners.filter(function(f) {
return f !== listener
})
if (!listeners.length) {
goog.events.unlisten(src, type, delegateProxy, opt_capt, opt_handler)
}
}
}
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul" class="a">
<li>
<section class="a">
<div class="a">
<button><span>span <i class="a">i.a</i> </span> text</button>
</div>
</section>
</li>
<li>
<section class="a">
<div class="a">
<button><span>span <i class="a">i.a</i> </span> text</button>
</div>
</section>
</li>
<li>
<section class="a">
<div class="a">
<button><span>span <i class="a">i.a</i> </span> text</button>
</div>
</section>
</li>
</ul>
<script src="../closure-library/closure/goog/base.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.js"></script>
<script src="delegate.js"></script>
<script>
goog.require('goog.events')
</script>
<script>
// goog.events.listen(ul, 'click', function(e) {
// console.info('listen', e, this, e.target, e.currentTarget);
// })
var listener = function(e) {
console.info('delegate', e, this, e.target, e.currentTarget);
}
delegate(ul, 'click', '.a', listener)
delegate(ul, 'click', '.a', listener)
// delegate(ul, 'click', 'span', listener)
// delegate(ul, 'click', 'section', listener, false, {})
// undelegate(ul, 'click', '.a', listener)
$(ul)
.on('click', '.a', function(e) {
console.info('jQuery', this, e.target, e.currentTarget);
})
.on('click', '.a', function(e) {
console.info('jQuery', this, e.target, e.currentTarget);
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment