JavaScript DOM 事件

1.注册事件(绑定事件)

1.1 注册事件概述

给元素添加事件 成为注册事件或者绑定事件。

注册事件有两种方式 传统方式和方法监听注册。

传统注册方式:

利用on开头的事件 onClick

例如 <button onClick = 'alert('hi')'></button>  button.onClick =function()

特点:注册事件的唯一性 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数.

方法监听注册方式:

w3c标准 推荐方式

addEventListener() 他是一个方法 IE9之前不兼容  用的是attacEvent()代替。

特点:同一个元素同一个事件可以注册多个监听器 按注册顺序依次执行

<!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>传统注册事件</button>
    <button>方法监听注册事件</button>
    <button>IE9之前</button>
    <script>
        var btns = document.querySelectorAll('button');
        btns[0].onclick = function () {
            alert('哈哈哈');
        }
        //覆盖前一个
        btns[0].onclick = function () {
            alert('嘻嘻嘻');
        }

        btns[1].addEventListener('click', function () {
            alert('哇哇哇');
        })
        btns[1].addEventListener('click', function () {
            alert('噜噜噜');
        })

        // addEventListener(type,listener,[,useCapture])
        // type 事件类型的字符串 比如click mouseover 注意这里不要带on
        // listener 事件处理函数 事件发生时 会调用该监听函数
        // useCapture 可选参数 是一个bool值 默认是false 

        //IE9之前不支持addEventListener 使用attachEvent()代替 但是是非标准的 尽量不要再生产环境中使用
        //attachEvent(eventNameWithOn,callBack);
        //eventNameWithOn 事件类型字符串 比如 onclick onmouseover 这里要改on
        //callBack 事件处理函数 当目标出发事件时回调函数被调用
        btns[2].attachEvent('onclick',function() {
            alert('IE9');//只能IE9符合条件 其他浏览器也不识别
        })
    </script>
</body>

</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>各个浏览器都可以</button>
    <script>
        //兼容性处理原则 首先处理大多数浏览器 再处理特殊浏览器
        var btn = document.querySelector('button');
        var fn = function btnClick() {
            alert('我被点击了');
        }
        addEventListener1(btn, 'click', fn);

        function addEventListener1(element, eventName, fn) {
            if (element.addEventListener) {
                element.addEventListener(eventName, fn);
            } else if (element.attachEvent) {
                element.attachEvent('on' + eventName, fn)
            } else {
                element['on' + eventName] = fn
            }
        }
    </script>
</body>

</html>

删除事件(解绑事件)

传统注册方式 可以使用 eventTarget.onclick = null 来解除绑定的事件。

方法监听注册方式 可以使用eventTarget.removeEventListener(type,listener,[useCapture]);来解除绑定事件.

<!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>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <script>
        var divs = document.querySelectorAll('div');
        divs[0].onclick = function () {
            alert('哈哈哈');
            divs[0].onclick = null;
        }
        divs[1].addEventListener('click', fn);

        function fn() {
            alert('嘻嘻嘻');
            divs[1].removeEventListener('click', fn);
        }
        //只在IE9以下浏览器适用
        divs[2].attachEvent('onclick', fn1);

        function fn1() {
            alert('嘿嘿嘿')
            divs[2].detachEvent('onclick', fn1);
        }
        //兼容性的移除函数
        function removeEventListener(element,eventName,fn) {
            if (element.removeEventListener) {
                element.removeEventListener(eventName,fn);
            }else if (element.detachEvent) {
                element.detachEvent('on' + eventName,fn);
            }else {
                element['on' + eventName] = null;
            }
        }
    </script>
</body>

</html>

DOM事件流

事件流描述的是从页面中接收事件的顺序

事件发生时会在元素节点之间按照特定的顺序传播 这个传播过程即DOM事件流

比如我们给一个div注册了点击事件:

1.捕获阶段 2.当前目标阶段 3 冒泡阶段

事件冒泡:IE最早提出 事件开始时由最具体的元素接收 然后逐级向上传播到DOM最顶层节点的过程

事件捕获:网景最早提出 有DOM最顶层节点开始 然后逐级向下传播到最具体的元素接收的过程.

注意:

js代码中只能执行捕获或者冒泡其中的一个阶段

onclick 和 attachEvent 传统的 只能得到冒泡阶段

<!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>
        .father {
            width: 200px;
            height: 200px;
            background-color: purple;
            overflow: hidden;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: pink;
            margin: 50px;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        // dom 事件流三个阶段
        // 1.js代码中只能执行捕获或者冒泡其中的一个阶段
        // 2.onclick 和 attachEvent(IE) 只能得到冒泡
        var son = document.querySelector('.son')
        // 第三个参数:true 处于捕获阶段 false 处于冒泡阶段
        //body->father->son
        son.addEventListener('click', function () {
            alert('son');
        }, true)
        var father = document.querySelector('.father');
        father.addEventListener('click', function () {
            alert('father');
        }, true)
        //上面的可以看到 先弹出了father 后弹出了son  因为是时间处于捕获阶段 所以先触发了父亲
        son.addEventListener('click', function () {
            alert('son');
        }, false)
        var father = document.querySelector('.father');
        father.addEventListener('click', function () {
            alert('father');
        }, false)
        //上面的先弹出 son 在弹出 father 因为处于冒泡阶段
    </script>
</body>

</html>

实际开发中我们很少使用事件捕获 我们更关注事件冒泡

有些事件是没有冒泡的 比如 onblur onfocus onmouseover  onmouseleave

事件冒泡有时候会带来麻烦 有时候又会很有用 

事件对象

<!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>
    <div>123</div>
    <script>
        var div = document.querySelector('div');
        // event 就是一个事件对象 写到我们监听函数的小括号里面 当形参来看
        // 事件对象只有有了事件才会存在
        // 他是系统给我们自动创建的 不需要我们传递参数
        // 事件对象 使我们时间的一系列相关数据的结合 跟事件相关的 比如说鼠标点击里面就包含了鼠标的相关信息 鼠标坐标什么的 如果是键盘事件里面就包含了键盘事件的信息 比如用户按下了哪个键
        div.onclick = function (event) {
            console.log(event);
            //可以点击自行查看 有很多信息
        }
        //事件对象也有兼容性问题 IE678通过window.event获取
        // IE678
        div.onclick = function (event) {
            console.log(window.event);
            //可以点击自行查看 有很多信息
        }
        //兼容性写法
        div.onclick = function (event) {
            event = event || window.event
        }

    </script>
</body>

</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>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div>123</div>
    <ul>
        <li>abc</li>
        <li>abc</li>
        <li>abc</li>
    </ul>
    <script>
        // 常见事件对象的属性和方法
        var div = document.querySelector('div');
        div.addEventListener('click', function (e) {
            console.log(e.target); //返回触发事件的元素
            console.log(this) //返回的是绑定事件的对象
        })
        var ul = document.querySelector('ul');
        //了解this 有个非常相似的属性 currentTarget 指定的是绑定事件的元素 IE678不兼容
        ul.addEventListener('click', function (e) {
            console.log(e.target);//li 返回的是触发事件的元素 
            console.log(this); //ul 绑定事件的元素
            console.log(e.currentTarget); //返回绑定事件的元素
        })
        //区别:e.target 点击了什么 返回什么 this 谁绑定的返回谁
        //e.target 也是有兼容性的
        //兼容性写法 只能这样
        div.onclick = function(e) {
            e = e || window.event;
            var target = e.target || e.srcElement;
            console.log(target)
        }
    </script>
</body>

</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>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div>123</div>
    <a href="http://www.baidu.com">百度</a>
    <form action="http://www.baidu.com">
        <input type="submit" value="提交" name="sub">
    </form>
    <script>
        var div = document.querySelector('div');
        div.addEventListener('click', function (e) {
            console.log(e.type); //click
        })
        //阻止默认行文 让链接不跳转 或者让提交按钮不提交
        var a = document.querySelector('a');
        a.addEventListener('click', function (e) {
            e.preventDefault();//dom标准写法 IE低版本不支持
        })
        //传统的注册方式 可以兼容IE低版本
        a.onclick = function (e) {
            // //普通浏览器
            // e.preventDefault();
            // //低版本浏览器
            // e.returnValue;
            //我们还可以利用return false 阻止默认行为 没有兼容性问题 但是return后面的代码不执行了 而且只限于传统注册方式
            return false
        }
    </script>
</body>

</html>

阻止事件冒泡 开始由最具体的接收元素 然后逐级向上传播到DOM最顶层节点。

<!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>
        .father {
            width: 200px;
            height: 200px;
            background-color: purple;
            overflow: hidden;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: pink;
            margin: 50px;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        // dom 事件流三个阶段
        // 1.js代码中只能执行捕获或者冒泡其中的一个阶段
        // 2.onclick 和 attachEvent(IE) 只能得到冒泡
        var son = document.querySelector('.son')
        // 第三个参数:true 处于捕获阶段 false 处于冒泡阶段
        //body->father->son

        //上面的可以看到 先弹出了father 后弹出了son  因为是时间处于捕获阶段 所以先触发了父亲
        son.addEventListener('click', function (e) {
            alert('son');
            // //阻止事件冒泡
            // e.stopPropagation(); //不会再执行father的方法
            // //低版本的浏览器 是这样的
            // e.cancelBubble;
            //兼容性写法
            if (e && e.stopImmediatePropagation) {
                e.stopPropagation();
            } else {
                window.event.cancelBubble;
            }
        }, false)
        var father = document.querySelector('.father');
        father.addEventListener('click', function () {
            alert('father');
        }, false)
    </script>
</body>

</html>

时间委托(代理,委派)

事件冒泡本身的特性,会带来坏处 也会带来好处 需要我们灵活掌握

事件委托也称为事件代理 在JQuery里面成为事件委派

事件委托的原理:

不是每个子节点单独设置事件监听器 而是事件监听器设置在其父节点上 然后利用冒泡原理影响设置每个子节点

例如所有的li都有一个共同的事件 我们不能每个li都给一个监听事件 我们可以利用冒泡原理给ul一个事件,来完成这个需求。

事件委托的作用

按照上述例子 我们只操作了一次DOM 提高了程序的性能。

<!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>
    <ul>
        <li>知否知否</li>
        <li>知否知否</li>
        <li>知否知否</li>
        <li>知否知否</li>
        <li>知否知否</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function (e) {
            alert('知否知否');
            e.target.style.backgroundColor = 'pink';
        })
    </script>
</body>

</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>
    我是一段不愿意被分享的文字
    <script>
        // 禁止鼠标右键菜单
        // contentmenu主要控制应该何时显示上下文菜单 主要用于城御园取消默认的上下文菜单
        // contextmenu 禁止使用右键菜单
        document.addEventListener('contextmenu', function (e) {
            e.preventDefault(); //阻止默认行为
        })
        // 禁止选中 selectstart;
        document.addEventListener('selectstart', function (e) {
            e.preventDefault();
        })
    </script>
</body>

</html>

event对象代表事件的状态 跟事件相关的一系列信息的集合 现阶段我们主要是用鼠标事件对象

MouseEvent和键盘事件对象keyboardEvent

<!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>
        img {
            position: absolute;
            top: 2px;
        }
    </style>
</head>

<body>
    <img src="../img/angel.gif" alt="">
    <script>
        var img = document.querySelector('img');
        document.addEventListener('mousemove', function (e) {
            var x = e.pageX;
            var y = e.pageY;
            img.style.top = y - 40 + 'px';
            img.style.left = x - 50 + 'px';
        })
    </script>
</body>

</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>
    <script>
        // keyup 按键弹起时触发
        document.onkeyup = function () {
            alert('我叹气了');
        }
        // keydown 按键按下的时候触发 按着键不松一直触发
        document.onkeydown = function () {
            console.log('按下了')
        }
        // keypress 按键按下的时候触发
        document.onkeypress = function () {
            console.log('按下了')
        }
        // keypress 不识别功能键 ctrl  shift 左右箭头等
        // keydown  什么都能够识别 包括功能键
    </script>
</body>

</html>

键盘事件对象

keyup和keydown 事件不区分字母大小写 e.keyCode 得到按键对应的ASCII码

keypress 区分大小写.

原文地址:https://www.cnblogs.com/huanying2000/p/12748251.html