js 的事件

JavaScript 与 HTML 之间的交互是通过 事件 实现的。事件三要素: 事件源.事件 = function(){ 事件处理程序 };等价于 <标签 事件= “事件处理程序”>。

事件就是文档或浏览器窗口中发生的一些特定交互瞬间。包括事件类型、事件目标、事件处理程序、事件对象。

事件类型是用过用来说明发生什么类型事件的字符串。

事件目标是发生的事件或与之相关的对象。如 window、Document、Element 对象。以及由 XMLHTTPRequest 对象。

事件处理程序或事件监听程序是处理或响应事件的函数。

事件对象是与特定事件相关且包含有关该事件的详细信息的对象。

事件传播是浏览器决定哪个对象触发其事件处理程序的过程。当文档元素上发生某个类型的事件时,它们会在文档树上向上冒泡。

事件传播的另一中形式称为事件捕获,在容器元素上注册的特定处理程序有机会在事件传播到真实目标之前捕获。

事件流

事件流描述的是页面中接收事件的顺序。

事件冒泡

事件开始是由最具体的元素(文档中嵌套最深的那个节点)接收,然后逐级向上传播到较为不具体的节点。IE、FireFox、Chrome、Safari将事件一直冒泡到 window 上。

事件捕获

目的是在事件到达预定目标之前捕获它。不太具体的节点最先收到事件,而最具体的节点应该最后收到事件。

DOM 事件流

事件流的三个阶段:

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段

在 DOM 事件流中,实际目标在捕获阶段不会接收到事件。下一个阶段是”处于目标阶段”,事件在目标上发生,并在事件处理中看成是冒泡阶段的一部分。然后,冒泡阶段发生。

事件处理程序

HTML 事件处理程序

1
2
3
4
5
6
7
8
9
<input type="button" name="butt1" value="点我" onclick="console.log('我被点击了')">
<input type="button" name="butt2" value="点我" onclick="showMsg()">
<script type="text/javascript">
function showMsg() {
console.log('被点了');
}
</script>
<input type="button" name="butt3" value="点我" onclick="console.log(event.type)">
<input type="button" name="butt4" value="点我" onclick="console.log(this.value)">

event 变量可以直接访问事件对象。this 指的是触发事件的当前元素。

HTML 指定事件处理程序的缺点是:HTML 和 JavaScript 代码紧密耦合。

DOM0 级事件处理程序

将一个函数赋值给一个事件处理程序属性。优点:简单、跨浏览器的优势。

1
2
3
4
var btn = document.getElementById("btn");
btn.onclick = function() {
console.log("我被点击了");
}

DOM2 级事件处理程序

定义了两个指定和删除事件处理程序的操作: addEventListener() 和 removeEventListener();接收 3 个参数:

  • 要处理的事件名
  • 作为事件处理程序的函数
  • 一个布尔值:true 时表示在捕获阶段调用事件处理程序,false 表示在冒泡阶段调用事件处理程序。

IE 的事件处理程序

  • attachEvent()
  • detachEvent()

均接收两个参数,事件处理程序名称和事件处理程序函数。通过 attachEvent 添加的事件处理程序都会被添加到事件冒泡阶段。

跨浏览器的事件处理程序

封装一个兼容的事件处理程序,参数是:要操作的元素、事件类型、事件处理程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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] = null;
}
},
removeHandler: function(element, type, handler) {
if (element.removeListener) {
element.removeListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
}

事件对象

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

  • 导致事件的元素
  • 事件类型
  • 事件相关信息

DOM 中的事件对象

兼容 DOM 浏览器会将一个 event 对象传入到事件处理程序中。可以通过事件处理程序拿到 event 对象的信息。

1
2
3
4
5
6
7
var btn = document.getElementById("btn");
btn.onclick = function(event) {
console.log(event.type); // "click"
}
btn.addEventlistener("click", function(event){
console.log(event.type); // "click"
}, false);

在通过 HTML 特性指定的事件处理程序,变量 event 保存着 event 对象。

1
<input type="button" name="btn" value="点我" onclick="console.log(event.type)" />

event 对象包含与创建它的特定事件有关的属性和方法。触发的事件不同,可用的属性和方法也不一样。但所有事件都会有下表中列出的方法。

| 属性/方法 | 类型 | 读/写 | 说明 |
| bubbles | Boolean | 只读 | 表明是否冒泡 |
| cancelable | Boolean | 只读 | 是否可以取消事件的默认行为 |
| currentTarget | Element | 只读 | 事件处理程序当前正在处理事件的那个元素 |
| defaultPrevented | Boolean | 只读 | true 时表示调用了 perventDefault() |
| detail | Integer | 只读 | 事件相关的细节信息 |
| eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1捕获 2处于目标 3冒泡 |
| perventDefault() | Function | 只读 | 取消事件默认行为,如果candelable是true可以使用这个方法 |
| stopImmediatePropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序调用 |
| stopPropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,如果 bubbles 为 true,则可以使用这个方法 |
| target | Element | 只读 | 事件目标 |
| trusted | Boolean | 只读 | 为 true 时表示浏览器生成的,为 false 时表示开发人员通过 javascript 创建的 |
| type | String | 只读 | 被触发的事件类型 |
| view | AbstractView | 只读 | 与事件相关联的抽象视图。等同于发生事件的 window 对象 |

在事件处理程序的内部,this 始终等于 currentTarget 的值,而 target 只包含事件的实际目标。如果时间处理程序指定给了目标元素。则这三个的值相等。

stopPropagation() 方法用于立即停止事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡。

IE 中的事件对象

event 对象作为 window 对象的一个属性存在。

| 属性/方法 | 类型 | 读/写 | 说明 |
| cancelables | Boolean | 读/写 | 默认值是 false,设置为 true 就可以取消事件冒泡 |
| returnValue | Boolean | 读/写 | 默认 true,设为 false 可以取消事件的默认行为 |
| srcElement | Element | 只读 | 事件的目标 |
| type | String | 只读 | 被触发的事件类型 |

跨浏览器的事件对象

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
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] = null;
}
},
removeHandler: function(element, type, handler) {
if (element.removeListener) {
element.removeListener(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;
},
getTarget: 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;
}
}
}

事件类型

  • UI 事件:用户与页面上的元素交互时触发;
  • 焦点事件:元素获取焦点或失去焦点时触发
  • 鼠标事件
  • 滚轮事件
  • 文本事件
  • 键盘事件
  • 合成事件
  • 变动事件:底层 DOM 结构发生变化时触发

UI 事件

  • DOMActive 已废弃
  • load: 页面完全加载后在 window 上触发
  • unload:页面卸载后在 window 上触发
  • abort:在用户停止下载过程时,如果嵌入的元素没有加载完,则在目标元素上面触发
  • select:当用户选择文本框中的一个或多个时触发
  • resize:当窗口或框架的大小变化时触发
  • scroll:当用户滚动带滚动条的元素中的内容时在该元素上触发。

焦点事件

  • blur:元素失去焦点时触发;这个事件不会冒泡。
  • DOMFocusIn:在元素获取焦点时触发。与 HTML 事件的 focus 等价。只有 Opera 支持。DOM3 级已废弃,选择了 focusin。
  • DOMFocusOut:在元素失去焦点时触发。DOM3 级已废弃,选择了 focusout。
  • focus: 获取焦点时触发。
  • focusin:它冒泡,和 HTML 的 focus 事件等价。
  • focusout:它冒泡,是 HTML 的 blur 事件的通用版本。

鼠标与滚轮事件

  • click:用户单击鼠标按钮(一般是左键)或者按下 Enter 键时触发。
  • dbclick:双击鼠标触发。不能通过键盘触发这个事件。
  • mousedown:按下了鼠标任意键触发。不能通过键盘触发这个事件。
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
  • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
    • mouseenter 和 mouseleave 是一对,IE、FireFox 9+ 和 Opera 支持这个事件。
  • mousemove:鼠标指针在元素内部移动时重复触发。
  • mouseout:在鼠标指针位于一个元素上,然后用户将其移入另一个元素时触发。
  • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一元素边界之内时触发
  • mouseup:鼠标抬起时触发。

滚轮事件

  • mousewheel 事件
1
2
// 检查浏览器是否支持上面所有的事件
var isSupported = document.implementation.hasFeature("MouseEvent", "3.0");

客户区坐标位置

  • event.clientX 事件发生时鼠标指针在视口中的位置
  • event.clientY 事件发生时鼠标指针在视口中的位置

屏幕坐标位置

  • event.screenX 相对于整个电脑屏幕的坐标位置
  • event.screenY 相对于整个电脑屏幕的坐标位置

  • shiftKey
  • ctrlKey
  • altKey
  • metaKey

相关元素

发生 mouseover 和 mouseout 时,还会涉及更多的元素。 DOM 通过 event 对象的 relatedTarget 属性提供了相关元素信息。在 mouseover 事件触发时, IE 的 fromElement 保存了相关元素,在 mouseout 事件触发时,IE 的 toElement 保存了相关元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
var EventUtill = {
getRelatedTarget: function(event) {
if (event.relatedTarget) {
return event.relatedTarget;
} else if (event.toElement) {
return event.toElement;
} else if (event.fromEvent) {
return event.fromEvent;
} else {
return null;
}
}
}

更多事件信息

DOM2 级事件规范在 event 对象提供了 detail 属性,用于给出有关事件的更多信息。包含了一个数值表示单击了多少次。

  • altLeft
  • ctrlLeft
  • shiftLeft
  • offsetX
  • offsetY

键盘和文件属性

  • keydown:按下键盘上的任意键触发
  • keypress:按下键盘上的字符键触发
  • keyup:释放键盘上的键时触发

键码

  • event.keyCode

textInput 事件

  • textInput 事件
  • event.data 属性的值是用户输入的字符。
  • event.inputMethod 属性表示把文本输入到文本框的方式

复合事件

  • compositionstart
  • compositionupdate
  • compositionend

变动事件

  • DOMSubtreeModified
  • DOMNodeInserted
  • DOMNodeRemoved
  • DOMNodeInsertedIntoDocuemnt
  • DOMNodeRemovedFromDocuemnt
  • DOMAttrModified
  • DOMCharacterDataModified

HTML5 事件

  • contexmenu 事件
  • beforeunload 事件
  • DOMContentLoaded 事件
  • readystatechange 事件
    • uninitialized 对象尚未初始化
    • loading 对象正在加载
    • loaded 对象加载数据完成
    • interactive 可以操作对象了,但还没有完全加载
    • complete 对象已经加载完毕
  • pageshow 和 pagehide 事件
  • hashchange 事件

设备事件

  • orientationonchange 事件
  • MozOrientation 事件
  • deviceorientation 事件 设备在空间中朝向哪里
    • alpha 属性
    • beta 属性
    • gamma 属性
    • absolute 属性
    • compassCalibrated 属性
  • devicemotion 事件

触摸与手势事件

  • touchstart:手势在触摸屏时触发
  • touchmove:手势在屏幕上滑动时触发
  • touchend:手势离开触摸屏时触发
  • touchcansel:当系统停止跟踪触摸时触发
  • touches:表示当前跟踪的触摸操作的 Touch 对象的数组
  • targetTouches:特定于事件目标的 Touch 对象的数组
  • changeTouches:表示自上次触摸以来发生了什么改变 Touch 对象的数组

每个 touch 对象包含以下属性

  • clientX
  • clientY
  • identitier
  • pageX
  • pageY
  • screenX
  • screenY
  • target

内存和性能

JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。

事件委托

对事件处理程序过多的问题的解决方法就是 事件委托 。事件委托就是利用了事件冒泡,只指定一个事件处理程序来管理某一类型的的所有事件。

1
2
3
4
5
<ul id="ulBox">
<li id="li1">1</li>
<li id="li2">2</li>
<li id="li3">3</li>
</ul>

委托事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ul = document.getElementById("ulBox");
EventUtil.addHandler(ul, "click", function(event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch (target.id) {
case "li1":
document.title = "I changed";
break;
case "li2":
location.herf = "http://www.baidu.com";
break;
case "li3":
console.log("Hi");
break;
}
});

移除事件处理程序

1
btn.onclick = null;

禁止a标签的默认行为

1
onclick = "return false;"

一个判断客户端是 PC 端还是移动端的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
document.getElementById("txt").onkeydown=function (e) { console.log(e.keyCode); };
window.onresize=function () {
if (document.body.clientWidth>960) {
console.log("您用的是电脑浏览的该页面");
}else if(document.body.clientWidth>500){
console.log("您用的是平板浏览的该页面");
}else{
console.log("您用的是手机浏览的页面");
}
}
}; // 判断浏览器窗口大小
document.onclick=function (e) {
document.title = e.screenX+"==="+e.screenY; //screenX和screenY是相对于屏幕的左上角
console.log(e);
};

图片跟着鼠标飞

1
2
3
4
5
6
7
8
9
document.onmousemove=function (e) {
e=e||window.event; // 兼容代码
document.title=e.clientX+"==="+e.clientY; // clientX和clientY----事件参数获取
imgObj.style.position="absolute";
imgObj.style.left=e.clientX+"px";
imgObj.style.top=e.clientY+"px";
imgObj.style.left=e.pageX+"px"; //pageX--包含可视区域的横坐标和隐藏区域
imgObj.style.top=e.pageY+"px"; //pageY---包含可视区域的纵坐标和隐藏区域
};

模拟多人开发注册事件

1
2
3
4
5
6
7
8
9
10
11
12
13
function addEvent(element, eventName, fn) {
var oldEvent=element["on"+eventName];
if(oldEvent==null){//表示该事件没有处理函数
element["on"+eventName]=fn;//注册事件了
}else{
//有事件了---先调用原来的事件,再重新注册新的事件
element["on"+eventName]=function () {
//调用原来的事件-注册新的事件
oldEvent();
fn();
};
}
}

注册多个事件

三种注册事件的方式:

  • 对象.on事件名字=事件处理函数;
  • 对象.addEventListener(“事件的名字”,事件处理函数,false);
  • 对象.attachEvent(“on事件的名字”,事件处理函数);
  • 注册事件方式的区别:
    • btnObj.onclick=fn;这种方式任何浏览器都支持
    • btnObj.addEventListener();谷歌和火狐浏览器支持
    • btnObj.attachEvent();IE8支持
  • 同一个元素同时注册多个相同的事件,addEventListener和attachEvent区别:
    • 前者三个参数,后者两个参数
    • 前者第一个参数是事件的名字,没有on
    • 后者第一个参数是事件的名字,有on
    • addEventListener方法中第三个参数如果是false则是事件冒泡,如果是true则是事件捕获

事件冒泡阶段判断

  • 通过事件参数–e.eventPhase 可以获取当前事件经历的是什么阶段
  • 如果是1则是捕获阶段
  • 如果是2则是目标阶段:第一次点谁谁就是目标
  • 目标阶段之后就是冒泡阶段
  • 以上是结合第三个参数是false的情况而言
  • 另一种情况:第三个参数是true的时候,只有捕获阶段和目标阶段
  • 先捕获,然后再目标
  • 一般网页中都是有事件冒泡的,一般情况我们不用捕获

阻止事件冒泡

两种方式:

  • 第一种:方法 e.stopPropagation();
  • 第二种:属性 window.event.cancelBubble=true;

有关事件处理的函数的封装

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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] = null;
}
},
removeHandler: function(element, type, handler) {
if (element.removeListener) {
element.removeListener(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;
},
getTarget: 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;
}
},
getRelatedTarget: function(event) {
if (event.relatedTarget) {
return event.relatedTarget;
} else if (event.toElement) {
return event.toElement;
} else if (event.fromEvent) {
return event.fromEvent;
} else {
return null;
}
},
getWheelDelta: function(event) {
if (event.wheelDelta) {
return (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);
} else {
return -event.detail * 40;
}
},
getCharCode: function(event) {
if (typeof event.charCode == "number") {
return event.charCode;
} else {
return event.keyCode;
}
},
getClipboardText: function(event) {
var clipboardData = (event.clipboardData || window.clipboardData);
return clipboardData.getData("text");
},
setClipboardText: function(event) {
if (event.clipboardData) {
return event.clipboardData.setData("text/plain", value);
} else if (window.clipboardData) {
return window.clipboardData.setData("text/plain", value);
}
}
}

动画函数封装(js)

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//根据id获取对应的元素
function my$(id) {
return document.getElementById(id);
}
/*
* element---任意的元素
* attr---属性
* */
function getAttrValue(element,attr) {
return element.currentStyle ? element.currentStyle[attr] : window.getComputedStyle(element,null)[attr]||0;
}
/*
* element----要移动的元素
* target----移动的目标
* 初级版本
* */
function animate1(element,target) {
clearInterval(element.timeId);
element.timeId=setInterval(function () {
//获取当前的位置
var current=element.offsetLeft;
//每次移动多少步
var step=(target-current)/10;//(目标-当前)/10
step=step>0?Math.ceil(step):Math.floor(step);
current=current+step;
element.style.left=current+"px";
if(current==target){
clearInterval(element.timeId);
}
console.log("target:"+target+"current:"+current+"step:"+step);
},10);
}
/*
* 终极版本的动画函数
* */
function animate(element,json,fn) {
clearInterval(element.timeId);
element.timeId=setInterval(function () {
var flag=true;//假设都达到了目标
for(var attr in json){
if(attr=="opacity"){//判断属性是不是opacity
var current= getAttrValue(element,attr)*100;
//每次移动多少步
var target=json[attr]*100;//直接赋值给一个变量,后面的代码都不用改
var step=(target-current)/10;//(目标-当前)/10
step=step>0?Math.ceil(step):Math.floor(step);
current=current+step;
element.style[attr]=current/100;
}else if(attr=="zIndex"){//判断属性是不是zIndex
element.style[attr]=json[attr];
}else{//普通的属性
//获取当前的位置----getAttrValue(element,attr)获取的是字符串类型
var current= parseInt(getAttrValue(element,attr))||0;
//每次移动多少步
var target=json[attr];//直接赋值给一个变量,后面的代码都不用改
var step=(target-current)/10;//(目标-当前)/10
step=step>0?Math.ceil(step):Math.floor(step);
current=current+step;
element.style[attr]=current+"px";
}
if(current!=target){
flag=false;//如果没到目标结果就为false
}
}
if(flag){//结果为true
clearInterval(element.timeId);
if(fn){//如果用户传入了回调的函数
fn(); //就直接的调用,
}
}
console.log("target:"+target+"current:"+current+"step:"+step);
},10);
}

总结三大系列

clientX  clientY  clientWidth  clientHeight
pageX  pageY  pageWidth pageHeight
offsetLeft offsetTop  offsetWidth offsetHeight
感谢您的支持!