DOM事件探秘

Javascript与HTML之间的交互是通过事件来实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。比如:
-点击一个DOM元素
-键盘按下一个键
-输入框输入内容
-页面加载完成

事件流

事件流描述的是从页面中接收事件的顺序。
IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流.
DOM2级事件规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

事件流

事件冒泡

IE的事件流是事件冒泡流(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。

事件捕获

Netscape的事件流是事件捕获流(event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。

事件处理程序

事件处理程序(事件侦听器):响应某个事件的函数.
事件处理程序的名字以’on’开头,

1、HTML事件处理程序

1
2
3
4
5
6
7
<input type="button" value="Click Me" onclick="alert('Clicked')" />
<input type="button" value="Click Me" onclick="showMessage()" />
<script type="text/javascript">
function showMessage(){
alert("Hello world!");
}
</script>

以上两种input方式都是HTML事件处理程序。
在HTML中指定事件处理程序的缺点:

  1. 存在时差问题。假如用户在HTML元素一出现在页面上就触发相应的事件,当这时的事件处理程序有可能尚不具备执行条件,此时可能会报错。
  2. 这样扩展事件处理程序的作用域链在不同的浏览器中会导致不同结果。
  3. HTML与JavaScript代码紧密耦合,如果需要更换事件处理程序,需要同时改动HTML代码和JavaScript代码。

2、DOM0级事件处理程序

通过javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<input type="button" id="myBtn" value="Click Me" />
<input type="button" id="myRemoveBtn" value="Remove Event Handler" />
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id);
};
var removeBtn = document.getElementById("myRemoveBtn");
removeBtn.onclick = function(){
btn.onclick = null;
};
</script>

</body>

3、DOM2级事件处理程序

DOM2级事件定义了两个方法:用于处理指定和删除事件处理程序的操作:

  • addEventListener()
  • removeEventListener()它们都接收三个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <body>
    <input type="button" id="myBtn" value="Click Me" />
    <input type="button" id="myRemoveBtn" value="Remove Event Handler" />
    <script type="text/javascript">
    var btn = document.getElementById("myBtn");
    var handler = function(){
    alert(this.id);
    };
    btn.addEventListener("click", handler, false);
    var removeBtn = document.getElementById("myRemoveBtn");
    removeBtn.onclick = function(){
    btn.removeEventListener("click", handler, false); //works!
    };

    </script>

    </body>

4、IE事件处理程序

  • attachEvent()添加事件
  • detachEvent()删除事件
    这两个方法接收相同的两个参数:事件处理程序名称与事件处理函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <body>
    <input type="button" id="myBtn" value="Click Me" />
    <input type="button" id="myRemoveBtn" value="Remove Event Handler" />
    <script type="text/javascript">
    var btn = document.getElementById("myBtn");
    var handler = function(){
    alert("Clicked");
    };
    btn.attachEvent("onclick", handler);
    var removeBtn = document.getElementById("myRemoveBtn");
    removeBtn.onclick = function(){
    btn.detachEvent("onclick", handler);
    };
    </script>

    </body>

5、跨浏览器的事件处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var eventUtil={
// 添加句柄
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type]=handler;
}
},
// 删除句柄
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type]=null;
}
},
getEvent:function(event){
return event?event:window.event;
},
getType:function(event){
return event.type;
},
getElement:function(event){
return event.target || event.srcElement;
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}

事件对象

事件对象event:

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

1、DOM中的事件对象

(1)、type:获取事件类型
(2)、target:事件目标
(3)、stopPropagation() 阻止事件冒泡
(4)、preventDefault() 阻止事件的默认行为

2、IE中的事件对象

(1)、type:获取事件类型
(2)、srcElement:事件目标
(3)、cancelBubble=true阻止事件冒泡
(4)、returnValue=false阻止事件的默认行为

事件分类

事件分类
W3C事件分类

事件代理

将事件注册到元素的父节点上。
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。

  1. 每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。
  2. 必须先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。

对“事件处理程序过多”问题的解决方案就是事件委托。

事件委托

事件代理也被称为“事件委托”。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
(详见《JavaScript高级程序设计(第3版)》,第13章,P402页)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<body>
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
<script type="text/javascript">
(function(){
var list = document.getElementById("myLinks");

EventUtil.addHandler(list, "click", function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);

switch(target.id){
case "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.href = "http://www.wrox.com";
break;
case "sayHi":
alert("hi");
break;
}
});
})();
</script>

</body>

最适合采用事件委托技术的事件包括:click/mousedown/mouseup/keydown/keyup/keypress.

实战案例

QQ面板拖拽效果

查看DEMO
涉及知识点:

  1. 鼠标事件、焦点事件:
  • onmousedown
  • onmousemove
  • onmouseup
  • onmouseover
  • onmouseout
  • onclick
  1. 事件对象及其属性、方法:
  • event.stopPropagation() || event.cancelBubble=true 阻止事件冒泡
  • event.preventDefault() || event.returnValue=false 阻止事件的默认行为
  1. clientX,clientY:
    鼠标事件都是在浏览器窗口中的特定位置上发生的。
    这个位置信息保存在事件的clientX和clientY属性中。
    所有的浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标,但不包括页面滚动的距离。

  2. 页面视口尺寸:浏览器窗口的尺寸(浏览器的视口,不包括工具栏和滚动条)

  • 宽:document.documentElement.clientWidth || document.body.clientWidth,
  • 高:document.documentElement.clientHeight || document.body.clientHeight
  1. 元素偏移量:元素在屏幕上占用的所有可见的空间。
  • offsetHeight:元素在垂直方向上占用的空间大小。(height+border-width*2+水平滚动条高度)
  • offsetWidth: 元素在水平方向上占用的空间大小。(width+border-width*2+垂直滚动条高度)
  • offsetLeft: 元素的左外边框至包含元素的左内边框之间的像素距离
  • offsetTop: 元素的上外边框至包含元素的上内边框之间的像素距离
  1. 元素客户区大小:元素内容及其内边距所占据的空间大小。
  • clientWidth: width+padding-width*2;
  • clientHeight:height+padding-width*2.
  1. 滚动大小:包含滚动内容的元素的大小
  • scrollHeight:在没有滚动条的情况下,元素内容的总高度
  • scrollWidth: 在没有滚动条的情况下,元素内容的总宽度
  • scrollLeft: 被隐藏在内容区域左侧的像素数
  • scrollTop: 被隐藏在内容区域上方的像素数

元素尺寸1
元素尺寸2
参考资源

坚持原创技术分享,您的支持将鼓励我继续创作!