js复习事件的基本操作

一、什么是事件

1. 定义

在js中事件通常是指发生某种于Dom元素、document对象、window对象 有关的预定义或自定义的时刻或契机,这些契机通常是预定义的并且程序上依赖于它们,以关联这些契机发生时候的的功能发生代码。

2. 相关基本概念

(1)事件类型

字符串,用来表示事件类型

1
2
3
4
5
6
<div onclick="">来点我</div>
<script>
window.addEventListener('onclick',function () {
console.log('我是点击事件');
});
</script>

(2)事件目标

指事件发生在哪个对象上(dom元素、document对象、window对象)。

(3)事件处理程序

当事件发生时,执行的函数

1
2
3
4
5
6
<div onclick="console.log('来点我')">来点我</div>
<script>
window.addEventListener('onclick',function () {
console.log('我是点击事件');
})
</script>

(4)事件对象

事件发生时,事件本身包含的信息的对象(event对象)

如:位置信息,具体操作,事件对象等

1
2
3
4
5
6
7
8
<body>
<button onclick="getEvent(event)">来点我</button>
</body>
<script>
function getEvent(e) {
console.log(e); // MouseEvent {clientX:27, clientY: 16, toElement: button ...}
}
</script>

(5)事件流

浏览器中事件传播的机制:

[1]事件冒泡:

从具体节点到不具体节点,接受事件

[2]事件捕获:

从不具体节点到具体节点,接受事件

在事件达到节点之前,捕获事件,性能更好,移动设备上touch事件用捕获实现

ps: 规范中定义事件从document开始传播,但实际浏览器是从windows开始捕获(为了性能)

[3]IE事件流 (基于事件冒泡,并且他们不基于W3C规范)
  • EventTarget.attachEvent(type, listener)

    • this指向window
    • 执行顺序相反
    • detachEventEvent删除必须是同一引用
  • EventTarget.detachEventEvent(type, listener)

分两个阶段

(1)事件目标

(2)事件冒泡

[4]dom2级别事件流(现代浏览器):

添加事件处理程序

分三个阶段

(1)事件捕获 (规范不设计涉及事件目标,但是实现里会触发事件对象)

(2)事件目标

(3)事件冒泡 (为了IE增加了事件冒泡)

3.事件触发

(1) html事件

称为html内联属性或html事件

[1] 如何触发

在dom元素上on + 事件名称 = “需要执行的函数” (需要括号,因为是执行函数)

1
<button onclick="getEvent(event)">html事件</button>

例子:

1
2
3
4
5
6
7
8
<body>
<button onclick="getEvent(event)">html事件</button>
</body>
<script>
function getEvent(e) {
console.log(e);
}
</script>
[2] Html事件使用注意事项
  • Html不支持字符 需要转义
  • 可以访问全局作用域下所有函数
  • 有event 事件对象
  • this的指针指向当前是触发事件的dom元素
1
2
3
4
5
6
7
8
9
<body>
<button onclick="htmlEvent(this)">HTML事件</button>
</body>
<script>
function htmlEvent(_this) {
console.log(_this); // 不能直接打印this
console.log(this); // 指向window
}
</script>
  • 可以通过setattribute 动态修改、移除事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<button id="button" onclick="htmlEvent(this)">HTML事件</button>
<button onclick="removeEvent()">移除HTML事件</button>
</body>
<script>
var btn = document.getElementById('button');
function htmlEvent(_this) {
console.log(_this); // 不能直接打印this
console.log(this); // 指向window
btn.setAttribute('onclick',console.log('替换咯...'));
}

function removeEvent() {
btn.setAttribute('onclick',''); // 删除事件
}
</script>
[3] Html事件的缺点
  • 使用会存在时间差 写在body最后 try catch避免
  • html事件作用域,全局作用域访问限定对象会出问题
  • 代码耦合问题(需要修改两个地方)

(2) 属性事件(dom 0事件)

[1] 添加事件

获取dom元素,将on + 事件名称的属性赋予相关函数

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<button id="test">属性事件</button>
</body>
<script>
var test = document.getElementById('test');

test.onclick = getEvent;

function getEvent(e) {
console.log(e);
}
</script>
[2] 删除事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<button id="button">HTML事件</button>
<button id="button2">移除HTML事件</button>
</body>
<script>
var btn = document.getElementById('button');
var btn2 = document.getElementById('button2');
btn.onclick = htmlEvent;
btn2.onclick = removeEvent;

function htmlEvent() {
console.log(this);
}

function removeEvent() {
btn.onclick = null;
}
</script>
[3] 节点复制

事件会被复制,复制出来的事件不是指向同一个引用

[4]属性事件使用注意事项
  • 增加事件 事件处理程序(小写)

    1
    El.onclick
  • 支持Event对象

  • This指向当前dom

    1
    2
    3
    function htmlEvent() {
    console.log(this); // 指向当前dom
    }

解决问题

  • 解决了时间差问题
  • 降低了代码耦合性

缺点

  • 一次只能指定一个事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <body>
    <button id="test">属性事件</button>
    </body>
    <script>
    var test = document.getElementById('test');
    test.onclick = hello;

    test.onclick = world;

    test.onclick = helloWolrd;

    function hello() {
    console.log('hello');
    }
    // world 会覆盖 world
    function world() {
    console.log('world');
    }
    // 必须放一个fp
    function helloWolrd() {
    console.log('hello');
    console.log('world');
    }
    </script>

(3) 事件监听回调 (dom2)

注意事件类型没有on

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<button id="test">属性事件</button>
</body>
<script>
var test = document.getElementById('test');

function getEvent(e) {
console.log(e);
}

test.addEventListener('click',getEvent);
</script>

p.s: 没有dom1没有做事件相关规定

[1]绑定事件与删除事件

1
2
3
4
5
6
7
target.addEventListener(type, listener, options);

target.addEventListener(type, listener ,{capture: Boolean, passive: Boolean, once: Boolean});

target.addEventListener(type, listener, useCapture);

target.addEventListener(type, listener[, useCapture, wantsUntrusted ]); // Gecko/Mozilla only
  • type 表示监听事件类型的字符串。

  • listener

    当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数

    1
    2
    3
    4
    5
    6
    7
    var btn = document.getElementById('button');

    btn.addEventListener('click',handleEvent);

    function handleEvent() {
    console.log(this); // 指向当前dom
    }
  • options 可选
    一个指定有关 listener 属性的可选参数对象。可用的选项如下:

    • capture: Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。

      效果同useCapture可参考

      1
      2
      3
      4
      5
      6
      7
      8
      9
      var btn = document.getElementById('button');
      var container = document.getElementById('container');
      btn.addEventListener('click',function() {
      console.log('button'); // 后触发
      });

      container.addEventListener('click',function() {
      console.log('container'); // 先触发
      },{ capture: true });
- once:  Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。

1
2
3
4
var btn = document.getElementById('button');
btn.addEventListener('click',function() {
console.log('button'); // 只执行一次
},{once: true});
- passive: Boolean,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。 mozSystemGroup: 只能在 XBL 或者是 Firefox' chrome 使用,这是个 Boolean,表示 listener 被添加到 system group。 关于passive可以参考:[https://zhuanlan.zhihu.com/p/24555031](****) 具体之后文章会具体聊聊这个问题 - true的话浏览器会开2个线程为了提高性能, - 永远不会调用 preventDefault() - 同一个dom对象,只要同一事件一个设置了passivefasle,浏览器都不会做性能优化, - cancelable 为true都可以支持

option支持的安全检测例子:

1
2
3
4
5
6
7
8
9
10
11
var passiveSupported = false;

try {
var options = Object.defineProperty({}, "passive", {
get: function() {
passiveSupported = true;
}
});

window.addEventListener("test", null, options);
} catch(err) {}
  • useCapture 可选
    Boolean,是指在DOM树中,注册了该listener的元素,是否会先于它下方的任何事件目标,接收到该事件。沿着DOM树向上冒泡的事件不会触发被指定为use capture(也就是设为true)的listener。当一个元素嵌套了另一个元素,两个元素都对同一个事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。事件传播模式决定了元素以哪个顺序接收事件。进一步的解释可以查看 事件流 及 JavaScript Event order 文档。 如果没有指定, useCapture 默认为 false 。

例子:父容器元素与子容器元素都为false:

1
2
3
4
5
6
7
8
9
var btn = document.getElementById('button');
var container = document.getElementById('container');
btn.addEventListener('click',function() {
console.log('button'); // 先触发
},false);

container.addEventListener('click',function() {
console.log('container'); // 后触发
}, false);

例子:父容器元素与子容器元素都为true:

1
2
3
4
5
6
7
8
9
var btn = document.getElementById('button');
var container = document.getElementById('container');
btn.addEventListener('click',function() {
console.log('button'); // 后触发
},true);

container.addEventListener('click',function() {
console.log('container'); // 先触发
},true);

例子:父容器元素为true与子容器元素false:

1
2
3
4
5
6
7
8
9
var btn = document.getElementById('button');
var container = document.getElementById('container');
btn.addEventListener('click',function() {
console.log('button'); // 后触发
},false);

container.addEventListener('click',function() {
console.log('container'); // 先触发
},true);

例子:父容器元素为false与子容器元素true:

1
2
3
4
5
6
7
8
9
var btn = document.getElementById('button');
var container = document.getElementById('container');
btn.addEventListener('click',function() {
console.log('button'); // 先触发
},true);

container.addEventListener('click',function() {
console.log('container'); // 后触发
},false);

注意事项:

  • 事件类别没有on 如click keydown
  • this指向window
  • useCapture默认是fasle表示冒泡,true表示捕获
  • wantsUntrusted (only Gecko)
    如果为 true , 则事件处理程序会接收网页自定义的事件。此参数只适用于 Gecko,主要用于附加组件的代码和浏览器本身。请见 Interaction between privileged and non-privileged pages.
    在使用 options 对象中的特定值之前,最好确保用户的浏览器支持它,因为他并没有被所有浏览器所支持。查看更多细节关于option支持的安全检测。

  • 删除事件处理程序

1
target.removeEventListener(type, listener[, useCapture])
  • type:一个字符串,表示需要移除的事件类型,如 “click”。
  • listener: 需要移除的 EventListener 函数(先前使用 addEventListener 方法定义的)
  • useCapture 可选
    指定需要移除的 EventListener 函数是否为事件捕获。如果无此参数,默认值为 false。

注意事项

  • 用EventTarget.addEventListener指定,只能用EventTarget.removeEventListener删除
  • 如果同一个监听事件分别为“事件捕获”和“事件冒泡”注册了一次,一共两次,这两次事件需要分别移除。两者不会互相干扰。
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>
<div id="container">
<button id="button">事件</button>
<button onclick="removeBubbleEvent()">删除冒泡事件</button>
<button onclick="removeCaptureEvent()">删除捕获事件</button>
</div>

</body>
<script>
var btn = document.getElementById('button');

btn.addEventListener('click',clickEvent,false);
btn.addEventListener('click',clickEvent,true);


function removeCaptureEvent(){
btn.removeEventListener('click',clickEvent,true);
console.log('removeCaptureEvent ')
}

function removeBubbleEvent(){
btn.removeEventListener('click',clickEvent,false);
console.log('removeBubbleEvent ')
}

function clickEvent(){
console.log('clickEvent');
}
</script>
  • 删除的函数,必须与添加时函数同一引用
1
2
3
4
5
6
var div = document.getElementById('div');
var listener = function (event) {
/* do something here */
};
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="container">
<button id="button">事件</button>
<button onclick="delEvent()">删除事件</button>
</div>

</body>
<script>
var btn = document.getElementById('button');

btn.addEventListener('click',clickEvent,false);


function delEvent(){
btn.removeEventListener('click',clickEvent,false);
console.log('removeEvent')
}

function clickEvent(){
console.log('clickEvent');
}
</script>

(4) 事件目标支持情况

[1] element:

[x] html事件

[x] 属性事件 (dom0 事件)

[x] 事件监听回调 (dom2事件)

[2] document:

[x] html事件

[x] 属性事件 (dom0 事件)

[] 事件监听回调 (dom2事件)

[3] window:

实际body标签通过frameset等来支持html事件

[x] html事件

[x] 属性事件 (dom0 事件)

[x] 事件监听回调 (dom2事件)

Share