当前位置: 代码迷 >> JavaScript >> ExtJs源码分析与学习―ExtJs事件机制(5)
  详细解决方案

ExtJs源码分析与学习―ExtJs事件机制(5)

热度:78   发布时间:2012-10-06 17:34:01.0
ExtJs源码分析与学习―ExtJs事件机制(五)

????? 最近一直忙着做产品,所以好久没有写文章了,下面接着把ExtJs事件机制最后一点内容写完。主要是介绍Ext提供的三个辅助实现事件类――快捷键、导航键和鼠标按键事件。

?

快捷键 Ext.KeyMap

??? 该功能的实现被封装在类Ext.KeyMap中

Ext.KeyMap = function(el, config, eventName){
    this.el  = Ext.get(el);
    this.eventName = eventName || "keydown";
    this.bindings = [];
    if(config){
        this.addBinding(config);
    }
    this.enable();
};

??? 该类实现分为三步,首先找到注册快捷键的元素el,然后把config参数转换为事件的监听函数this.addBinding(config),最后再注册该监听函数this.enable()。先看addBinding方法的实现

?

addBinding : function(config){
        if(Ext.isArray(config)){
            Ext.each(config, function(c){
                this.addBinding(c);
            }, this);
            return;
        }
        var keyCode = config.key,
            fn = config.fn || config.handler,
            scope = config.scope;

	if (config.stopEvent) {
	    this.stopEvent = config.stopEvent;    
	}	

        if(typeof keyCode == "string"){
            var ks = [];
            var keyString = keyCode.toUpperCase();
            for(var j = 0, len = keyString.length; j < len; j++){
                ks.push(keyString.charCodeAt(j));
            }
            keyCode = ks;
        }
        var keyArray = Ext.isArray(keyCode);
        
        //代理按键的监听处理函数,对配置对象中的fn/handle函数进行代理加工处理
        var handler = function(e){
        	//如果指定'shift', 'ctrl', 'alt',而事件对象没有按下指定的'shift', 'ctrl', 'alt',那么就不进行处理
            if(this.checkModifiers(config, e)){
                var k = e.getKey();
                if(keyArray){
                    for(var i = 0, len = keyCode.length; i < len; i++){
                        if(keyCode[i] == k){
                          if(this.stopEvent){
                              e.stopEvent();
                          }
                          fn.call(scope || window, k, e);
                          return;
                        }
                    }
                }else{
                    if(k == keyCode){
                        if(this.stopEvent){
                           e.stopEvent();
                        }
                        fn.call(scope || window, k, e);
                    }
                }
            }
        };
        this.bindings.push(handler);
	},

?

?config配置项支持以下属性

属性??? 类型???????????? 描述
----------? ---------------? ----------------------------------
key???????? String/Array????? 进行处理的单个keycode或keycodes组成的数组
shift?????? Boolean?????????? True:只有shift按下的的同时处理key (默认false)
ctrl??????? Boolean?????????? True:只有ctrl按下的的同时处理key (默认false)
handler???? Function????????? 当KeyMap找到预期的组合键时所执行的函数
alt???????? Boolean?????????? True:只有alt按下的的同时处理key (默认false)
fn????????? Function????????? 当组合键按下后回调函数
scope?????? Object??????????? 回调函数的作用域
stopEvent?? Boolean?????????? 用来停止事件冒泡,阻止元素默认行为。


再看enable方法,把事件注册到元素上

?

enable: function(){
		if(!this.enabled){
		    this.el.on(this.eventName, this.handleKeyDown, this);
		    this.enabled = true;
		}
	},

为指定的el元素注册事件

例子:常用复制、剪切、粘贴的实现

?

var config = [{//剪切
		key : 'x',
		ctrl : true,
		fn : function() {
			//cut
		},
		scope : this
	},{//复制
		key : 'c',
		ctrl : true,
		fn : function() {
			//copy
		},
		scope : this
	},{//粘贴
		key : 'v',
		ctrl : true,
		fn : function() {
			//paste
		},
		scope : this
	},{//编辑
		key : 'abcdefghigklmnopqrstuvwxyz0123456789',
		ctrl : false,
		shift : false,
		alt : false,
		fn : function(k, e) {
			alert(k);
		},
		scope : this
	}];

var map = new Ext.KeyMap("my-element", config);

?

导航键 Ext.KeyNav

?

??? 导航键的实现被封装在类Ext.KeyNav中,首先看其构造函数

?

?

Ext.KeyNav = function(el, config){
    this.el = Ext.get(el);
    Ext.apply(this, config);
    if(!this.disabled){//如果被禁用则激活
        this.disabled = true;
        this.enable();//注册监听函数
    }
};

??? 再看注册监听函数

?

   enable: function() {
        if (this.disabled) {
            if (Ext.isSafari2) {
                // call stopKeyUp() on "keyup" event
                this.el.on('keyup', this.stopKeyUp, this);
            }

            this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
            this.disabled = false;
        }
    },

????? IE及其他一些浏览器的keyPress事件不会对非字母数字键进行冒泡,所以采用keyDown事件替代keyPress事件。而该事件的处理函数relay起了代理作用

?

relay : function(e){
        var k = e.getKey(),
            h = this.keyToHandler[k];
        if(h && this[h]){
            if(this.doRelay(e, this[h], h) !== true){
                e[this.defaultEventAction]();
            }
        }
    },

?

????? KeyToHandler是键名和键值对应的hash表,通过event对象获得了键值之后,再和Ext.KeyNav类中12个导航键名比较,看看其是否为导航键,如果是,再看看有没有注册的处理函数,有才运行该监听函数。

该导航键共提供了12个,分别为
keyToHandler : {
??????? 37 : "left",
??????? 39 : "right",
??????? 38 : "up",
??????? 40 : "down",
??????? 33 : "pageUp",
??????? 34 : "pageDown",
??????? 46 : "del",
??????? 36 : "home",
??????? 35 : "end",
??????? 13 : "enter",
??????? 27 : "esc",
??????? 9? : "tab"
},

例子

?

var nav = new Ext.KeyNav("my-element", {
    "left" : function(e){
        this.moveLeft(e.ctrlKey);
    },
    "right" : function(e){
        this.moveRight(e.ctrlKey);
    },
    "enter" : function(e){
        this.save();
    },
    scope : this
});

?

鼠标按住事件 Ext.util.ClickRepeater


???? 鼠标按住事件就是用鼠标按住某个元素,会根据指定的时间间隔来反复地执行同样的动作。该功能的实现被封装在类Ext.util.ClickRepeater,首先看构造函数

?

constructor : function(el, config){
        this.el = Ext.get(el);
        this.el.unselectable();//元素内容不能选择

        Ext.apply(this, config);

        this.addEvents(
        /**
         * @event mousedown
         * 当鼠标按下的时候触发。
         * Fires when the mouse button is depressed.
         * @param {Ext.util.ClickRepeater} this
         * @param {Ext.EventObject} e
         */
        "mousedown",
        /**
         * @event click
         * 当元素被按下接受到按下的消息的时候出发,比mousedown事件慢。
         * Fires on a specified interval during the time the element is pressed.
         * @param {Ext.util.ClickRepeater} this
         * @param {Ext.EventObject} e
         */
        "click",
        /**
         * @event mouseup
         * 当鼠标释放后触发。
         * Fires when the mouse key is released.
         * @param {Ext.util.ClickRepeater} this
         * @param {Ext.EventObject} e
         */
        "mouseup"
        );

        if(!this.disabled){
            this.disabled = true;
            this.enable();
        }

        // allow inline handler
        if(this.handler){
            this.on("click", this.handler,  this.scope || this);
        }

        Ext.util.ClickRepeater.superclass.constructor.call(this);        
},

????? 该构造函数中调用了this.enable();来处理鼠标按下事件,然后如果配置项声明了handler处理函数,会把该函数注册到cilck事件中。下面看方法enable

?

enable: function(){
        if(this.disabled){
            this.el.on('mousedown', this.handleMouseDown, this);
            if (Ext.isIE){
                this.el.on('dblclick', this.handleDblClick, this);
            }
            if(this.preventDefault || this.stopDefault){
                this.el.on('click', this.eventOptions, this);
            }
        }
        this.disabled = false;
    },

????? 该方法中给元素el注册了mousedown事件,如果是IE浏览器,还注册了dblclick事件,然后根据配置项来阻止默认或冒泡处理,下面看handleMouseDown

?

handleMouseDown : function(e){
        clearTimeout(this.timer);
        this.el.blur();//除去焦点
        if(this.pressClass){
            this.el.addClass(this.pressClass);
        }
        this.mousedownTime = new Date();

        Ext.getDoc().on("mouseup", this.handleMouseUp, this);
        this.el.on("mouseout", this.handleMouseOut, this);

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);

        // Do not honor delay or interval if acceleration wanted.
        if (this.accelerate) {
            this.delay = 400;
        }
        this.timer = this.click.defer(this.delay || this.interval, this, [e]);
},

????? 当用户在某个元素上按下鼠标时,首先让该元素失去焦点,改变样式,同时还分别为鼠标按键松开或移出注册了监听函数,最后按指定的间隔来推迟运行click函数

?

click : function(e){
        this.fireEvent("click", this, e);
        this.timer = this.click.defer(this.accelerate ?
            this.easeOutExpo(this.mousedownTime.getElapsed(),
                400,
                -390,
                12000) :
            this.interval, this, [e]);
},

?

???? 该函数递归调用click函数,如果设置了this.accelerate,时间间隔就会按一定的算法越运行越短,即运行click会越来越快。

  相关解决方案