Event(事件对象)

# Event(事件对象)

(飞机-黑匣子)当一个事件发生的时候,和当前这个对象发生的这个事件有关的一些详细的信息(例如鼠标位置、按键信息)都会被临时保存到一个指定地方-event对象,供我们在需要的调用。

  • 使用条件
  1. 事件对象必须在一个事件调用的函数里面使用才有内容,而且是直接调用。
  2. 事件函数:事件调用的函数,一个函数是不是事件函数,不在定义的决定,而是取决于调用的时候。
  • 兼容
  1. ie/chrome : event是一个内置全局对象。
  2. 标准下(比如ff) : 事件对象是通过事件函数的第一个参数传入。 如果一个函数是被事件调用的,那么这个函数定义的第一个参数就是事件对象。
var ev = event || window.event
1
  • 遍历event对象
for ( var attr in ev ) {
    console.log( attr + ' = ' + ev[attr] );
}
1
2
3

[TOC]

# 一、DOM事件的级别

参考链接:https://www.jianshu.com/p/622d994906f7 (opens new window)

为何事件没有DOM1的写法呢?因为,DOM1标准制定的时候,没有涉及与事件相关的内容。

# 1.1 DOM0级事件处理

将一个函数赋值给一个事件处理属性。

element.onclick = function() {
    // ...
}
1
2
3

DOM0级事件处理程序的缺点在于一个处理程序无法同时绑定多个处理函数,比如给点击按钮事件上加上另外一个函数。

document.onclick = fn1;
document.onclick = fn2; 
//会覆盖前面绑定fn1
1
2
3
// 删除事件处理程序
btn.onclick = null;
1
2

# 1.2 DOM2级事件处理

  • 允许一个程序添加多个处理函数。

  • DOM2级事件定义了addEventListener 和 removeEventListener两个方法。

btn.addEventListener('click', showFn, false);
btn.addEventlistener('mouseover',showfn,false)
1
2
  • ie:obj.attachEvent(事件名称,事件函数);

    1. 没有捕获

    2. 事件名称有on

    3. this指向window

  • 标准:obj.addEventListener(事件名称,事件函数,是否捕获);

    1. 有捕获;默认是false冒泡;true是捕获
    2. 事件名称没有on
  • 兼容

function bind(obj, evname, fn) {
    if (obj.addEventListener) {
        obj.addEventListener(evname, fn, false);
    } else {
        obj.attachEvent('on' + evname, function() {
            fn.call(obj);
        });
    }
}
1
2
3
4
5
6
7
8
9

# 1.2.2 解绑事件

document.onclick = fn1;
//document.onclick = null;	//取消
1
2
ie : obj.detachEvent(事件名称,事件函数);
标准 : obj.removeEventListener(事件名称,事件函数,是否捕获);
1
2

# 1.3 DOM3级事件处理

在DOM2级事件的基础上添加很多事件类型。

# 1.3.1 UI事件

当用户与页面上的元素交互时触发,如:load、scroll。

# 1.3.2 焦点事件

当元素获得或失去焦点时触发,如:blur、focus 。

  1. 焦点:使浏览器能够区分用户输入的对象。

    一个元素有焦点的时候,那么它就可以接收用户的输入。

  2. 设置焦点: a.点击 b.tab c.js

  3. 不是所有元素都能够接受焦点的,能够响应用户操作的元素才有焦点。

  • onfocus : 当元素获取到焦点的时候触发。
oText.onfocus = function() {
    if ( this.value == '请输入内容' ) {
        this.value = '';
    }
}
1
2
3
4
5
  • onblur : 当元素失去焦点的时候触发。
oText.onblur = function() {
    if ( this.value == '' ) {
        this.value = '请输入内容';
    }
}
1
2
3
4
5

4.obj.focus() 给指定的元素设置焦点。 obj.blur() 取消指定元素的焦点。 obj.select()选择指定元素里面的文本内容,但是这里的select()选定的文本只能是可交互的文本。

# 1.3.3 鼠标事件

当用户通过鼠标在页面执行操作时触发如:onclick、mouseup 。

# 1.3.3.1 onmouseover的使用

  1. 当鼠标在一个元素上面移动的触发;触发频率不是像素,而是间隔时间,在一个指定时间内(很短),如果鼠标的位置和上一次的位置发生了变化,那么就会触发一次。
  2. 这里要注意的是,因为鼠标在移动的时候可能会移出div,所以要写成document.onmousemove
# 1.3.3.2 各个鼠标事件的区别
  • mouseenter
    • 当鼠标移入某元素时触发。
  • mouseleave
    • 当鼠标移出某元素时触发。
  • mouseover
    • 当鼠标移入某元素时触发,移入和移出其子元素时也触发
  • mouseout
    • 当鼠标移出某元素时触发,移入和移出其子元素时也触发
  • mousemove
    • 当鼠标在某元素上移动时触发,即使在其子元素上也会触发。

# 1.3.4 滚轮事件

当使用鼠标滚轮或类似设备时触发,如:mousewheel 文本事件,当在文档中输入文本时触发,如:textInput 。

# 1.3.4.1 onmousewheel/DOMMouseScroll

ie/chrome : onmousewheel firefox : DOMMouseScroll 必须用addEventListener

oDiv.onmousewheel = fn;
//因为ie没有addEventListener这个属性,所以要做判断
if (oDiv.addEventListener) {
    oDiv.addEventListener('DOMMouseScroll', fn, false);
}
1
2
3
4
5
# 1.3.4.2 wheelDelta/detail

ie/chrome : event.wheelDelta上:120;下-120 firefox : event.detail上:-3;下:3

//兼容处理
var b = true;
if (ev.wheelDelta) {
    b = ev.wheelDelta > 0 ? true : false;
} else {
    b = ev.detail < 0 ? true : false;
}
1
2
3
4
5
6
7

# 1.3.5 键盘事件

当用户通过键盘在页面上执行操作时触发,如:keydown、keypress 。

  1. event.keyCode获取按键键值(数字类型)
  2. document.onkeydown 当键盘按键按下的时候触发(按下不抬起则连续触发)。 document.onkeyup当键盘按键抬起的时候触发。
  3. event.ctrlKey,shiftKey,altKey 当一个事件发生的时候,如果ctrl||shift ||alt 是按下的状态,返回true,否则返回false。
  4. 不是所有元素都能够接收键盘事件,能够响应用户输入的元素,能够接收焦点的元素就能够接收键盘事件。 oDiv(这里是个普通色块).onkeydown× document.onkeydown
  5. 实例注意事项
//提交留言
oText.onkeyup = function(ev) {
    var ev = ev || window.event;
    if ( this.value != '' ) {
        //不可以写成:ev.keyCode==13&&ev.keyCode==17
        if (ev.keyCode == 13 && ev.ctrlKey) {
            var oLi = document.createElement('li');
            oLi.innerHTML = this.value;
            if ( oUl.children[0] ) {
                oUl.insertBefore( oLi, oUl.children[0] );
            } else {
                oUl.appendChild( oLi );
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 1.3.6 合成事件

当为IME(输入法编辑器)输入字符时触发,如:compositionstart 。

# 1.3.7 变动事件

当底层DOM结构发生变化时触发,如:DOMsubtreeModified 同时DOM3级事件也允许使用者自定义一些事件。

# 二、DOM事件模型、DOM事件流

# 2.1 DOM事件模型

# 2.1.1 捕获

从 window 对象传到 目标元素。

# 2.1.1.1 具体流程

捕获阶段,事件依次传递的顺序是:window --> document --> html--> body --> 父元素、子元素、目标元素。

window.addEventListener("click", function () {
    alert("捕获 window");
}, true);

document.addEventListener("click", function () {
    alert("捕获 document");
}, true);

// 获取html节点: document.documentElement 		
document.documentElement.addEventListener("click", function () {
    alert("捕获 html");
}, true);

// 获取body节点:document.body
document.body.addEventListener("click", function () {
    alert("捕获 body");
}, true);

fatherBox.addEventListener("click", function () {
    alert("捕获 father");
}, true);

childBox.addEventListener("click", function () {
    alert("捕获 child");
}, 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

# 2.1.2 冒泡

从目标元素传到 Window 对象。

默认情况下,事件处理程序在冒泡阶段执行。

事件冒泡机制:当一个元素接收到事件的时候,会把它接收的所有都传给父级,一直传到window对象。

oBtn.onclick = function(ev) {
    var ev = ev || event;
    oDiv.style.display = 'block';
}

document.onclick = function() {
    setTimeout(function() {
        oDiv.style.display = 'none';
    }, 1000);
}
//点击按钮后,由于冒泡到文档,一秒后,oDiv就会消失
1
2
3
4
5
6
7
8
9
10
11

利用事件冒泡机制

<script>
    window.onload = function() {
        var oDiv =document.getElementById('div1');
        oDiv.onmouseover = function() {
            this.style.left = '0px';
        }
        oDiv.onmouseout = function() {
            this.style.left = '-100px';
        }
    }
</script>

<body>
    <div id="div1">
        <div id="div2">分享到</div>
    </div>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

这样鼠标扫到分享到,也可以显示div1部分。

# 2.2 DOM事件流

三个阶段是:捕获->目标->冒泡。

# 2.2.1 捕获

# 2.2.2 目标

导致事件的最深嵌套元素是事件的目标。

# 2.2.3 冒泡

# 三、Event常见API方法

# 3.1 阻止默认事件

默认事件:当一个事件发生的时候浏览器自己会默认做的事情。

// 取消默认事件
//谷歌及IE8以上
ev.preventDefault();
//无兼容问题(但不能用于节点直接onclick绑定函数)
return false;
1
2
3
4
5

# 3.2 阻止冒泡

if(ev && ev.stopPropagation) {
    //非IE浏览器
    ev.stopPropagation();
} else {
    //IE浏览器(IE11以下)
    ev.cancelBubble = true;
}
1
2
3
4
5
6
7

# 3.3 设置事件优先级

 event.stopImmediatePropagation();
1

用addEventListener给某按钮同时注册了事件A、事件B,则单击按钮,就会依次执行事件A和事件B。

若单击按钮时,只执行事件A,则在事件A的响应函数中加入该语句,阻止执行事件B。

# 3.4 获取鼠标位置

clientX[Y] : 当一个事件发生的时候,鼠标到页面可视区的距离

# 四、内存和性能

# 4.1 事件委托

解决事件处理程序过多的问题。

事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

只需在DOM树中尽量最高的层次上添加一个事件处理程序。

function title(event) {
    document.title = event.target.id;
}

// let one = document.getElementById("one");
// let two = document.getElementById("two");
// let three = document.getElementById("three");
// one.addEventListener('click', title);
// two.addEventListener('click', title);
// three.addEventListener('click', title);

let parent = document.getElementById("parent");
parent.addEventListener('click', function (event) {
    switch (event.target.id) {
        case "one":
            title(event);
            break;
        case "two":
            title(event);
            break;
        case "three":
            title(event);
            break;
    }
});
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
document.addEventListener("click",function(event){
	var target = event.target;
	if(target.nodeName == "LI"){
		alert(target.innerHTML);
	}
})
1
2
3
4
5
6
  • 好处

    • document对象很快就可以访问到,只要可点击的元素呈现在页面上,就可以立即具备适当的功能。
    • 只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。
    • 整个页面占用的内存空间更少,能提升整体性能。
  • 注意点

    • 最适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress。虽然mouseover和mouseout事件也冒泡,但要适当处理它们并不容易,而且经常需要计算元素位置。(因为当鼠标从一个元素移到其子节点时,或者当鼠标移出该元素时,都会触发mouseout事件。)
    • focus、blur这些事件是没有事件冒泡机制的,所以无法委托绑定事件。

# 4.1.2 常用API

event.currentTarget   //当前所绑定的事件对象。在事件委托中,指的是父元素

event.target  //当前被点击的元素。在事件委托中,指的是子元素
1
2
3

# 4.2 移除事件处理程序

# 五、自定义事件

# 5.1 new Event()

// 创建事件
var myEvent = new Event('clickTest');

// 监听事件
element.addEventListener('clickTest', function () {
    console.log('smyhvae');
});

//触发事件
element.dispatchEvent(myEvent); 
1
2
3
4
5
6
7
8
9
10

上面这个事件是定义完了之后,就直接自动触发了。在正常的业务中,这个事件一般是和别的事件结合用的。

比如延时器设置按钮的动作:

setTimeout(function () {
    element.dispatchEvent(myEvent); 
}, 1000);
1
2
3

# 5.2 new CustomEvent()

相比Event构造函数,可以向事件对象添加更多数据。

// detail 属性可用于传递自定义数据,CustomEvent 接口可以为 event 对象添加更多的数据。
// add an appropriate event listener
obj.addEventListener("cat", function(e) { process(e.detail) });

// create and dispatch the event
var event = new CustomEvent("cat", {
    detail: {
        hazcheeseburger: true
    }
});
obj.dispatchEvent(event);
1
2
3
4
5
6
7
8
9
10
11