JavaScript 客户端JavaScript之事件(DOM API 提供模块之一)

时间:2023-05-01 12:08:14
具有交互性的JavaScript程序使用的是事件驱动的程序设计模型。
目前使用的有3种完全不同的不兼容的事件处理模型。
1、原始事件模型
(一种简单的事件处理模式)
一般把它看作0级DOM API的一部分内容,所有启用了JavaScript的浏览器都支持它,因此它具有可移植性。
2、标准事件模型
(一种强大的具有完整性的事件模型)
 2级DOM标准对它进行了标准化,除IE以外的所有浏览器都支持它。
3、IE事件模型
   想用高级事件处理特性的JavaScript程序设计者必须为IE浏览器编写特定的代码。
17.1 基本事件处理(原始事件模型)
17.1.1事件和事件类型
    在原始事件模型中,事件是浏览器内部提取的,JavaScript代码不能直接操作事件。
原始事件模型的事件类型:响应事件时调用的事件句柄名。
在此模型中,用HTML元素的属性(和相关的JavaScript对象的相应属性)设置事件处理代码。
如:
<input type="text" onclick="......."/>
document.getElementsByTag("input").onclick=.......;
事件句柄和支持它们的HTML元素
onbort                图像装被中断(img支持
onblur
onchange          获取焦点,尽管变了值后,失去焦点时触 发,input select textarea支持
onclick
oncbclick
onerror
onfocus
onkeydown
onkeypress
onkeyup
onload
onmousedown
onmousemove
onmouseout
onmouseover
onmouseup
onrest
onresize
onselect            选中文本 input textera支持
onsubmit
onunload
分类 1、原始事件(输入事件)由鼠标和键盘产生
2、高级事件:特定环境下发生,如onload
也可以分类为 设备相关 设备无关
17.1.2作为HTML属性的事件句柄会隐式声明一个函数。(前提是值为一段代码,而不是函数名称)
<input type="text" onclick="......."/>
17.1.3 作为JavaScript属性的事件句柄
document.getElementsByTag("input").onclick=.......;
这个属性必须全为小写,在此事件够本中,this引用发生事件的对象。
显式调用事件够本
由于JavaScript事件句柄属性的值是函数,所以可调用 如:
document.mform.onsubmit()
注:直接调用事件够本的方法并不是模拟事件发生时的真正状态,只是执行这个函数而已。
显式调用事件处理函数的原因之一是,可以用JavaScript扩展HTML代码定义的事件处理函数,假定想在用户点击
按钮时执行特殊的动作,但是又不想破坏HTML文档自身定义的onclick事件句柄。
可以先保存这个事件句柄函数,然后重新设置的时候,将这个函数,放入新的事件句柄函数当中,然后再在其后添加新的代码。
如:
var b=document.myform.mybutton;
var oldHandler=b.onclick;
function newHandler()
{
.....
}
b.onclick=function()
{
    oldHandler();
    newHandler();
}
注:现在大多浏览器多次设置事件句柄属性值,并不会覆盖上一个。只会叠加。
17.1.4事件句柄的返回值
通常,如果浏览器执行某种默认动作来响应一个事件,那么可以返回false阻止浏览器执行那个动作。
这些事件句柄是
onsubmit、onclick、onkeydown、onkeypress、onmousedown、onmouawup的onreset
注:事件句柄从来不要求显式地返回值。如果不返回值,就会发生默认的动作。
代码:
 代码:
<input type="text" id="name" /> window.onload = function () {
document.getElementById("name").onkeypress = function (event) {
if (event.keyCode >115)
{
return false;
}
}
}
17.1.5 事件句柄和this关键字
在事件句柄调用时,它是作为产生事件的元素的方法调用的,所以关键字this引用了那个目标元素。
17.1.6事件句柄的作用域
    定义为HTML属性的事件句柄,它所执行的作用域和其他函数的作用域不同。
JavaScript中的函数运行在词法作用域中。这意味着函数在定义它们的作用域中运行,而不在调用它们的作用域中运行。
定义 为HTML属性的事件句柄具有更加复杂的作用域链。它们的作用域链的头是调用对象,传递给事件句柄
的所有参数都是在这里定义的,它们和事件句柄主体中声明的局部变量一样,
但是事件句柄的作用域链中的下一个对象却并非全局对象,而是触发事件句柄的对象。
如:
<input type='button' onclick="var a=0;"/>
此a会被定义为此button对象属性
作用域如:
  <form>
<%-- this代表事件发生的目标对象 --%>
<input id="b1" type="button" value="Button1" onclick="alert(this.form.b2.value);" />
<%-- this代表事件发生的目标对象,所以可以省略this --%>
<input id="b2" type="button" value="Button2" onclick="alert(form.b1.value);" />
<%--form在此匿名函数的定义域(document) 当中,所以可以省略form --%>
<input id="b3" type="button" value="Button3" onclick="alert(b4.value);" />
<%--此匿名函数的定义的作用域为window.document当中,所以可以省略document --%>
<input id="b4" type="button" value="Button4" onclick="alert(getElementById('b3').value);" />
<%--此匿名函数的定义的作用域为window.document当中,但document对象也是运行在window当中,所以可以省略window --%>
</form>
注:DOM是作为BOM的组成部分存在的。
注:form本身应该属于document 同理 getElementById也是document对象的方法。
在此事件句柄函数中,寻找局部变量路径:调用对象,form对象,document对象,window对象。
由以上代码可以看出
此事件句柄的作用域链不会随定义句柄的对象终止,而且至少包括包含按钮的<form>
元素和包含表单的docuement对象。因为函数是在定义它的作用域中运行的。
作用域链的最终对象都是window对象,因为它总是在客户端javascript中。
以上b4的代码与以下相同
 var b4 = document.getElementById("b4");
b4.onclick = function () {
with (document) {
with (this.form) {
with (this) {
alert(b3.value);
}
}
}
}
注:以上出现的两个this,因为使用了with,所以分别代表document对象,与docuemnt.form对象。
因为在这种事件句柄当中 document对象比window对象更在作用域链前,所以如果在此事件句柄使用标识符 open()
而不是指定window,会默认使用document的open()方法,或者其它类似相关的情况。都会造成不同的情况。
防止这种意味的情况,使用这种句柄时,尽量简单,理想的方法是,让它们只调用在别的地方定义的全局函数。
注:以上讨论的作用域链都只是针对定义为HTML属性的事件句柄。
如果把一个函数赋予适当的javascript事件句柄属性来设置事件句柄,那么根本不涉及特殊的作用域链。
函数在定义它的作用域中执行,这几乎总是全局作用域(在此定义的作用域为window对象,而不是window.document对象),
如果是嵌套函数,情况会比较特殊。
以上待加深理解。 所有的节点元素对象都存在于DOM树中,DOM树的所有节点都存在于window当中。
全局独立作用域,且只有一个。
17.2      2级DOM中高级事件处理
17.2.1
在0级DOM事件模型中,当浏览器把事件分派给发生事件的文档元素时,如果对象具有合适的事件句柄,就运行这个句柄,不
用执行其他操作。
但在这种高级事件模型中,目标的每个祖先元素也有机会处理那个事件。
当事件发生在目标对象上时:
事件传播分三个阶段进行:
    第一、在捕捉(Capturing)阶段,事件从Document对象沿着文档树向下传播给目标节点(不再往下传播)。如果目标的任何一个
祖先(不是目标自身)专门注册了捕捉事件句柄,那么在事件传播的过程中,就会运行这些句柄。
    第二、下一个阶段发生在目标节点本身,直接注册在目标上的适合的事件句柄运行。(与0级事件模型提供的事件处理方法
相似。
    第三、起泡阶段(Bubbling),事件将从目标元素向上传播或起泡至Document对象的文档层次(直到document对象中的body 或者 html标记)。
注:所有事件都受事件传播的捕捉阶段的支配,但并非所有类型的事件都起泡。
一般,原始输入事件起泡(如click),而高级语义事件不起泡。
1、在事件传播的过程中,任何事件句柄都可调用表示那个事件的Event对象的stopPropagation()方法,停止事件的进一步传播。
2、有些事件会引发浏览器执行相关的默认动作。这样的默认动作只在事件传播的三个阶段都完成之后才分执行。可使用
Event对象的preventDefault()方法阻止默认动作发生。
关于事件传播有一个重要的细节。在0级模型中,只能为特定对象的特定类型的事件注册一个句柄。在2级模型中可以注册任意
多个处理函数,且将在事件传播的捕捉阶段或起泡阶段调用。
17.2.2 事件句柄的注册
    通过调用对象的addEventListener()方法为特定元素注册事件句柄。
移除使用removeEventListener()
注:此外的事件类型全为小写,且无前缀"on"
此方法有三个参数
a、事件类型名 如 click change focus等
b、事件句柄函数  在此事件句柄函数中,this关键字所引用的对象正是其上注册了这个句柄的对象
c、布尔值              true,则指定的事件句柄将在事件捕捉阶段用于捕捉事件。
                              false,则事件句柄就是常规的,当事件直接发生在对象上,或发生在元素的子女上,又向上起泡到该
                                        元素时,该句柄将被触发。
  <div id="div1" style="height:100px;height:40px;border:solid 1px red;">
<input id="b1" type="button" value="Button1" />
</div>
 window.onload = function () {
var b1 = document.getElementById("b1");
b1.addEventListener("click", function () {
alert("this is b1");
})
var div1 = document.getElementById("div1");
div1.addEventListener("click", function () {
alert("this is div1");
}, true) }
注:可以给同一个对象的同一个类型的事件,注册多个处理函数,在该类型的事件在那个对象上发生时,被注册的所有
函数都被调用(但注册的函数是同一个时,第一次注册后的所有注册都将被忽略。),且调用顺序不确定。
通过,临时注册一个事件处理函数,用完后迅速删除它比较有效。如mousedown mousemove mouseup 实现拖动元素。
如:代码
 function mousemoveFun(event) {
var left = event.clientX - div1.style.width.substring(0, div1.style.width.length - 2) / 2;
var top = event.clientY - div1.style.height.substring(0, div1.style.height.length - 2) / 2;
div1.style.left = left + "px";
div1.style.top = top + "px";
}
window.onload = function () {
var b1 = document.getElementById("b1");
var div1 = document.getElementById("div1");
var ismove = false;
div1.addEventListener("mousedown", function (event) {
document.addEventListener("mousemove", mousemoveFun, true);
ismove = true;
})
document.addEventListener("mouseup", function (event) {
if (!ismove) {
return;
}
ismove = false;
document.removeEventListener("mousemove", mousemoveFun, true);
var left = event.clientX - div1.style.width.substring(0, div1.style.width.length - 2) / 2;
var top = event.clientY - div1.style.height.substring(0, div1.style.height.length - 2) / 2;
div1.style.left = left + "px";
div1.style.top = top + "px";
}, true);
//注意:mousemove和mouseup事件的句柄被注册为捕捉事件句柄,因为用户移动鼠标的速度比跟跟随它移动的文档元素快,所以其中一些事件发生在
//原始目标元素外部,没有捕获,事件可能无法分配给正确的句柄
}
17.2.4  把对象注册为事件句柄
    在句柄函数中,调用某个对象的方法(并传递event参数),或直接把了此方法的对象直接传递给事件监听对象。
在此方法中,this引用的是方法所属的对象。
addEventListener()
代码:
  <input id="b1" type="button" value="Button1" />
var o = new Object();
o.value = "js对象";
o.func = function (event) {
//this引用的 o 而不是文档元素对象
alert(this.value);
}
window.onload = function () {
var b1 = document.getElementById("b1");
b1.addEventListener("click", function (event) {
o.func(event);
}, true);
}
17.2.5    事件模块和事件类型
    2级DOM标准是模块化的,可以用以下测试浏览器是否支持 EventAPI
document.implmentation.hasFeature("Events","2.0");
MutationEvents模块
当文档结构发生变化时,Mutation事件会触发。
表17.2 (所有以下模块共同组成 Event接口模块)
模块名                        事件接口                        事件类型
HTMLEvents                Event                    abort,blur,change,error,focus,
                                                                  load,rest,resize,sroll,select,submit,unload
MouseEvents            MouseEvent           click,mousedown,mousemove,mouseout 
                                                                  mouseover,mouseup
UIEevents                    UIEvent                 DOMActivate,DOMFocusIn,DOMFocusOut
MutationEvents
17.2.6 Event接口
当事件发生时,2级DOM API 提供了事件的额外信息(如何时何地发生的),作为传递给事件句柄的对象的属性。
每个事件模块有一个相关的事件接口,该接口声明了该事件类型的详细信息如 表17.2
1、HTMLEvent
    使用了Event接口,其它两个使用的是Event子接口。
有以下额外信息(属性)
type,target,currentTarget,eventPhase,timestamp,bubblues,cancelable
注:子接口除了实现Event接口的所有属性,还定义了自己特有的属性
2、UIEvent
    UIEvent是Event接口的子接口。
    view
    detail
3、MouseEvent
    MouseEvent接口继承Event接口和UIEvent接口的所有属性和方法。并且有自己特有的属性。
    button
    altKey,ctrlKey,metaKey,ShiftKey
    clientX,clientY
    screenX,screenY
    relatedTarget
4、MutationEvents
17.2.7 混合事件模型
    支持2级DOM的浏览器。都将0级事件注册自动使用addEventListener()方法
17.3 Internet Explorer事件模型 
17.3.1 IE Event对象
属于
type
srcElement
button
clientX,clientY
offsetX,offsetY
altKey,ctrlkey,shiftkey
keycode
formElement toElement
canclebubble
returnvalue
17.3.2
作为全局变量的IE Event对象
window.event 只有IE标准下,才为全局变量,其它标准为参数传递。
兼容:
function(event)
{
    var e=event||window.event;
}
17.3.3      IE事件句柄的注册
attachEvent()
detachEvent()
1、IE事件不支持事件传播
2、只有两个参数:事件类型(包含前缀on),句柄函数
3、被注册的句柄函数全作为全局函数调用,this代表window
4、同一个函数注册同一个事件中会被调用多次
17.3.4
IE中的事件起泡
(向上起泡)
IE Event没有stopPropagation(),只可采用以下方法来阻止向上进一步起泡传播。
window.event.cancelBubble=true;
只适用于当前事件,新事件生成时,自动重新恢复为false。
17.3.5 捕获鼠标事件
在处理鼠标事件时,需要使用到这两个方法
setcapture()
releasecaptuer()
17.3.6 attachEvent()和this关键字
this代表window,且IE中 Event无currentTarget,srcElement在事件起泡后,会发生改变。
如果想要编写一个通用的可以在任何元素上注册的事件句柄,且需要知道它注册于哪个元素上,不能使用
attachEvent()
必须使用:1、0级事件模型注册句柄
                   2、围绕句柄定义一个包围函数并注册这个包围函数
 如:
function genericHandler()
{
//...
}
var element=document.getElementById("div1")
element.onmouseover=genericHandler();
//或者
element.attachEvent("onmouseover",function()
{
        genericHandler.call(element,event);//明确指定函数的调用对象
});
17.3.8具有IE兼容性的事件模型(实例代码)
p414 
17.4鼠标事件 
17.5按钮事件
keydown keypress keyup
在keydown与Keyup之间,会产生多个keypress事件,
两个属性
keyCode            ASCII码
charCode         字符
IE中只有keyCode(keydown表示ASCII码,keypress时表示字符)
字符码转换:Sting.fromCharCode()
17.5.3
过滤键盘输入
通过return false
当keyup发生时,value值已经更新了(input textarea)
keypress发生后,把输入的字符回到自己的value属性后面。
17.6 onload事件
当文档完全载入之后,启动window.onload事件
可以为这事件注册事件句柄函数。
17.7合成事件(自定义事件)
2级DOM及IE事件模型都允许
1、在DOM事件模型中
使用Document.createEvent()创建一个合成事件,使用Event.initEvent() UIEvent.initUIEvent() 
或MouseEvent.initMouseEvent() 初始化事件,使用对象的dispathEvent() 分派(绑定)。
2、IE中
Document.createEventObject()创建 
对象方法 fireEvent()分派
本质就是创建一个自定义的事件。
注:使用dispatchEvent()和fireEvent()分派合成事件不需要排除,且是异步处理的。
注:随着浏览器的版本不断更新,更多event参数还得根据浏览器而定
代码:
 <input id="b1" type="button" value="Button1" />
var e;
window.onload = function () {
var b1 = document.getElementById("b1");
b1.addEventListener("click", function (event) {
dataEvent.receive("b1", e, function (event) {
alert(event.datatype + ":" + event.data);
});
}, true);
var e = dataEvent.send("b1", "name", "小三");
}
var dataEvent = {};
dataEvent.send = function (target, datatype, data) {
if (typeof target == "string") {
target = document.getElementById(target);
}
if (document.createEvent) {
var e = document.createEvent("Events");//此处使用的是接口名 Events UIEvents MouseEvents MutationEvents 有可能没有s
e.initEvent("dataavailable", true, false);//初始化自定义事件
}
else if (document.createEventObject) {
var e = document.createEventObject();
}
else return;
//自定义事件的一些属性
e.datatype = datatype;
e.data = data;
return e;
}
dataEvent.receive = function (target, event, handler) {
if (typeof target == "string")
{ target = document.getElementById(target); }
if (target.addEventListener)
{ target.addEventListener("dataavailable", handler, false); }
else if (target.attachEvent) {
target.attachEvent("ondataavailable", handler);
}
if (target.dispatchEvent) {
target.dispatchEvent(event);
}
else if (target.fireEvent) {
target.fireEvent("ondataavailable", event);
}
}