Javascript学习指南(第2版)笔记(三) 排错、调试、事件捕获
1、排错、调试
2、事件
3、Event对象
4、事件冒泡
5、事件句柄和 this
6、DOM Level 2事件模型
7、测验
1、排错、调试
Firebug是最常用的 Firefox开发工具,同时常用的 Web工具还有 Web Developer toolkit,该工具功能包括页面 HTML和 CSS代码的校验、可访问性检查、查看 CSS和 cookies、检查图像、查看 JavaScript修改后的页面源代码(包括动态源代码)。
2、事件
事件包括:用户界面事件(鼠标、键盘触发的)、逻辑事件(一个处理的结果)、和变化事件(修改文档的操作)。
事件句柄 (Event Handlers)
HTML 4.0 的新特性之一是能够使 HTML 事件触发浏览器中的行为,比如当用户点击某个 HTML 元素时启动一段 JavaScript。下面是一个属性列表,可将之插入 HTML 标签以定义事件的行为。
属性 | 此事件发生在何时... | IE | F | O | W3C |
---|---|---|---|---|---|
onabort | 图像的加载被中断。 | 4 | 1 | 9 | Yes |
onblur | 元素失去焦点。 | 3 | 1 | 9 | Yes |
onchange | 域的内容被改变。 | 3 | 1 | 9 | Yes |
onclick | 当用户点击某个对象时调用的事件句柄。 | 3 | 1 | 9 | Yes |
ondblclick | 当用户双击某个对象时调用的事件句柄。 | 4 | 1 | 9 | Yes |
onerror | 在加载文档或图像时发生错误。 | 4 | 1 | 9 | Yes |
onfocus | 元素获得焦点。 | 3 | 1 | 9 | Yes |
onkeydown | 某个键盘按键被按下。 | 3 | 1 | No | Yes |
onkeypress | 某个键盘按键被按下并松开。 | 3 | 1 | 9 | Yes |
onkeyup | 某个键盘按键被松开。 | 3 | 1 | 9 | Yes |
onload | 一张页面或一幅图像完成加载。 | 3 | 1 | 9 | Yes |
onmousedown | 鼠标按钮被按下。 | 4 | 1 | 9 | Yes |
onmousemove | 鼠标被移动。 | 3 | 1 | 9 | Yes |
onmouseout | 鼠标从某元素移开。 | 4 | 1 | 9 | Yes |
onmouseover | 鼠标移到某元素之上。 | 3 | 1 | 9 | Yes |
onmouseup | 鼠标按键被松开。 | 4 | 1 | 9 | Yes |
onreset | 重置按钮被点击。 | 4 | 1 | 9 | Yes |
onresize | 窗口或框架被重新调整大小。 | 4 | 1 | 9 | Yes |
onselect | 文本被选中。 | 3 | 1 | 9 | Yes |
onsubmit | 确认按钮被点击。 | 3 | 1 | 9 | Yes |
onunload | 用户退出页面。 | 3 | 1 | 9 | Yes |
注:HTML DOM Event 对象 http://www.w3school.com.cn/htmldom/dom_obj_event.asp
内联模式/内联注册模式:
以属性的形式为 HTML元素添加属性的方法。
<body onload="var 1=23; i*=3; alert(i);">
<body onload="calcNumber();">
传统模式/传统注册模式:
通过对象属性将一个函数指派为事件句柄。如果想禁用事件处理,那么可以将事件句柄的值赋为 null。
window.onload = calcNumber;
注:
在 DOM Level 0事件模型中,任何对象只允许指定一个事件句柄。如果你想针对某一特定对象的某个事件指定多个函数,则需要在事件句柄代码中列出他们:
内嵌模式:
<body onload="helloMsg(); helloTwice();">
传统模式:
function helloMsg() {
var helloString = "hello there";
alert(helloString);
helloTwice();
}
当要求浏览器停止执行事件行为,可以从事件句柄函数中返回 false值。
3、Event对象
Event对象是和所有事件相关的。它有一些用来提供事件相关信息的属性,如 Web页面中鼠标点击的位置。
IE将 Event视为 window对象的属性。当处理事件时,将通过程序访问 window对象,其所包含的数据也会相应的进行填充。
var locString = "X = " + window.event.screenX + " Y = " + window.event.screenY;
alert(locString);
}
document.onmousedown = mouseDown;
在基于 Netscape的浏览器(如 Firefox、Mozilla、Opera和 Safari)中,获取 Event对象的方法是不同的:他将作为函数的一部分传入。
var locString = "X = " + theEvent.screenX + " Y = " + theEvent.screenY;
alert(locString);
}
document.onmousedown = mouseDown;
处理这些浏览器差异的方法之一是检查传入函数的 Event对象是否已经实例化。如果是,那么将这个 Event对象赋给一个局部变量;否则,将假定 window.event为该事件,并将其赋给这个局部变量。
var theEvent = nsEvent ? nsEvent : window.event; //判断 nsEvent对象是否已定义。定义则赋值,否则选择 window.event属性
var locString = "X = " + theEvent.screenX + " Y = " + theEvent.screenY;
alert(locString);
}
document.onmousedown = mouseDown;
鼠标 / 键盘属性
属性 | 描述 | IE | F | O | W3C |
---|---|---|---|---|---|
altKey | 返回当事件被触发时,"ALT" 是否被按下。 | 6 | 1 | 9 | Yes |
button | 返回当事件被触发时,哪个鼠标按钮被点击。 | 6 | 1 | 9 | Yes |
clientX | 返回当事件被触发时,鼠标指针的水平坐标。 | 6 | 1 | 9 | Yes |
clientY | 返回当事件被触发时,鼠标指针的垂直坐标。 | 6 | 1 | 9 | Yes |
ctrlKey | 返回当事件被触发时,"CTRL" 键是否被按下。 | 6 | 1 | 9 | Yes |
metaKey | 返回当事件被触发时,"meta" 键是否被按下。 | No | 1 | 9 | Yes |
relatedTarget | 返回与事件的目标节点相关的节点。 | No | 1 | 9 | Yes |
screenX | 返回当某个事件被触发时,鼠标指针的水平坐标。 | 6 | 1 | 9 | Yes |
screenY | 返回当某个事件被触发时,鼠标指针的垂直坐标。 | 6 | 1 | 9 | Yes |
shiftKey | 返回当事件被触发时,"SHIFT" 键是否被按下。 | 6 | 1 | 9 | Yes |
注:HTML DOM Event 对象 http://www.w3school.com.cn/htmldom/dom_obj_event.asp
注:
在 IE中,fromElement 对于 mouseover 和 mouseout 事件,fromElement 引用移出鼠标的元素。
而在 Mozilla/Firefox中,relatedTarget 返回与事件的目标节点相关的节点。
要解决浏览器差异,可以使用:
var oldElement = theEvent.fromElement ? theEvent.fromElement : theEvent.relatedTarget;
4、事件冒泡
如果你为多个嵌套的元素设置相同的事件句柄,会发生什么呢?他们将以什么样的顺序触发?如果你想使得一次只影响一个元素,那么如何保存触发事件句柄的事件呢?
要管理元素堆栈中的事件,其中一个方法就是众所周知的事件冒泡。在事件冒泡中,最内部的元素将首先触发该事件,然后堆栈内的下一个元素触发该事件,以此类推,直到最外面的元素。如果事件句柄被指定给所有元素,那么这些事件将依次被触发。
如果 div2在 div1内部,而 div1又在 document内部,三者都做了事件处理。当事件触发的时候,优先最里面的 div2元素,然后是 div1元素,最后是 document元素。
如果你有一个元素堆栈,并且只希望一个元素触发该事件句柄,那么你可以取消事件冒泡机制。如果在 IE中要取消一个事件冒泡,可以使用 IE中事件的 cancelBubble属性;对于 Mozilla而言,则应该使用事件的 stopPropagation方法。你可以先检查 stopPropagation方法是否存在,然后根据其结果确定使用哪种方法:
if (evnt.stopPropagation) evnt.stopPropagation;
else evnt.cancelBubble = true;
}
//注:IE 7中调试出错,提示信息 "stopPropagation为空或不是对象"。
5、事件句柄和 this
this关键字表示的是当前调用的函数或者方法的所有者。对于一个全局变量而言,它表示的就是 window对象。对于一个对象的方法而言,它表示的就是该对象实例。而在一个事件句柄中,它表示的就是接收到该事件的元素。
alert(this); //在 Firefox中将输出 "[object HTMLDivElement]"
alert("first element event");
}
6、DOM Level 2事件模型
对于堆栈内元素的事件处理,还有一种被称为事件捕捉(event capturing)或 cascade-down的事件处理机制。对于前面这个包含3个元素的示例而言,事件将从最外面的元素开始触发:window -> div1 -> div2。
老事件模型和新的 DOM Level 2事件模型之间,主要区别:
a.新事件模型并不依赖于特定的事件来处理属性;
b.你可以对任何一个对象的任何一种事件注册多个事件句柄函数。
新的事件句柄提供的3个方法:
addEventListener,添加一个事件监听器;
removeEventListener,删除一个事件监听器;
dispatchEvent,分发一个新的事件。
示例:
object.addEventListener('event', eventFunction, boolean);
如 click或 load之类的事件是其第一参数;第二个参数是指定的事件句柄函数;第三个参数用来指定事件是以 cascade-down或者冒泡模式处理的。当第三个参数为 false时这个事件监听器将以冒泡模式处理,否则将把这个事件监听器改成事件捕捉模型。
alert("Capturing: " + this);
}
function bubbleUp(evnt) {
alert("Bubbling: " + this);
}
window.onload = setup;
function setup(evnt) {
//事件捕捉
document.addEventListener("click", cascadeDown, true);
document.forms[0].addEventListener("click", cascadeDown, true);
document.forms[0].elements[0].addEventListener("click", cascadeDown, true);
//事件冒泡
document.addEventListener("click", bubbleUp, false);
document.forms[0].addEventListener("click", bubbleUp, false);
document.forms[0].elements[0].addEventListener("click", bubbleUp, false);
}
/*
在 Firefox中,单击按钮将顺序生成6个对话框
Capturing: [object HTMLDocument]
Capturing: [object HTMLFormElement]
Capturing: [object HTMLInputElement]
Bubbling: [object HTMLInputElement]
Bubbling: [object HTMLFormElement]
Bubbling: [object HTMLDocument]
*/
如果你想停止事件执行时,可以在函数中调用 stopPropagation方法:
function cascadeDown(evnt) {
...
evnt.stopPropagation();
}
如果要彻底删除一个事件监听器,可以使用 removeEventListener方法:
document.removeEventListener("click", cascadeDown, true);
在 IE中,与 addEventListener和 removeEventListener方法相似的是 attachEvent和 detachEvent,对应语法是:
object.attachEvent("eventhandler", function);
第一个参数是事件句柄,第二个是其函数。detachEvent语法类似。
跨浏览器解决方案:
window.onunload = cleanup;
function setup(evnt) {
var evtObject = document.getElementById("clickme");
//检查对象模型
if (evtObject.addEventListener) evtObject.addEventListener("click", clickMe, false);
else if (evtObject.attachEvent) evtObject.attachEvent("onclick", clickMe);
else if (evtObject.onclick) evtObject.onclick=clickMe;
}
/*清理
在 IE中,需要跟踪 window的 unload事件,然后调用 detachEvent方法清理,释放相应的内存
而 addEventListener方法使用的内存是无需清理的。
*/
function cleanup() {
var evtObject = document.getElementById("clickme");
if (evtObject.detachEvent) evtObject.detachEvent("onclick", clickMe);
}
function clickMe() {
alert("clickMe");
}
IE 属性
除了上面的鼠标/事件属性,IE 浏览器还支持下面的属性:
属性 | 描述 |
---|---|
cancelBubble | 如果事件句柄想阻止事件传播到包容对象,必须把该属性设为 true。 |
fromElement | 对于 mouseover 和 mouseout 事件,fromElement 引用移出鼠标的元素。 |
keyCode | 对于 keypress 事件,该属性声明了被敲击的键生成的 Unicode 字符码。对于 keydown 和 keyup 事件,它指定了被敲击的键的虚拟键盘码。虚拟键盘码可能和使用的键盘的布局相关。 |
offsetX,offsetY | 发生事件的地点在事件源元素的坐标系统中的 x 坐标和 y 坐标。 |
returnValue | 如果设置了该属性,它的值比事件句柄的返回值优先级高。把这个属性设置为 fasle,可以取消发生事件的源元素的默认动作。 |
srcElement | 对于生成事件的 Window 对象、Document 对象或 Element 对象的引用。 |
toElement | 对于 mouseover 和 mouseout 事件,该属性引用移入鼠标的元素。 |
x,y | 事件发生的位置的 x 坐标和 y 坐标,它们相对于用CSS动态定位的最内层包容元素。 |
注:HTML DOM Event 对象 http://www.w3school.com.cn/htmldom/dom_obj_event.asp
标准 Event 属性
下面列出了 2 级 DOM 事件标准定义的属性。
属性 | 描述 | IE | F | O | W3C |
---|---|---|---|---|---|
bubbles | 返回布尔值,指示事件是否是起泡事件类型。 | No | 1 | 9 | Yes |
cancelable | 返回布尔值,指示事件是否可拥可取消的默认动作。 | No | 1 | 9 | Yes |
currentTarget | 返回其事件监听器触发该事件的元素。 | No | 1 | 9 | Yes |
eventPhase | 返回事件传播的当前阶段。 | Yes | |||
target | 返回触发此事件的元素(事件的目标节点)。 | No | 1 | 9 | Yes |
timeStamp | 返回事件生成的日期和时间。 | No | 1 | 9 | Yes |
type | 返回当前 Event 对象表示的事件的名称。 | 6 | 1 | 9 | Yes |
注:HTML DOM Event 对象 http://www.w3school.com.cn/htmldom/dom_obj_event.asp
7、测验
Q1:使用 DOM Level 0方法为 document的 click事件指定一个事件句柄函数。
A1:document.onclick = clickMe;
Q2:使用 DOM Level 2事件处理机制为 document添加 click事件句柄。
A2:document.addEventListener("click", clickMe, false);
Q3:如果使事件处理机制能安全运行所有浏览器?
A3:
if (evtObject.addEventListener) evtObject.addEventListener("click", clickMe, false);
else if (evtObject.attachEvent) evtObject.attachEvent("onclick", clickMe);
else if (evtObject.onclick) evtObject.onclick=clickMe;
Q4:对于为 document对象指定的 onclick事件句柄,如何知道是在屏幕的什么位置执行了单击操作?
A4:
如果使用 DOM Level 0事件处理系统,那么将无法使用 window对象的 event对象,也不能将其作为参数传给函数。
对于 DOM Level 2事件处理模型而言,event对象将会传给事件句柄函数,你可以通过 event对象访问其 screenX和 screenY属性。
Q5:使用 DOM Level 2事件系统,如何阻止从其他元素中冒泡上来的事件。
A5:
IE所支持的方法和绝大多数浏览器所支持的方法有所不同,因此你需要分别支持 IE和其他浏览器。你可以检查 event对象是否支持 stopPropagation方法。如果支持,则调用它;否则就将 cancelBubble属性的值设置为 true。
Q7:捕获 document对象的 keydown事件
A7:
if (document.addEventListener) document.addEventListener("keydown", getKey, false);
else if (document.attachEvent) document.attachEvent("onkeydown", getKey);
else if (document.onkeydown) document.onkeydown=getKey;
}
function getKey(evnt) {
var theEvent = evnt ? evnt : window.event;
alert(theEvent.which); //Firefox 返回值,IE 7提示 undefined
}