JavaScript

JavaScript事件解析

事件:
事件三要素:

  • 事件源 :即dom元素或者其他对象
  • 事件类型:事件的类型
  • 事件监听程序 : 事件的回调函数

注册事件处理程序

1.通过设置事件目标的属性

ele.onclick = function(e){};

注意:这种写法如有多个处理程序则会覆盖掉之前的

2.通过元素的addEventListener

ele.addEventListener('click' , function(e){console.log(e.target);});
//相同的参数多次调用只注册一次

ele.removeEventListener('click',func)  //移除处理程序

注意:这种写法可以写多个,且不会覆盖之前的,会顺序执行

通过addEventListener添加的事件处理程序,只能通过removeEventListener来删除,也就是说通过addEventListener添加的匿名函数将无法被删除。

//IE8及之前的版本没有addEventListener
ele.attachEvent('onclick', function(){
    //这里的this指向window
});
//这种不会传给回调函数event对象,而是通过window.event获取
//相同参数多次调用会注册多次
ele.datachEvent('click',func);

3.设置HTML标签属性为事件处理程序

<button onclick="console.log(event.target);console.log(this)"></button>
//当指定一串js代码作为HTML事件处理程序属性的值时,浏览器会把代码串转换为类似如下的函数中:       
function(event){
    with(document){
        with(this.form || {}){
            with(this) {
                <!--这里是编码-->
            }
        }
    }
}

通过HTML属性注册事件处理程序被转换为能存取全局变量的顶级函数而非任何本地变量。因为历史原因,它们运行在一个修改后的作用域链中。

所以在标签属性程序中,event.target指向目标元素,this也指向目标元素,在里面还可以直接访问docment作用域下的值

事件处理程序的返回值

通过设置对象属性或者HTML属性注册事件处理程序的返回值有时非常有意义。

事件处理程序的返回值只对通过属性注册的处理程序(元素对象属性ontype 或者HTML属性)有意义。

通过返回false取消执行这个事件相关的默认操作。
使用addEventListener和attachEvent需要调用preventDefault()来阻止默认行为。
或者设置事件对象的returnValue(这是ie9以下的写法)属性。

注意:

阻止默认事件的默认行为,并不是阻止冒泡或者捕获。
例如:a标签阻止其默认行为就是不让其跳转。表单的submit事件,阻止其默认行为就是不让其提交。

调用顺序

文档元素或其他对象为指定事件类型注册多个事件处理程序。当适当的事件发生时,按照如下顺序

  • HTML属性设置的处理程序
  • 通过元素对象属性设置的或者通过addEventListener设置的(按代码的先后位置)

注意:

HTML属性和元素对象的ontype属性同时存在时,ontype会覆盖HTML元素属性绑定的处理程序。

addEventListener可以和HTML元素属性和元素对象属性设置的处理程序同时存在。

事件传播

发生在文档元素上的大部分事件会冒泡到DOM树根。事件会一直冒泡到Documnet再到Window。

例外 : focus blur scroll不会冒泡

文档元素上的load事件会冒泡,但它会在Document对象上停止冒泡不会传播到Window对象。只有整个文档都加载完毕才会触发window的load事件。

事件冒泡是事件传播的第三个阶段。目标对象本身的事件处理程序调用时第二个阶段。第一个阶段甚至发生在目标处理程序调用之前,称为捕获阶段。

addEventListener()把一个布尔值作为第三个参数。如果这个参数为true,那么事件处理程序被注册为捕获事件处理程序,它会在事件传播的第一个阶段调用。

//document的捕获程序,几乎在所有元素发生点击事件时第一个调用
document.addEventListener('click', function(e){conosle.log('catch')}, true);
//document的事件处理程序,只有当点击事件冒泡到doucment时才会调用
document.addEventListener('click',function(e){console.log('bubble')},);

事件传播的捕获阶段像反向的冒泡阶段。最先调用window对象的捕获处理程序,然后是Document对象的,接着是body对象的,在然后是DOM树向下,以此类推,直到调用事件目标的父元素的捕获事件处理程序。

注意:

在自身上的事件捕获程序,当事件是自身触发而不是其子元素触发时,捕获程序就是事件处理程序了,事件也不会在向下传播了,而是向上冒泡。

事件取消

function cancelDefault(event){
    var event = event || window.event ;  //用于IE
    <!--处理事件的逻辑代码-->
    
    //取消事件相关的默认行为
    if(event.preventDefault) event.preventDefault();
    if(event.returnValue) event.returnValue = false; //IE
    
    return false; //用于处理使用对象属性注册的处理程序
}

取消冒泡

event.stopPropagation();  

用于阻止事件的传播

注意:

这里不止是冒泡,当在事件捕获阶段,调用event.stopPropagation()也会阻止事件向下传播,从而让触发事件的元素不能执行事件处理程序。也能阻止向上冒泡。

并不会阻止默认事件。event.stopPropagation()和event.preventDefault()是独立的

<button id="submit">submit</button>

document.getElementById('submit').onclick = function(event){
    console.log('submit');
}
//事件捕获程序
document.addEventListener('click' , function(e){
    e.stopPropagation();
},true);
//取消冒泡
event.cancelBubble = false //兼容性不好,支持IE8
event.stopImmediatePropagation();
//和stopPropagation()类似,但是会阻止同一元素多个事件处理程序的调用,
//也就是调用后,之后的事件处理程序不会被执行。

事件类型

文档加载事件

load事件直到文档和所有图片加载完毕时才发生。

当文档加载解析完毕且所有延迟脚本(defer)都执行完毕时会触发DOMContentLoaded事件(3级DOM事件标准,HTML5也标准化了),此时图片和异步脚本(async)可能依旧在加载,但是文档已经为操作准备就绪了。

document.readyState属性随着文档加载过程而变化。HTML5标准化了readstatechange事件,仅在load事件之前立即触发。

鼠标事件

//注意这里的事件命名规范不同

click         //点击
dbclick       //双击
contextmenu   //当上下文菜单即将出现时触发。即右键菜单时触发,可以阻止默认事件,让菜单不显示
mousedown    //按下鼠标键
mouseup      //释放鼠标键
mouseover   //鼠标移入元素
mousemove // 鼠标移动
mouseout  // 鼠标移出元素
mouseenter  //类似mouseover,但不会冒泡。HTML5已标准化
mouseleave  //类似mouseout ,不会冒泡。HTML5已标准化

传递给鼠标事件处理程序的事件对象有clientXclientY属性,它们指定了鼠标指针相对于视口的坐标。加上视口的滚动偏移量就可以得到鼠标在文档中的坐标。

altKey、ctrlKey、metaKey、和shiftKey指定是否有辅助键一起按下。

鼠标滚轮事件

wheel //滚轮滚动 3级DOM规范  

滚轮事件其实也是鼠标事件,有鼠标事件的一些属性,还有deltaX deltaY deltaZ
来判断滚动的方向和距离。如deltaY为100 则滚轮键向下滚动了100px(页面可能没发生滚动),100为其最小单位。
wheelDeltaX wheelDeltaY 也可以判断。
wheelDeltaY 最小单位为120 ,向下为负值。

可以判断滚轮键触发时鼠标的位置。

键盘事件

只有在元素获得焦点后才会触发键盘事件

onkeydown
onkeypress
onkeyup

先发生keydown 在keypress然后keyup。keypress只在按下可打印字符时才触发。而另外两个都会触发。

3级DOM规范定义了一个新属性key,会以字符串的形式包含键名。

表单事件

onblur		  //元素失去焦点时运行的脚本。
onchange		//在元素值被改变时运行的脚本。
oncontextmenu	//	当上下文菜单被触发时运行的脚本。 H5
onfocus		//当元素获得焦点时运行的脚本。
onformchange //		在表单改变时运行的脚本。H5
onforminput	//	当表单获得用户输入时运行的脚本。H5
oninput		 //当元素获得用户输入时运行的脚本。H5
oninvalid	//	当元素无效时运行的脚本。H5
onreset		 //当表单中的重置按钮被点击时触发。HTML5 中不支持。
onselect	//	在元素中文本被选中后触发。
onsubmit	//	在提交表单时触发。

input事件是H5新增的事件,和change事件不同,它只要输入就会触发,而change是要等元素失去焦点之后才触发

自定义事件

元素可以自己触发事件:

element.click();   //element.[eventType]();

可以自定义事件,满足某一条件在让元素触发

var my = document.createEvent('已有的事件类型')//eg:'MouseEvent' 'HTMLEvent' 'CustomEvent' 'Event'...

my.initEvent('myevent' ,true ,true);  //这里的myevent为自定义的事件名
//后面的两个参数分别表示是否冒泡和是否可取消
//my.attr  也可以添加一些自定义属性

someEle.addEventListener('myevent',function(e){});  //为某一元素添加自定义事件的处理函数

<!--满足某一条件时触发-->
element.dispatchEvent(my);  //元素手动触发自定义事件
元素可以监听这个自定义事件
elemet.addEventListener('my' , func(event){});

更多关于自定义事件的细节:
参考1
参考2 参考3

target、currentTarget、this的区别

event.target 和 this的区别
在为注册事件处理函数时,event.target是指触发事件的元素,this指调用处理函数的元素

target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(注册该事件的对象)(一般为父级)。this指向永远和currentTarget指向一致(只考虑this的普通函数调用)。

事件流:捕获(自顶而下)——目标阶段——冒泡(自下而顶)