2013年7月

第二章:事件和监听《Developing JavaScript Web Applications》学习笔记

监听事件

绑定事件监听的函数addEventListener(),3 个参数:type(比如click),
listener(绑定的事件函数,比如callback)及useCapture(事件顺序,使用捕获,否则冒泡)。
使用removeEventListener() 来移除事件监听,参数和传入addEventListener() 的一样。如果监听的函数是匿名函数,没有任何引用指向它,在不销毁这个元素的前提下,这个监听是无法被移除的.

/*element.addEventListener(type,listener,useCapture);*/
btn1Obj.addEventListener("click",method1,false);
btn1Obj.addEventListener("click",method2,false);
btn1Obj.addEventListener("click",method3,false);//执行顺序为method1->method2->method3
btn1Obj.romoveEventListener("click",method3,false);//清除事件method3

在IE下,使用attachEvent()和 detachEvent()分别来进行事件监听和移出。IE下事件是冒泡的方式,所以没有第三个参数(useCapture)。事件type和w3c的函数不太一样,需要加上前缀on;

var btn1Obj = document.getElementById("btn1");
 //object.attachEvent(event,function);
btn1Obj.attachEvent("onclick",method1);
btn1Obj.attachEvent("onclick",method2);
btn1Obj.attachEvent("onclick",method3);//执行顺序为method3->method2->method1
btn1Obj.detachEvent("onclick",method3);//清除事件method3

取消事件

当事件冒泡时,可以通过stopPropagation() 函数来终止冒泡,这个函数是event 对象
中的方法。比如这段代码,任何父节点的事件回调都不会触发:

button.addEventListener("click", function(e){
e.stopPropagation();
/* ... */
}, false);

//IE浏览器:
event.cancelBubble=true;

另外,通过调用event 对象的preventDefault() 函数来阻止默认行为。

事件对象

var demo=document.getElementById("demo");
demo.addEventListener("click",function(e){
  console.log(e);
},false)
//控制台输出e对象:
altKey: false
bubbles: true
button: 0
cancelBubble: false
cancelable: true
charCode: 0
clientX: 75
clientY: 172
clipboardData: undefined
ctrlKey: false
currentTarget: null
dataTransfer: null
defaultPrevented: false
detail: 1
eventPhase: 0
fromElement: null
keyCode: 0
layerX: 75
layerY: 172
metaKey: false
offsetX: 67
offsetY: 105
pageX: 75
pageY: 172
path: NodeList[0]
relatedTarget: null
returnValue: true
screenX: 75
screenY: 712
shiftKey: false
srcElement: div#demo
target: div#demo
timeStamp: 1405400498039
toElement: div#demo
type: "click"
view: Window
webkitMovementX: 0
webkitMovementY: 0
which: 1
x: 75
y: 172

切换上下文

当使用浏览器内置的addEventListener() 时,上下文从局部变量切换为目标HTML 元素:

new function(){
this.appName = "wem";
document.body.addEventListener("click", function(e){
// 上下文发生改变,因此appName 是undefined
alert(this.appName);
}, false);
};

要想保持原有的上下文,需要将回调函数包装进一个匿名函数,然后定义一个引用指向它。我们在第1 章已经提到这种模式,即使用代理函数来保持当前的上下文。这在jQuery 中也是一种很常用的模式,包括一个proxy() 函数,只需将指定的上下文传入函数即可:

$("signinForm").submit($.proxy(function(){ /* ... */ }, this));

事件委托

我们开发一个列表(ul>li)点击事件,为每个li绑定事件显然这样做不太好,这时候用到事件委托。

// 在ul 列表上做了事件委托
list.addEventListener("click", function(e){
if (e.currentTarget.tagName == "li") {
/* ... */
return false;
}
}, false);

jQuery的delegate() 函数正是解决事件委托而建立的。用法:

// 不要这样做,这样会给每个li 元素都添加事件监听(非常浪费)
$("ul li").click(function(){ /* ... */ });
// 这样只会添加一个事件监听
$("ul").delegate("li", "click", /* ... */);

自定义事件

// 绑定自定义事件
$(".class").bind("refresh.widget",function(){});
// 触发自定义事件
$(".class").trigger("refresh.widget");

pub/sub模式

var PubSub = {
subscribe: function(ev, callback) {
// 创建 _callbacks 对象,除非它已经存在了
var calls = this._callbacks || (this._callbacks = {});
// 针对给定的事件key 创建一个数组,除非这个数组已经存在
// 然后将回调函数追加到这个数组中
(this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);
return this;
},
publish: function() {
// 将arguments 对象转换为真正的数组
var args = Array.prototype.slice.call(arguments, 0);
// 拿出第1 个参数,即事件名称
var ev = args.shift();
// 如果不存在_callbacks 对象,则返回
// 或者如果不包含给定事件对应的数组
var list, calls, i, l;
if (!(calls = this._callbacks)) return this;
if (!(list = this._callbacks[ev])) return this;
// 触发回调
for (i = 0, l = list.length; i < l; i++)
list[i].apply(this, args);
return this;
}
};
// 使用方法
PubSub.subscribe("wem", function(){
alert("Wem!");
});
PubSub.publish("wem");

jquery实现的简易的pub/sub

(function($){
var o = $({});
$.subscribe = function() {
o.bind.apply( o, arguments );
};
$.unsubscribe = function() {
o.unbind.apply( o, arguments );
};
$.publish = function() {
o.trigger.apply( o, arguments );
};
})(jQuery);

这里的API 和jQuery 的bind() 及trigger() 函数的参数一致。惟一的区别就是这两个
函数直接保存在jQuery 对象中,且名叫publish() 和subscribe() :

$.subscribe( "/some/topic", function( event, a, b, c ) {
console.log( event.type, a + b + c );
});
$.publish( "/some/topic", "a", "b", "c" );

jquery自定义插件

$.fn.tabs = function (control) {
var element = $(this);
control = $(control);
element.delegate("li", "click", function () {
// 遍历选项卡名称
var tabName = $(this).attr("data-tab");
// 在点击选项卡时触发自定义事件
element.trigger("change.tabs", tabName);
});
// 绑定到自定义事件
element.bind("change.tabs", function (e, tabName) {
element.find("li").removeClass("active");
element.find(">[data-tab='" + tabName + "']").addClass("active");
});
element.bind("change.tabs", function (e, tabName) {
control.find(">[data-tab]").removeClass("active");
control.find(">[data-tab='" + tabName + "']").addClass("active");
});
// 激活第1 个选项卡
var firstName = element.find("li:first").attr("data-tab");
element.trigger("change.tabs", firstName);
return this;
};

//how to use
$(document).ready(function(){
$("#tabs").tabs("#tabsContent");
});

See the Pen cDaBb by buer (@buer) on CodePen.

第一章:MVC和类《Developing JavaScript Web Applications》学习笔记

一.什么是MVC

MVC 是一种设计模式,它将应用划分为3 个部分:数据(模型)、展现层(视图)和用户交互层(控制器)。换句话说,一个事件的发生是这样的过程:

  1. 用户和应用产生交互。
  2. 控制器的事件处理器被触发。
  3. 控制器从模型中请求数据,并将其交给视图。
  4. 视图将数据呈现给用户。

二.关于类

1.类生成器建立

var Class = function(){
    var klass = function(){
        this.init.apply(this, arguments);
    };
    klass.prototype.init = function(){};
    return klass;
};
var Person = new Class();
Person.prototype.init = function(){
    // 基于Person 的实例做初始化
};
// 用法:
var person = new Person();

2.extend方法,给类添加属性

klass.extend = function (obj) {
    var extended = obj.extended;
    for (var i in obj) {
    klass[i] = obj[i];
    }
    if (extended) extended(klass)
};

3.include方法,用于添加实例方法

// 给实例添加属性
klass.include = function (obj) {
    var included = obj.included;
    for (var i in obj) {
    klass.fn[i] = obj[i];
    }
    if (included) included(klass)
};

4.基于原型prototype类继承的研究

if (parent) {
    var subclass = function() { };
    subclass.prototype = parent.prototype;
    klass.prototype = new subclass;
};

5.javascript作用域,事件代理。proxy方法,控制“类”库的作用域

// 添加一个proxy 函数
klass.proxy = function(func){
    var self = this;
    return (function(){
                return func.apply(self, arguments);
            });
}
// 在实例中也添加这个函数
klass.fn.proxy = klass.proxy;

6.匿名函数,局部变量

(function(exports){
    var foo = "bar";
    // 将变量暴露出去
    exports.foo = foo;
})(window);

以上研究,详见以下代码:

var Class=function(parentClass){
  var kclass=function(){
    this.init.apply(this,arguments);
  };
  if(parentClass){
    var subClass=function(){};
    subClass.prototype=parentClass.prototype;
    kclass.prototype=new subClass();
  }
  kclass.prototype.init=function(){};
  kclass.fn=kclass.prototype;
  kclass.fn.parent=kclass;
  kclass.proxy=function(func){
    var self=this;
    return (function(){
        func.apply(self,arguments);
    });
  },
  kclass.fn.proxy = kclass.proxy;

kclass.extend=function(obj){
for(var i in obj){
kclass[i]=obj[i];
}
if(obj.callback)obj.callback(kclass);
};
kclass.include=function(obj){
for(var i in obj){
kclass.fn[i]=obj[i];
}
if(obj.callback)obj.callback(kclass);
};
return kclass;
};

//第一个例子:类生成,实例生成
var Person=new Class();
Person.extend({
"say":function(say){
console.log(say);
}
});
Person.include({
"getWork":function(workName){
return workName;
}
});
Person.say("i can say");
var lili=new Person();

lili.getWork("计算机");
//第二个例子:继承
var Animal = new Class();
Animal.extend({
say: function(){
console.log('say');
}
});
Animal.include({
breath: function(){
console.log('breath');
}
});
var Cat = new Class(Animal);
// 用法
var tommy = new Cat();
//tommy.say(); say不是原型方法 无法被继承
tommy.breath();
//第三个例子:控制作用域proxy函数
var Button = new Class();
Button.include({
init: function(element){
this.element = jQuery(element);
// 代理了这个click 函数
this.element.click(this.proxy(this.click));
},
click: function(){ console.log(this)}
});

var click_m_button=new Button();
click_m_button.init("#m-button");

See the Pen sDixq by buer (@buer) on CodePen.