JS事件

JS中的事件类型

事件是可以被 JavaScript 侦测到的行为。

1.表单事件

submit 表单中的提交按钮被点击时发生的事件 当表单(form)被提交时执行脚本

reset 当表单被重置时执行脚本

blur 元素失去焦点时触发,不冒泡

focus 元素获得焦点时触发,不冒泡

focusin 元素获得焦点时触发,冒泡

focusout 元素失去焦点时触发,冒泡

change 在对象的值发生改变时触发的事件 当元素(select 、复选框 等)改变时执行脚本

select 当元素被选取时执行脚本

2.window事件

load 当用户进入页面时会触发 load 事件。在页面或者图片加载完成以后。文档加载完成。

unload 当用户离开页面时会触发 unload 事件。

DOMContentLoaded

1、当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。

2、当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。

1)支持DOMContentLoaded事件的,就使用DOMContentLoaded事件;

2)IE6、IE7不支持DOMContentLoaded,但它支持onreadystatechange事件,该事件的目的是提供与文档或元素的加载状态有关的信息。

resize:窗口尺寸变化

scroll:滚动条移动

3.鼠标事件

鼠标移动到目标元素上的那一刻,首先触发mouseover

之后如果光标继续在元素上移动,则不断触发mousemove

如果按下鼠标上的设备(左键,右键,滚轮……),则触发mousedown

当设备弹起的时候触发mouseup

滚动滚轮触发mousewheel,这个要区别于scroll

鼠标移出元素的那一刻,触发mouseout

click:单击

dblclick:双击

mousedown:鼠标按下

mouseup:鼠标抬起

mouseover:鼠标悬浮

mouseenter:类似mouseover,鼠标进入,但不冒泡

mouseout:鼠标离开元素

mouseleave:类似mouseout,鼠标离开,但不冒泡

mousemove:鼠标在元素上移动

click事件发生时,event对象会包含以下属性:

pageX,pageY:点击位置相对于html元素的坐标,单位为像素(加上滚动距离的,没有滚动时与clientX相同)

clientX,clientY:点击位置相对于视口(viewport)的坐标,单位为像素(不包含滚动条)

screenX,screenY:点击位置相对于显示屏幕的坐标,单位为设备硬件的像素

scrollHeight,scrollWidth:没有滚动条时,元素内容的高度/宽度

scrollLeft,scrollTop:被隐藏在内容区域左侧/上方的像素数。通过设置这个可以改变元素的滚动位置

4.键盘事件

keydown:按键按下

keyup:按键抬起

keypress:按键按下

keydown和keypress区别:

keydown:用户在键盘上按下某按键是发生。一直按着某按键则会不断触发(opera浏览器除外)。

keypress:用户按下一个按键,并产生一个字符时发生(除了shift、alt、ctrl之类的键,就是说用户按了一个能在屏幕上输出字符的按键keypress事件才会触发)。一直按着某按键则会不断触发。

5.拖拽事件

drag:源对象被拖拽过程中触发

dragstart,dragend:dragstart开始拖拽时触发,dragend结束拖拽时触发

dragenter,dragleave:源对象拖进目标对象后,目标对象上触发dragstart。源对象离开目标对象后,在目标对象上触发dragleave

dragover:在源对象拖拽到另一对象上方时,在后者上触发。

drop:当源对象被推拽到目标对象上方,用户松开鼠标时,在目标对象上触发drop事件

事件流

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

事件冒泡

所谓事件冒泡指的是事件最开始有最具体的元素接收,然后逐级向上传播到不具体的节点。 IE事件流就是事件冒泡。

事件捕获

与事件冒泡相反。事件捕获是由NetScape提出的。NetScape事件流是事件捕获。

事件触发一般来说会按照上面的顺序进行,但是也有特例,如果给一个目标节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行。

// 以下会先打印冒泡然后是捕获
node.addEventListener('click',(event) =>{
	console.log('冒泡')
},false);
node.addEventListener('click',(event) =>{
	console.log('捕获 ')
},true)

DOM事件流

“DOM2级事件”规定的事件流包含三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,然后是实际的目标接收到事件,最后阶段是冒泡阶段。

<body>
<div id="wrapDiv">
        wrapDiv
        <p id="innerP">
                innerP
                 <span id="textSpan">textSpan</span>
        </p>
</div>
</body>
<style>
#wrapDiv, #innerP, #textSpan{
margin: 5px;
padding: 5px;
box-sizing: border-box;
cursor: default;
}
#wrapDiv{
width: 300px;
height: 300px;
border: indianred 3px solid;
}
#innerP{
width: 200px;
height: 200px;
border: hotpink 3px solid;
}
#textSpan{
display: block;
width: 100px;
height: 100px;
border: orange 3px solid;
}
</style>
<script>
var wrapDiv = document.getElementById("wrapDiv");
var innerP = document.getElementById("innerP");
var textSpan = document.getElementById("textSpan");

// 捕获阶段绑定事件
window.addEventListener("click", function(e){
console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

document.addEventListener("click", function(e){
console.log("document 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

document.documentElement.addEventListener("click", function(e){
console.log("documentElement 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

document.body.addEventListener("click", function(e){
console.log("body 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

wrapDiv.addEventListener("click", function(e){
console.log("wrapDiv 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

innerP.addEventListener("click", function(e){
console.log("innerP 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

textSpan.addEventListener("click", function(e){
console.log("textSpan 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

// 冒泡阶段绑定的事件
window.addEventListener("click", function(e){
console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

document.addEventListener("click", function(e){
console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

document.documentElement.addEventListener("click", function(e){
console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

document.body.addEventListener("click", function(e){
console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

wrapDiv.addEventListener("click", function(e){
console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

innerP.addEventListener("click", function(e){
console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

textSpan.addEventListener("click", function(e){
console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
</script>

图1

图2

综上分析

捕获阶段:首先window会获捕获到事件,之后document、documentElement、body会捕获到,再之后就是在body中DOM元素一层一层的捕获到事件,有wrapDiv、innerP。

目标阶段:真正点击的元素textSpan的事件发生了两次,因为在上面的JavaScript代码中,textSapn既在捕获阶段绑定了事件,又在冒泡阶段绑定了事件,所以发生了两次。

冒泡阶段:会和捕获阶段相反的步骤将事件一步一步的冒泡到window

事件处理程序

1. DOM 0级 事件处理

var btn = document.getElementById("myBtn");
btn.onclick = function(){
  alert(this.id);  //myBtn
}

事件处理程序在元素的作用域上运行,this引用当前元素。

只能添加一个处理程序,后面的会覆盖前面的。

删除通过DOM0级方法指定的事件处理程序:btn.onclick=null;

亲测:发生在捕获阶段。在DOM2之前。

2. DOM 2级 事件处理

addEventListener()与removeEventListener():接受三个参数:要处理的事件类型名称、作为事件处理程序的函数、布尔值 (true:捕获阶段调用事件处理程序。false:冒泡阶段调用事件处理程序,默认false冒泡)

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
  alert(this.id);  //myBtn
},false);

事件处理程序在元素的作用域上运行,this引用当前元素。

可以添加多个事件处理程序,按顺序触发。

若传入的是匿名函数,则无法通过removeEventListener移除。

通常我们使用 addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值 useCapture 参数来说,该参数默认值为 false 。useCapture 决定了注册的事件是捕获事件还是冒泡事件。对于对象参数来说,可以使用以下几个属性

capture,布尔值,和 useCapture 作用一样

once,布尔值,值为 true 表示该回调只会调用一次,调用后会移除监听

passive,布尔值,表示永远不会调用 preventDefault

一般来说,我们只希望事件只触发在目标上,这时候可以使用 stopPropagation 来阻止事件的进一步传播。通常我们认为 stopPropagation 是用来阻止事件冒泡的,其实该函数也可以阻止捕获事件。stopImmediatePropagation 同样也能实现阻止事件,但是还能阻止该事件目标执行别的注册事件。

node.addEventListener('click',(event) =>{
	event.stopImmediatePropagation()
	console.log('冒泡')
},false);
// 点击 node 只会执行上面的函数,该函数不会执行
node.addEventListener('click',(event) => {
	console.log('捕获 ')
},true)

3.IE事件处理程序(冒泡)

attachEvent()与detachEvent():接受两个参数:要处理的事件类型名称、作为事件处理程序的函数

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
  alert(this === window);  //true
});

this指向全局作用域的对象window

可以添加多个事件处理程序,按相反的顺序触发。

若传入的是匿名函数,则无法通过detachEvent移除。

事件对象event

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件相关的信息。包含导致事件的元素、事件的类型以及其他与特定事件相关的信息。event对象会被作为第一个参数传递给事件监听的回调函数。

event对象的属性和方法

type:事件的名称(string)

target:事件起源的DOM节点(Node)

currentTarget:当前回调函数被触发的DOM节点(Node)

bubbles:指明这个事件是否是一个冒泡事件(boolean)

cancelable:指明这个事件的默认行为是否可以用preventDefault方法阻止。(boolean)

eventPhase:返回一个数字,表示事件目前所处的阶段。0:事件开始从DOM表层向目标元素传播 1:捕获阶段 2:事件到达目标元素 3:冒泡阶段

preventDefault():这个方法将阻止浏览器中用户代理对当前事件的相关默认行为被触发。比如阻止元素的click事件加载一个新的页面 (function)

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

只有在事件处理程序执行期间,event对象才会存在,一旦事件处理程序执行完毕,event对象就自动销毁。

IE中的事件对象

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

IE事件对象的属性和方法

cancelBubble :布尔值。默认false,设置成true用来取消事件冒泡。与DOM中的stopPropagation方法相同

returnValue:布尔值。默认true,设置为false时用以取消事件的默认行为。与DOM中的preventDefault方法相同

srcElement:node值。事件的目标。与dom中的target属性相同

type:字符串。被触发的事件类型

阻止事件冒泡与默认行为

//阻止事件冒泡函数
function stopBubble(e) {
   if (e && e.stopPropagation)
       e.stopPropagation()
   else
       window.event.cancelBubble = true
}

// 阻止默认浏览器动作(W3C)
function stopDefault(e) {
    // 阻止默认浏览器动作(W3C)
    if (e && e.preventDefault) {
        e.preventDefault();
    } else {
        // IE中阻止函数器默认动作的方式
        window.event.returnValue = false;
    }
    return false;
}
Table of Contents