学习 easyui 之四:禁用 linkbutton 问题以后,颜色变灰,可是还能执行onclick事件

1.问题的起源

linkbutton 是 easyui 中经常使用的一个控件,咱们可使用它建立按钮。用法很简单,使用 a 元素,标记上 easyui-linkbutton 的类就能够看到按钮了。javascript

<a id="btn" class="easyui-linkbutton">这是一个按钮</a>

看起来就是这个样子html

或者使用代码方式。java

$("#btn").linkbutton();

不过,点了也没有做用,若是但愿有做用,那么,再为它添加一个事件处理吧。一般你会使用 jQuery 的方式添加事件处理函数。结果多是这样。jquery

脚本中的事件注册。数组

$("#btn").click(function () {
        alert("我被点到了!");
    });

看起来一切正常。很快你发现一个新的需求,须要暂时禁用这个按钮,太简单了,easyui 中已经提供了禁用按钮的方法 disable,来让咱们禁用一下。缓存

直接调用linkbutton的"disable"属性,代码变成了这样。dom

$("#btn").linkbutton("disable");

按钮则变成了这样。函数

再点击一下,傻眼了吧!提示框照样弹了出来!。ui

2. linkbutton 是如何禁用按钮的

easyui 提供了 linkbutton 的源代码,因此,咱们能够很方便地看一下,内部是如何实现禁用按钮的。this

function setButtonState(domElem, disabled) {                    // 设置按钮状态
    var data = $.data(domElem, "linkbutton");                   // 获取easyui对象
    if (disabled) {                                             // 若是禁用
        data.options.disabled = true;                       //设置改easyui对象的options的disabled属性为true
        var href = $(domElem).attr("href");                     // 获取该元素节点的href属性
        if (href) {                                             //若是存在href
            data.href = href;                                   // 保存原来的超级连接
            $(domElem).attr("href", "javascript:void(0)");      // 从新设置
        }
        if (domElem.onclick) {                                  // 是否有点击事件处理
            data.onclick = domElem.onclick;
            domElem.onclick = null;                             // 取消掉
        }
        $(domElem).addClass("l-btn-disabled");                  // 使用样式
    } else {
        data.options.disabled = false;                          // 启用按钮
        if (data.href) {                                        // 恢复原来的超级连接
            $(domElem).attr("href", data.href);
        }
        if (data.onclick) {                                     // 恢复原来的点击事件处理
            domElem.onclick = data.onclick;
        }
        $(domElem).removeClass("l-btn-disabled");
    }
}

能够看到,禁用的时候,首先将原来注册的点击事件处理保存到 data.onlick 中,而后,将元素的 onclick 设置为 null;

已经禁用了,又为何没有效果呢?

3. 不一样的事件处理方式

在 DOM 中,存在着两类不一样的事件处理方式,DOM Level 0 方式和 DOM Level 2 方式。

在DOM Level 0的API中,经过在HTML中设置属性,或者在JavaScript中设置一个对象的属性(property)的方法注册事件. 例如,注册事件能够这样进行。

document.getElementById("btn").onclick = function () {
    alert("我被点击啦!");
};

取消事件注册,能够经过将 onclick 从新赋予一个 null 来完成。

而在DOM Level 2模型中,你经过调用那个对象的addEventListener()方法注册事件处理程序.

document.getElementById("btn").addEventListener("click", function () {
    alert("我被点击啦!");
}, false);

取消注册能够经过 removeEventListener 进行。

问题在于,这两种方式是各自独立处理的,互相并不影响。

若是你查看一下 jQuery 的原代码,好比参考一下这篇文章,就会看到,jQuery 使用的正是 DOM Level 2 方式。

if (elem.addEventListener) {
    elem.addEventListener(type, eventHandle, false);

} else if (elem.attachEvent) {
    elem.attachEvent("on" + type, eventHandle);
}

对比 easyui 中对于 linkbutton 的处理代码,咱们会看到,因为 easyui 中使用了 DOM Level 0 方式处理按钮的启用和禁用,而 jQuery 则使用 DOM Level 2 方式进行事件的注册,两种方式擦肩而过,因此,咱们的禁用失效了。

使用  DOM Level 0 方式,咱们能够经过事件名称,方便地取得原来注册的事件处理函数,保存起来,以便之后回复。这正是 linkbutton 如今已经完成的。

使用 DOM Level 2 方式,咱们又应该如何获取已经注册的事件处理函数呢?咱们有 addEventListener,有 removeEventListener。可是已经注册的事件处理在哪里呢?

让咱们看看 jQuery 是如何处理的。

4. jQuery 将咱们注册的事件处理函数保存到哪里去了?

在 jQuery 中,为每一个 DOM 对象准备了一个数据缓存对象,这个对象的使用方式很巧妙。

首先 jQuery 建立了一个用来规定每一个 DOM 对象保存数据的时候使用的属性名,为了惟一,这个属性名使用了各类方式来保证惟一性。

在 jQuery 2.0 中是这样定义的。

expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" )

在 jQuery 1.4.4 中是这样定义的。

expando: "jQuery" + jQuery.now(),

因此,最终,jQuery.expando 多是相似这样的一个惟一的值:jQuery16101803968874529044,这个值用来做为 DOM 元素的一个扩展属性,避免与其余脚本库扩展属性的冲突。它的值则是这个 DOM 缓存的数据对象的编号。在 jQuery  上则定义了一个名为 cache 的全局缓存对象,使用这个编号来到 cache 对象上找到属于本身的数据缓存对象。

每一个元素对应的数据缓存对象上的 events 属性中用来保存这个元素注册的事件处理程序。

events 对象上,再经过事件名称做为索引器,保存对应事件的处理数据,每一个事件名称对应的数据是一个数组,数组中是一个一个的事件描述对象。在 jQuery 中定义以下。

复制代码

handleObj = jQuery.extend({
    type: type,
    origType: origType,
    data: data,
    handler: handler,
    guid: handler.guid,
    selector: selector,
    needsContext: selector && jQuery.expr.match.needsContext.test(selector),
    namespace: namespaces.join(".")
}, handleObjIn);

复制代码

其中的 handler 就是注册的事件处理函数。

(1 << -1) - 1  的 jQuery event(上) 和 jQuery event(下)  有详细的说明,能够参考一下。

终于找到了,咱们又如何拿到这个 click 事件的处理数组呢?

还有一些小问题要处理,原本,jQuery 提供了一个 $.data 函数可让咱们直接获取元素缓存的数据,包括 jQuery 内部使用的 events 数据。可是,从 jQuery 1.8 开始,使用 $.data 就不能够了。见 jQuery 1.8 Released 这篇文章

$(element).data(“events”): In version 1.6, jQuery separated its internal data from the user’s data to prevent name 
collisions. However, some people were using the internal undocumented “events” data structure 
so we made it possible to still retrieve that via .data(). This is now removed in 1.8, 
but you can still get to the events data for debugging purposes via $._data(element, "events"). 
Note that this is not a supported public interface; 
the actual data structures may change incompatibly from version to version.

可是,依然提供了一个非支持的接口 $._data(element, "events") 来获取这些数据。

snandy 的 读jQuery之六(缓存数据) 中有比较详细的说明。

5. 修复 linkbutton 的启用和禁用

修改的方式,就是在原来的基础上,增长对于经过 DOM Level 2 方式注册的事件进行处理。

咱们检查是否注册了 jQuery 的 click 事件处理函数,若是有,在数据缓存对象上增长一个名为  savedHandlers    的数组来保存原来的点击事件处理函数,而后,从原来的对象上取消已经注册的事件处理函数。

恢复的时候,检查数据对象上是否有保存的事件处理函数数组,若是存在,遍历这个数组,将这些事件处理函数从新注册到元素上。

修改方式分为二种:

1.修改easyui的源代码,把源代码中的setButtonState()方法使用下面的这个替代:

function setButtonState(domElem, disabled) {                    // 设置按钮状态

    var data = $.data(domElem, "linkbutton");                   // 获取对象的数据
    if (disabled) {                                             // 禁用按钮
        data.options.disabled = true;
        var href = $(domElem).attr("href");                     // 获取超级链接
        if (href) {
            data.href = href;                                   // 保存原来的超级连接
            $(domElem).attr("href", "javascript:void(0)");      // 从新设置
        }
        if (domElem.onclick) {                                  // 是否有点击事件处理
            data.onclick = domElem.onclick;
            domElem.onclick = null;                             // 取消掉
        }
        var eventData = $(domElem).data("events") || $._data(domElem, 'events');
        if (eventData && eventData["click"]) {
            var clickHandlerObjects = eventData["click"];
            data.savedHandlers = [];
            for (var i = 0; i < clickHandlerObjects.length; i++) {
                if (clickHandlerObjects[i].namespace != "menu") {
                    var handler = clickHandlerObjects[i]["handler"];
                    $(domElem).unbind('click', handler);
                    data.savedHandlers.push(handler);
                }
            }
        }

        $(domElem).addClass("l-btn-disabled");                  // 使用样式
    } else {
        data.options.disabled = false;                          // 启用按钮
        if (data.href) {                                        // 恢复原来的超级连接
            $(domElem).attr("href", data.href);
        }
        if (data.onclick) {                                     // 恢复原来的点击事件处理
            domElem.onclick = data.onclick;
        }
        if (data.savedHandlers) {
            for (var i = 0; i < data.savedHandlers.length; i++) {
                $(domElem).click(data.savedHandlers[i]);
            }
        }

        $(domElem).removeClass("l-btn-disabled");
    }
}

2.为easyui打个补丁,重写enable和disable:

/**
 * linkbutton方法扩展
 * @param {Object} jq
 */
$.extend($.fn.linkbutton.methods, {
    /**
     * 激活选项(覆盖重写)
     * @param {Object} jq
     */
    enable: function(jq){
        return jq.each(function(){
            var state = $.data(this, 'linkbutton');
            if ($(this).hasClass('l-btn-disabled')) {
                var itemData = state._eventsStore;
                //恢复超连接
                if (itemData.href) {
                    $(this).attr("href", itemData.href);
                }
                //回复点击事件
                if (itemData.onclicks) {
                    for (var j = 0; j < itemData.onclicks.length; j++) {
                        $(this).bind('click', itemData.onclicks[j]);
                    }
                }
                //设置target为null,清空存储的事件处理程序
                itemData.target = null;
                itemData.onclicks = [];
                $(this).removeClass('l-btn-disabled');
            }
        });
    },
    /**
     * 禁用选项(覆盖重写)
     * @param {Object} jq
     */
    disable: function(jq){
        return jq.each(function(){
            var state = $.data(this, 'linkbutton');
            if (!state._eventsStore)
                state._eventsStore = {};
            if (!$(this).hasClass('l-btn-disabled')) {
                var eventsStore = {};
                eventsStore.target = this;
                eventsStore.onclicks = [];
                //处理超连接
                var strHref = $(this).attr("href");
                if (strHref) {
                    eventsStore.href = strHref;
                    $(this).attr("href", "javascript:void(0)");
                }
                //处理直接耦合绑定到onclick属性上的事件
                var onclickStr = $(this).attr("onclick");
                if (onclickStr && onclickStr != "") {
                    eventsStore.onclicks[eventsStore.onclicks.length] = new Function(onclickStr);
                    $(this).attr("onclick", "");
                }
                //处理使用jquery绑定的事件
                var eventDatas = $(this).data("events") || $._data(this, 'events');
                if (eventDatas["click"]) {
                    var eventData = eventDatas["click"];
                    for (var i = 0; i < eventData.length; i++) {
                        if (eventData[i].namespace != "menu") {
                            eventsStore.onclicks[eventsStore.onclicks.length] = eventData[i]["handler"];
                            $(this).unbind('click', eventData[i]["handler"]);
                            i--;
                        }
                    }
                }
                state._eventsStore = eventsStore;
                $(this).addClass('l-btn-disabled');
            }
        });
    }
});

须要注意的是:

下面的两种写法有区别:

(1).onclick事件放在<a>标签元素中.

:<a onClick="addEprjListNormConsumption();" href="javascript:void(0);"  class="easyui-linkbutton "/>,

能够$(".needDisable").linkbutton("disable"),禁用linkbutton的点击事件

(2).onclick事件单独列出来,例如:

<a href="javascript:void(0);"  class="easyui-linkbutton _mu"/>

$("._mu").click(function(){...});

这样的话,这么写$(".needDisable").linkbutton("disable")会禁用按钮,可是不会禁用onclick事件,解决办法,在其onclick事件中,写上return false;便可

参考:http://www.easyui.info/archives/792.html

相关文章
相关标签/搜索