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