事件委托
在JavaScript里,通常要做的一件事是绑定事件,比如用户在页面的点击、滚动等,然后执行注册的回调函数,这样就响应了用户的某种行为。简单的例子如下:
1 2 3 4 |
$<span class="token punctuation">(</span><span class="token string">'button'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">alert<span class="token punctuation">(</span></span><span class="token string">'hello'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
在用户每次点击页面上的按钮时,弹出一个对话框显示‘hello’。 在有些情况下,我们期望页面上的一些元素响应用户同样的动作,举个例子。在用户点击列表的每一项时,将其内容显示在div#data-show里。
1 2 3 4 5 6 7 8 9 10 11 |
<span class="token operator"><</span>ul id<span class="token operator">=</span><span class="token string">"data-list"</span><span class="token operator">></span> <span class="token operator"><</span>li<span class="token operator">></span><span class="token number">1</span><span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span> <span class="token operator"><</span>li<span class="token operator">></span><span class="token number">2</span><span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span> <span class="token operator"><</span>li<span class="token operator">></span><span class="token number">3</span><span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span> <span class="token operator"><</span>li<span class="token operator">></span><span class="token number">4</span><span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span> <span class="token operator"><</span>li<span class="token operator">></span><span class="token number">5</span><span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span> … <span class="token operator"><</span>li<span class="token operator">></span><span class="token number">100</span><span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>ul<span class="token operator">></span> <span class="token operator"><</span>div id<span class="token operator">=</span><span class="token string">"data-show"</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> |
可以这么做:
1 2 3 4 |
$<span class="token punctuation">(</span><span class="token string">'#data-list li'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $<span class="token punctuation">(</span><span class="token string">'#data-show'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html<span class="token punctuation">(</span></span>$<span class="token punctuation">(</span>this<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
其实是给列表的每一项(100个)分别绑定了点击事件。这样做的弊端在于,增加了内存,因为$(’#data-list li’)里有100个li对象。同时降低了代码性能,因为$(’#data-list li’)会搜索ul#data-list下所有的li元素。
在满足需求的情况下,怎么做更好呢?
答案是使用事件委托!
1 2 3 4 |
$<span class="token punctuation">(</span><span class="token string">'#data-list'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token string">'li'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> $<span class="token punctuation">(</span><span class="token string">'#data-show'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html<span class="token punctuation">(</span></span>$<span class="token punctuation">(</span>this<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
将li元素的点击事件委托给其父元素ul。这么做之所以行得通,是因为事件具有冒泡的特点,当内层元素的某个事件被触发,事件会一级一级冒泡到更外层元素。当外层元素被绑定事件且被触发时,判断事件的来源即event.target是否是目标元素li,如果是就执行回调。上面的代码等价于:
1 2 3 4 5 6 7 8 9 10 11 |
<span class="token keyword">function</span> <span class="token function">showText<span class="token punctuation">(</span></span>text<span class="token punctuation">)</span> <span class="token punctuation">{</span> $<span class="token punctuation">(</span><span class="token string">'#data-show'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html<span class="token punctuation">(</span></span>text<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> $<span class="token punctuation">(</span><span class="token string">'#data-list'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $target <span class="token operator">=</span> $<span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>$target<span class="token punctuation">.</span><span class="token function">is<span class="token punctuation">(</span></span><span class="token string">'li'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">showText<span class="token punctuation">(</span></span>$target<span class="token punctuation">.</span><span class="token function">html<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
除了提高性能和节省内存的好处外,事件委托的另一个好处在于,页面动态变化后,不需要重新检索元素和绑定事件。上例中,如果通过AJAX向列表增加新项,新添加项仍能响应用户点击。
利用事件委托实现事件在HTML页面中的可配置
可能存在这样的需求,页面上的多个元素(不同的),会响应同样的用户行为,比如一个按钮和一个链接,均须响应同样的行为。这种case还有可能存在页面之间。举个例子。
Page 1:
1 2 3 |
<span class="token operator"><</span>button class<span class="token operator">=</span><span class="token string">"primary"</span><span class="token operator">></span>See more<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span>a class<span class="token operator">=</span><span class="token string">"secondary"</span><span class="token operator">></span>See details<span class="token operator"><</span><span class="token operator">/</span>link<span class="token operator">></span> |
Page 2:
1 2 |
<span class="token operator"><</span>input type<span class="token operator">=</span><span class="token string">"button"</span> class<span class="token operator">=</span><span class="token string">"third"</span> value<span class="token operator">=</span><span class="token string">"Check more"</span><span class="token operator">/</span><span class="token operator">></span> |
JS代码势必会这样:
1 2 |
$<span class="token punctuation">(</span><span class="token string">'.primary'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add<span class="token punctuation">(</span></span><span class="token string">'.secondary'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add<span class="token punctuation">(</span></span><span class="token string">'.third'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'click'</span><span class="token punctuation">,</span> callback<span class="token punctuation">)</span><span class="token punctuation">;</span> |
如果又有其他元素也要响应同样的行为,需要修改以上JS代码,而使其变得更长。是否有更好的方式?看下面这个例子。
JS code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<span class="token keyword">var</span> events <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> list <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> on<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span>actionName<span class="token punctuation">,</span> fn<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> actionName <span class="token operator">===</span> <span class="token string">'string'</span> <span class="token operator">&&</span> <span class="token keyword">typeof</span> fn <span class="token operator">===</span> <span class="token string">'function'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Invalid args'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> list<span class="token punctuation">[</span>actionName<span class="token punctuation">]</span> <span class="token operator">=</span> fn<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> trigger<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span>actionName<span class="token punctuation">,</span> data<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> callback <span class="token operator">=</span> list<span class="token punctuation">[</span>actionName<span class="token punctuation">]</span><span class="token punctuation">;</span> callback <span class="token operator">&&</span> callback<span class="token punctuation">.</span><span class="token function">call<span class="token punctuation">(</span></span><span class="token keyword">null</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $<span class="token punctuation">(</span>document<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token string">'.delegated-action'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> $el <span class="token operator">=</span> $<span class="token punctuation">(</span>this<span class="token punctuation">)</span><span class="token punctuation">,</span> actionName <span class="token operator">=</span> $el<span class="token punctuation">.</span><span class="token function">data<span class="token punctuation">(</span></span><span class="token string">'actionName'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>actionName<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> evt <span class="token operator">=</span> <span class="token punctuation">{</span> $event<span class="token punctuation">:</span> event<span class="token punctuation">,</span> $self<span class="token punctuation">:</span> $el<span class="token punctuation">,</span> data<span class="token punctuation">:</span> $el<span class="token punctuation">.</span><span class="token function">data<span class="token punctuation">(</span></span><span class="token string">'actionData'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> events<span class="token punctuation">.</span><span class="token function">trigger<span class="token punctuation">(</span></span>actionName<span class="token punctuation">,</span> evt<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">showIndex<span class="token punctuation">(</span></span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">alert<span class="token punctuation">(</span></span>event<span class="token punctuation">.</span>data<span class="token punctuation">.</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> events<span class="token punctuation">.</span><span class="token function">on<span class="token punctuation">(</span></span><span class="token string">'see-more'</span><span class="token punctuation">,</span> showIndex<span class="token punctuation">)</span><span class="token punctuation">;</span> |
这个例子中,将所有含有样式类delegated-action的元素上的点击事件委托给了document。对象events存储用户通过events.on定义的所有动作和回调。在页面中,只要给元素加上属性class=”delegated-action”和data-action-name=”see-more”,该元素就能响应用户的点击动作了。若需要给回调传入数据,可以将数据以JSON的形式绑定在data-action-data属性上,而不需要修改JS代码。
HTML code:
1 2 3 4 5 6 |
<span class="token operator"><</span>a class<span class="token operator">=</span><span class="token string">"delegated-action"</span> href<span class="token operator">=</span><span class="token string">"javascript:"</span> data<span class="token operator">-</span>action<span class="token operator">-</span>name<span class="token operator">=</span><span class="token string">"see-more"</span> data<span class="token operator">-</span>action<span class="token operator">-</span>data<span class="token operator">=</span><span class="token string">'{"index": 1}'</span><span class="token operator">></span>See more<span class="token operator"><</span><span class="token operator">/</span>a<span class="token operator">></span> <span class="token operator"><</span>button class<span class="token operator">=</span><span class="token string">"delegated-action"</span> data<span class="token operator">-</span>action<span class="token operator">-</span>name<span class="token operator">=</span><span class="token string">"see-more"</span> data<span class="token operator">-</span>action<span class="token operator">-</span>data<span class="token operator">=</span><span class="token string">'{"index": 2}'</span><span class="token operator">></span>See more<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span>input class<span class="token operator">=</span><span class="token string">"delegated-action"</span> type<span class="token operator">=</span><span class="token string">"button"</span> data<span class="token operator">-</span>action<span class="token operator">-</span>name<span class="token operator">=</span><span class="token string">"see-more"</span> data<span class="token operator">-</span>action<span class="token operator">-</span>data<span class="token operator">=</span><span class="token string">'{"index": 3}'</span> value<span class="token operator">=</span><span class="token string">"See more"</span><span class="token operator">/</span><span class="token operator">></span> |
这种方式很适合页面上通用组件或行为,而不用关心元素的类型、ID、命名等,只需要在HTML里添加指定的样式类,定义data-action-name和data-action-data属性即可。文章只拿click事件来描述例子,读者有兴趣可以扩展到代码以适合其他事件。
- 本文固定链接: http://7iang.com/?p=368
- 转载请注明: 7iang 于 7iang 麦冰棍 发表