JavaScript 事件委托

事件委托

在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事件来描述例子,读者有兴趣可以扩展到代码以适合其他事件。

你可能感兴趣的:(javascript,javascript,事件委托)