JS事件总结

JS事件

事件最重要的概念就是:事件源,事件名,监听器,处理程序

01 事件流

就是事件传播,描述的是从页面中接收事件的顺序

如果单击了某个按钮,它们认为单击事件不仅仅发生在按钮上,甚至单击了整个页面

IE事件流是事件冒泡流

Netscape是事件捕获流

其实不同的就是事件的触发时机,是在捕获阶段触发事件,还是在冒泡阶段触发事件

02 事件冒泡和事件捕获的概念

事件冒泡:事件由当前最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档document)

html 复制代码
<div id="box"></div>
// 点击box,点击事件会向上传播,box的每一层父级都会触发它们身上的点击事件
<script>
    const box = document.getElementById('box');
    box.onclick = function () {
        box.innerHTML += 'div<br>';
    }
    // 获取body
    document.body.onclick = function () {
        box.innerHTML += 'body<br>';
    }
    // 获取html
    document.documentElement.onclick = function () {
        box.innerHTML += 'html<br>';
    }
    // 获取document
    document.onclick = function () {
        box.innerHTML += 'document<br>';
    }
    // 获取window
    window.onclick = function () {
        box.innerHTML += 'window<br>';
    }
</script>

事件捕获:事件由不太具体的节点,更早的接收事件,而最具体的节点应该最后接收到事件(逐级向下传播)

html 复制代码
<div id="box"></div>
<script>
    const box = document.getElementById('box')
    box.addEventListener('click', function () {
    box.innerHTML += 'box<br>'
    },true)
// addEventListener的第三个参数用来控制事件触发时机,在捕获阶段(true)还是在冒泡阶段(false)这个参数还可以时一个对象
/*
{
    capture: true,      // 是否捕获阶段执行
    once: true,         // 是否只执行一次
    passive: true       // 是否阻止preventDefault
}
*/
    document.body.addEventListener('click', function () {
    box.innerHTML += 'body<br>'
    },true)
​
    document.documentElement.addEventListener('click', function () {
    box.innerHTML += 'html<br>'
    },true)
​
    document.addEventListener('click', function () {
    box.innerHTML += 'document<br>'
    },true)
​
    window.addEventListener('click', function () {
    box.innerHTML += 'window<br>'
    },true)
</script>

03 三种事件处理程序的绑定方式

关于三种方式的this指向问题

html 复制代码
<body>
    <button class="btn">Click Me</button>
    <script>
        const btn = document.querySelector('.btn')
        // btn.onclick = function () {
        //     console.log(this) // 当前dom对象
        // }
​
        // btn.onclick = () => {
        //     console.log(this) // window
        // }
​
        // btn.addEventListener('click',function() {
        //     console.log(this) // 当前dom对象
        // })
​
        btn.addEventListener('click',() => {
            console.log(this) // window
        })
    </script>
</body>

1、HTML事件处理程序

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #app{
            width: 200px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div id="app" onclick="console.log(event)"></div>
    <div id="app2" onclick="test(event)"></div>
    <script>
        function test(e){
            console.log(e)
        }
    </script>
</body>
</html>
javascript 复制代码
// 只要是html事件处理程序,浏览器都会对简单代码语句进行包装,因为这里面实际上都是字符串,是不能执行的,函数调用语句也是的,实际上也只是一个函数调用语句的字符串
// 下面是对两种形式的包装方式
element.onclick = function(event) {
    test(event); // 把你写在 onclick 里的字符串代码直接塞到这里执行
};
element.onclick = function(event) {
    console.log(event);
};
// 所以这种方式也可以看作是通过外层包装函数的第一个参数接收事件对象

2、DOM0级事件处理程序

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #app {
            width: 200px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div id="app">
    </div>
​
    <script>
        const box = document.getElementById('app')
        box.onclick = function(e){
            // 这种方式相当于是给执行的dom对象属性绑定方法,所以这里的this指向当前dom对象
            console.log(this)
            // 这里的全局事件对象event就是浏览器已经维护好的,可以直接使用,在window对象上
            console.log(event)
            // 当然这里也是可以通过事件处理程序的第一个参数,来接收事件对象
            console.log(e)
        }
    </script>
</body>
</html>

3、DOM2级事件处理程序

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #app{
            width: 200px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div id="app"></div>
    <script>
        const box = document.getElementById('app')
        box.addEventListener('click',function(e){
            console.log(this)
            // 这里明显没有传递参数,所以这里的event还是window上的全局事件对象
            console.log(event)
            // 当然这里也是可以通过事件处理程序的第一个形参,来接收事件对象
            console.log(e)
        })
    </script>
</body>
</html>

04 事件绑定的兼容写法

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">你过来呀!</button>
    <script>
        // DOM2级事件处理程序,addEventListener,ie不支持,需要使用attachEvent
        const btn = document.getElementById('btn')
        // btn.addEventListener('click',fn)
        // btn.attachEvent('onclick',fn)
        addEvent(btn,'click',function(){
            console.log(this)
        })
        // 定义兼容性绑定函数,attachEvent中的this指向window,也需要做兼容处理
        function addEvent(element,type,handler){
            if(element.addEventListener){
                element.addEventListener(type,handler,false)
            }else if(element.attachEvent){
                element.attachEvent('on' + type,function(){
                    handler.call(element)
                })
            }
        }
    </script>
​
</body>
</html>

05事件调用顺序

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div id="box" onclick="this.innerHTML += 'html\n'"></div>
    <script>
        // 如果同时出现html级事件处理程序和DOM0级事件处理程序,html级会被覆盖掉
        const box = document.getElementById('box')
        box.onclick = function(){
            this.innerHTML += 'DOM0\n'
        }
​
        box.addEventListener('click',function(){
            this.innerHTML += 'DOM2\n'
        })
    </script>
​
</body>
</html>

06事件目标对象

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style type="text/css">
        .item {
            width: 100px;
            height: 80px;
            background-color: lightblue;
            margin: 50px 10px;
        }
    </style>
</head>
<body>
    <ul id="box">
        <li class="item">1</li>
        <li class="item">2</li>
    </ul>
    <script>
        const box = document.getElementById('box')
        box.onclick = function(e){
            e = e || window.event
            // 1、currentTarget 事件绑定的元素
            console.log(e.currentTarget)
            // 2、target 事件触发的元素
            console.log(e.target)
            // 3、srcElement 事件触发的元素,兼容性好
            console.log(e.srcElement)
​
            // this 指向事件绑定的元素
            console.log(e.currentTarget === this)
            console.log(e.target === this)
​
            // 兼容性处理
            let target = e.target || e.srcElement
        }
    </script>
​
</body>
</html>

07 事件代理(事件委托)

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件代理</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        ul {
            list-style: none;
            overflow: hidden;
            margin-top: 50px;
        }
        ul li {
            float: left;
            width: 100px;
            height: 30px;
            background-color: lightblue;
            text-align: center;
            line-height: 30px;
            margin: 0 10px;
            color: #fff;
        }
    </style>
</head>
<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>
    <script>
        window.onload = function () {
            const ul = document.querySelector('ul')
            setTimeout(() => {
                const item = document.createElement('li')
                item.innerHTML = '6'
                ul.appendChild(item)
            }, 100)
​
            // 获取所有li元素,这里只获取了原来的5个li元素,新增的li元素没有添加事件监听,所以不会触发事件
            // const lis = document.querySelectorAll('li')
            // lis.forEach(item => {
            //     item.addEventListener('mouseover',function () {
            //         this.style.backgroundColor = 'red'
            //     })
            //     item.addEventListener('mouseout',function () {
            //         this.style.backgroundColor = 'lightblue'
            //     })
            // })
​
            // 给未来的li添加事件监听,这里使用事件代理
            ul.addEventListener('mouseover',function (e) {
                if(e.target.tagName === 'LI'){
                    e.target.style.backgroundColor = 'red'
                }
            })
            ul.addEventListener('mouseout',function (e) {
                if(e.target.tagName === 'LI'){
                    e.target.style.backgroundColor = 'lightblue'
                }
            })
        }
    </script>
</body>
</html>

08 事件冒泡,阻止冒泡

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件冒泡</title>
​
</head>
<body>
    <button id="btn" style="width: 200px; height: 30px;">点击我</button>
    <input type="text" id="test">
    <script>
        // 两个属性,两个方法 bubbles cancelBubble stopPropagation() stopImmediatePropagation()
        // 1、bubble 返回一个布尔值,表示当前事件是否冒泡,注意大部分事件都是冒泡事件,但是focus blur scroll事件不是冒泡事件
        const btn = document.getElementById('btn')
        const test = document.getElementById('test')
​
        btn.addEventListener('click',function(e){
            console.log(e.bubbles)  // true
        })
        test.addEventListener('focus',function(e){
            console.log(e.bubbles) // false
        })
        // 2、cancelBubble 取消事件冒泡,也不能阻止同一事件的其他监听函数执行
        btn.addEventListener('click',function(e){
            e.cancelBubble = true
        })
        // 3、stopPropagation() 取消事件冒泡,ie8不支持,但是它无法阻止同一事件的其他监听函数执行
        // 4、stopImmediatePropagation() 取消事件冒泡,ie8不支持,但是它可以阻止同一事件的其他监听函数执行
        btn.addEventListener('click',function(e){
            e = e || window.event
            // e.stopImmediatePropagation()
            this.innerHTML = '阻止冒泡'
        })
        btn.addEventListener('click',function(e){
            console.log('按钮的另一个监听函数执行了')
        })
        document.body.onclick = function(){
            console.log('这里打印就是冒泡了,没打印就是没有冒泡')
        }
    </script>
</body>
</html>

09 取消默认事件

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>取消默认事件</title>
</head>
<body>
    <!-- 这两种方式都是让浏览器执行一段"返回空值"的代码,骗过浏览器让其不跳转 -->
    <a href="javascript:;">返回空值取消默认事件</a>
    <a href="javascript:void(0);">void(0)取消默认事件</a>
    <a href="http://www.baidu.com" id="baidu">事件对象取消默认事件</a>
​
    <script>
        // 事件对象的两个方法来阻止默认行为
        var a = document.getElementById('baidu');
        a.onclick = function(e) {
            e = e || window.event;
            // 阻止默认行为 ie8不支持
            // e.preventDefault();
            // 阻止默认事件,兼容ie8以下的浏览器
            // e.returnValue = false;
            // 兼容写法
            if(e.preventDefault) {
                e.preventDefault();
            } else {
                e.returnValue = false;
            }
            // 这样也可以阻止默认行为,兼容了所有浏览器
            return false
        }
    </script>
</body>
</html>

10事件对象中的坐标位置

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>鼠标事件对象属性</title>
    <style>
        #box{
            width: 200px;
            height: 200px;
            background-color: red;
        }
    </style>
</head>
<body>
    <div id="parent">
        <div id="box"></div>
    </div>
    <script>
        var box = document.getElementById('box');
        box.onclick = function(event){
            // 相对于浏览器有效窗口的坐标,也就是相对于body的坐标
            console.log(event.clientX);
            console.log(event.clientY);
            // 相对于我电脑显示器的坐标
            console.log(event.screenX);
            console.log(event.screenY);
            // 相对于浏览器页面的距离,包括滚动条
            console.log(event.pageX);
            console.log(event.pageY);
            // 相对于当前事件源的偏移坐标
            console.log(event.offsetX);
            console.log(event.offsetY);
        }
​
    </script>
</body>
</html>
相关推荐
小狼154541 小时前
拼多多订单数据导出 CSV 实战:接口分页、字段平铺与 5 个数据坑,多多开票助手
java·前端·javascript
搬砖的小码农_Sky1 小时前
AI Agent:Node.js和浏览器运行环境的本质区别
javascript·node.js·人机交互
ss2731 小时前
AI全栈实战 | 2.2-02 React 思维模型:数据不可变 vs Vue 数据可变,两种哲学把复杂度推给谁
前端·vue.js·react.js
想吃火锅10051 小时前
【leetcode】189.轮转数组js
开发语言·javascript·leetcode
天衍四九-1 小时前
Docker Compose企业实战系列(三):Vue/React前端项目容器化完整部署
前端·vue.js·docker
Rosanci2 小时前
从零到合入主线:我的 DeepSeek Harness 开源贡献实战手记
开发语言·前端·开源
用户960819842232 小时前
多周期联看时十字光标对不齐:前端排查「时间桶」的三步清单
前端
Data analyse4562 小时前
数据合规的敏感数据怎么识别?
前端·人工智能·数据分析
陪我一起学编程2 小时前
vue3笔记
前端·webpack·vue3·vite·脚手架·vue3笔记·组件式