05-事件处理

7/19/2022

# 1. 事件类型

事件 + 事件的反馈 = 前端交互(前端的核心)

鼠标事件:

  • onclick 点击事件 = onmousedown + onmouseup
  • ondbclick 双击事件
  • onmousedown 鼠标按下去
  • onmouseup 鼠标抬起来
  • onmouseover 鼠标移到某元素上面
  • onmouseout 鼠标离开某元素上面
  • onmousemove 鼠标在元素上面的移动
  • ....

键盘事件:

  • onkeydown 键盘按下去
  • onkeyup 键盘抬起来
  • onkeypress 键盘抬住

事件的执行顺序是 onkeydown、onkeypress、onkeyup

  • down事件先发生
  • press发生在文本被输入
  • up发生在文本输入完成

加载事件:

  • load 加载 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源 事件源是window
  • unload 加载 退出页面 此事件不好捕获到 也没有什么用
  • DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 img 和样式表之类的外部资源可能尚未加载 完成。

表单事件:

  • focus: 获取焦点
  • blur: 失去焦点
  • change: 改变输入框中的内容,并失去焦点时触发
  • input:内部发生变化,立即触发,不等到失去焦点
  • submit: 当点击了提交按钮,会触发submit事件
  • reset: 当点击了重置按钮,会触发reset事件

其它事件:

  • resize: 改变了浏览器窗口大小时,会触发resize事件
  • scroll: 页面滚动时,会触发scroll事件
  • ....
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
        // 代码执行是从上到下执行,在执行JS代码时,
        // HTML结构还没加载完毕,获取DOM节点,是没有
        // 办法获取的。
        // 所以我们需要等DOM节点加载完毕,再去获取DOM元素,
        // 加载事件  load事件  事件源是window
        window.onload = function(){
            let btn = document.getElementById("btn");
            console.log(btn); // null / button
        }
    </script>
</head>
<body>
    <button id="btn">登录</button>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <input type="text" id="ipt">
    <script>
        window.onload = function(){
            let ipt = document.getElementById("ipt"); 
            // 表示表单获取焦点时触发
            ipt.onfocus = function(){
                console.log("表单获取了焦点~");
            }
            ipt.onblur = function(){
                console.log("表单失去了焦点~");
            }
            // 输入框中的数据发生了变化,并失败了焦点就会触发
            ipt.onchange = function(){
                console.log("表单中的数据发生变化~");
            }
            ipt.oninput = function(){
                console.log("内容变化了~");
            }
        }
    </script>
</body>
</html> 
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

# 2. 事件的绑定和解绑

# 2.1 HTML级别的事件绑定

可以在HTML开始标签上,去绑定事件,这种绑定基本不用

<body>
    <!-- 
        button叫事件源  
        click叫事件类型    on仅仅是前缀
        "alert('点我弄啥~')"事件反馈   事件处理程序   也叫监听器
        我们说的事件绑定:指的是绑定监听器
    -->
    <!-- <button onclick="alert('点我弄啥~')">点我</button> -->
    <!-- test() 加了() 这行代码在你眼中就是一个值 -->
    <!-- 在test()外面,还包一层函数,这一层函数你看不到   -->
    <!-- 只有点击了按钮,外面的函数才会执行,才能执行到里面的test() -->
    <!-- ()不加不行 -->
    <button onclick="test()">点我</button>
    <script>
        function test(){
            alert("点我弄啥~~")
        }
    </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 2.2 DOM0事件绑定

DOM0事件绑定,就是给DOM元素的onxxxx属性赋值,是基于属性赋值的操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        .loading{
            background-color: #ccc;
            color: #424242;
            outline: 0px;
            border: none;
        }
    </style>
</head>
<body>
    <button id="btn">加载更多</button>
    <script>
        let btn = document.getElementById("btn");
        // DOM0级别的事件绑定
        // 基于属性赋值   如果重复绑定多次,后面的会覆盖前面的
        btn.onclick = function(){
            // console.log("1");
            this.innerHTML = "加载中...."
            // this.className = "loading"
            this.classList.add("loading")

            var self = this;
            setTimeout(function(){
                // this表示window
                // this.innerHTML = "加载更多"
                // this.classList.remove("loading")
                self.innerHTML = "加载更多"
                self.classList.remove("loading")
            },2000)
        }
        // 由于是基于属性赋值,后面赋值的会覆盖掉前面的
        // btn.onclick = function(){
        //     console.log("2");
        // }
    </script>
</body>
</html>
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

现在学习了ES6,可以使用箭头函数,如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        .loading{
            background-color: #ccc;
            color: #424242;
            outline: 0px;
            border: none;
        }
    </style>
</head>
<body>
    <button id="btn">加载更多</button>
    <script>
        let btn = document.getElementById("btn");
        btn.onclick = function(){
            this.innerHTML = "加载中...."
            this.classList.add("loading")
            setTimeout(()=>{
                this.innerHTML = "加载更多"
                this.classList.remove("loading")
            },2000)
        }
    </script>
</body>
</html>
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

如果有HTML级别的事件绑定和DOM0的事件绑定,谁的优先给高,演示如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button id="btn" onclick="test()">加载更多</button>
    <script>
        function test(){
            console.log("html级别的事件绑定");
        }

        // dom0级别的事件绑定优先级是高于HTML级别的事件绑定的
        let btn = document.getElementById("btn");
        btn.onclick = function(){
            console.log("dom0级别的事件绑定");
        }
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2.3 DOM2的事件绑定

DOM2的事件绑定是基于事件池的,不是基于属性赋值的,所以可以多次绑定,不会覆盖掉,演示代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        .loading{
            background-color: #ccc;
            color: #424242;
            outline: 0px;
            border: none;
        }
    </style>
</head>
<body>
    <button id="btn">加载更多</button>
    <script>
        let btn = document.getElementById("btn");
        // DOM2的事件绑定
        // 基于事件池   事件绑定都是异步代码  都是宏任务
        btn.addEventListener("click",function(){
            this.innerHTML = "加载中..."
            this.className = "loading"
            
            setTimeout(()=>{
                this.innerHTML = "加载更多"
                this.className = ""
            },2000)
        });
        btn.addEventListener("click",function(){
            console.log("~我是有底线的~");
        })
    </script>
</body>
</html> 
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

特别需要注意,绑定的是事件处理程序,也就是监听器,也就是事件反馈,如果你的监听器是同一个,即使你绑定了多次,也只会执行一次,演示如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button id="btn">加载更多</button>
    <script>
        let btn = document.getElementById("btn");
        btn.addEventListener("click",function(){
            console.log("111");
        });
        btn.addEventListener("click",function(){
            console.log("222");
        })
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button id="btn">加载更多</button>
    <script>
        let btn = document.getElementById("btn");
        // 如果这样绑定,事件池中只有一个Fn
        btn.addEventListener("click",fn);
        btn.addEventListener("click",fn)
        function fn(){
            console.log("xxx");
        }
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3. 事件解绑

绑定它是绑定的监听器,解绑也是解绑监听器,解绑也分DOM0的解和DOM2的解绑,演示DOM0的解绑代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        button{
            outline: none;
            border: none;
            width: 100px;
            height: 30px;
            background-color: red;
            color: #fff;
            text-align: center;
            line-height: 30px;
        }
        .got{
            background-color: #ddd;
        }
    </style>
</head>
<body>
    <button id="btn">领取优惠劵</button>
    <script>
        let btn = document.getElementById("btn");
        // 事件绑定  绑定的监听器
        btn.onclick = function(){
            this.innerHTML = "已领取"
            this.className = " got"
            console.log("已领取~");

            // 已领取 需要解绑  解绑的也是监听器
            this.onclick = null; // DOM0的解绑
        }
    </script>
</body>
</html>
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

下面的DOM2的事件解绑是不OK的,如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        button {
            outline: none;
            border: none;
            width: 100px;
            height: 30px;
            background-color: red;
            color: #fff;
            text-align: center;
            line-height: 30px;
        }

        .got {
            background-color: #ddd;
        }
    </style>
</head>

<body>
    <button id="btn">领取优惠劵</button>
    <script>
        let btn = document.getElementById("btn");
        
        // DOM2的事件绑定
        btn.addEventListener("click",fn,false)

        // DOM2的解绑  解绑的参数需要和绑定的参数一样
        // 不行:因为上,刚把fn扔到池中,下面紧接着,把fn从池中扔出来的
        btn.removeEventListener("click", fn,false)

        function fn(){
            this.innerHTML = "已领取"
            this.className = " got"
            console.log("已领取~");
        }
    </script>
</body>

</html>
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

正确的解绑如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        button {
            outline: none;
            border: none;
            width: 100px;
            height: 30px;
            background-color: red;
            color: #fff;
            text-align: center;
            line-height: 30px;
        }

        .got {
            background-color: #ddd;
        }
    </style>
</head>

<body>
    <button id="btn">领取优惠劵</button>
    <script>
        let btn = document.getElementById("btn");
        
        // DOM2的事件绑定
        btn.addEventListener("click",fn,false)


        function fn(){
            this.innerHTML = "已领取"
            this.className = " got"
            console.log("已领取~");

            // DOM2的解绑  解绑的参数需要和绑定的参数一样
            btn.removeEventListener("click", fn,false)
        }
    </script>
</body>

</html>
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

其它的解绑方案如下:

<body>
    <button id="btn">领取优惠劵</button>
    <script>
        let btn = document.getElementById("btn");
        
        // DOM2的事件绑定
        btn.addEventListener("click",function fn(){
            this.innerHTML = "已领取"
            this.className = " got"
            console.log("已领取~");

            btn.removeEventListener("click", fn,false)
        })
    </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
    <button id="btn">领取优惠劵</button>
    <script>
        let btn = document.getElementById("btn");
        
        // DOM2的事件绑定
        btn.addEventListener("click",function(){
            this.innerHTML = "已领取"
            this.className = " got"
            console.log("已领取~");
            // console.log(arguments.callee);

            btn.removeEventListener("click", arguments.callee, false)
        })
    </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4. 事件冒泡和事件捕获

  • 我们会发现默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序我们称之为事件冒泡(Event Bubble)
  • 另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获(Event Capture)

如果我们都监听,那么会按照如下顺序来执行:

  • 捕获阶段(Capturing phase):事件(从 Window)向下走近元素
  • 目标阶段(Target phase):事件到达目标元素
  • 冒泡阶段(Bubbling phase):事件从元素上开始冒泡

开发中通常会使用事件冒泡,所以事件捕获了解即可。

# 5. 事件对象

当一个事件发生时,就会有和这个事件相关的很多信息:

  • 比如事件的类型是什么,你点击的是哪一个元素,点击的位置是哪里等等相关的信息
  • 那么这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象;
  • 该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作;

如何获取这个event对象呢?

  • event对象会在传入的事件处理(event handler)函数回调时,被系统传入;
  • 我们可以在回调函数中拿到这个event对象;

常见的属性:

  • type:事件的类型;
  • target:当前事件发生的元素;
  • currentTarget:当前处理事件的元素;
  • eventPhase:事件所处的阶段;
  • offsetX、offsetY:事件发生在元素内的位置;
  • clientX、clientY:事件发生在客户端内的位置;
  • pageX、pageY:事件发生在客户端相对于document的位置;
  • screenX、screenY:事件发生相对于屏幕的位置;

常见的方法:

  • preventDefault:取消事件的默认行为;
  • stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止);

# 6. 事件处理中的this

在函数中,我们也可以通过this来获取当前的发生元素:

  • 在浏览器内部,调用event handler是绑定到当前的事件源上的

# 7. 事件委托(event delegation)

事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式(也是一种设计模式)

  • 当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;
  • 可以通过event.target获取到当前监听的元素;
Last Updated: 12/25/2022, 10:02:14 PM