likes
comments
collection
share

掌握前端事件处理:全面了解 DOM 事件机制

作者站长头像
站长
· 阅读数 10

概述

HTML DOM 允许 JavaScript 对 HTML 事件作出反应。JavaScript 能够在事件发生时执行,比如当用户点击某个 HTML 元素时。

为了在用户点击元素时执行代码,请向 HTML 事件属性添加 JavaScript 代码:

<head>
    <script>
        window.onload = function(){
            /**
             * 1.获取dom元素 
             * 2.给dom元素绑定事件
             * 3.声明事件处理程序
            */
            // 分别获取div和button的节点
            var div = document.querySelector('div');
            var button = document.querySelector('button');
            // 如需向 HTML 元素分配事件,您能够使用事件属性。给按钮节点绑定点击事件
            button.onclick = function(){
                div.style.width = '100px';
                div.style.height = '100px';
                div.style.backgroundColor = 'pink';
                div.style.fontSize = '22px';
                div.innerText = 'hello dom';
            }
        }
    </script>
</head>
<body>
    <div>我是div</div>
    <button>点击我</button>
</body>

JavaScript与HTML之间的交互是通过事件实现的。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。

事件流

事件冒泡

<head>
    <style>
        .outer{
            width: 300px;
            height: 300px;
            background-color: red;
            margin: 0 auto;
            padding: 50px;
            box-sizing: border-box;
        }
        .center{
            width: 200px;
            height: 200px;
            background-color: pink;
            padding: 50px;
            box-sizing: border-box;
        }
        .inner{
            width: 100px;
            height: 100px;
            background-image: linear-gradient(to right,red,pink,blue);
        }
    </style>
    <script>
        window.onload = function(){
            /**
             * 给dom元素绑定事件
             * 1.获取dom元素
             * 2.给dom元素绑定事件
            */
           var outer = document.querySelector('.outer');
           var center = document.querySelector('.center');
           var inner = document.querySelector('.inner');
           // 当我们只有一个inner点击方法的时候 我们发现想要实现的效果和我们预期的一样
           inner.onclick = function(){
            alert('我是inner');
           }
           // 但是当我们给inner的父元素和祖先元素也添加点击事件时 一点击inner 所有祖先元素的事件都会被触发,这就是事件冒泡现象
           outer.onclick = function(){
            alert('我是outer');
           }
           center.onclick = function(){
            alert('我是center');
           }
        }
    </script>
</head>
<body>
     <div class="outer">
        <div class="center">
            <div class="inner"></div>
        </div>
     </div>
</body>

在点击页面中的id为inner的div元素,click事件会以如下顺序发生

  1. div#inner
  2. div#center
  3. div#outer
  4. body
  5. html
  6. document

也就是说,div#inner元素,即被点击的元素,最先触发 click 事件。然后,click 事件沿DOM 树一路向上,在经过的每个节点上依次触发,直至到达 document 对象。

掌握前端事件处理:全面了解 DOM 事件机制

阻止事件冒泡

使用阻止事件冒泡之前,先要知道DOM事件默认提供的一个对象,HTML DOM Event对象。

Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。

事件通常与函数结合使用,函数不会在事件发生前被执行!

event.stopPropagation()

直接在对应方法中使用event.stopPropagation()便可阻止事件冒泡。

<head>
    <style>
        .outer{
            width: 300px;
            height: 300px;
            background-color: red;
            margin: 0 auto;
            padding: 50px;
            box-sizing: border-box;
        }
        .center{
            width: 200px;
            height: 200px;
            background-color: pink;
            padding: 50px;
            box-sizing: border-box;
        }
        .inner{
            width: 100px;
            height: 100px;
            background-image: linear-gradient(to right,red,pink,blue);
        }
    </style>
    <script>
        window.onload = function(){
            /**
             * 给dom元素绑定事件
             * 1.获取dom元素
             * 2.给dom元素绑定事件
            */
           var outer = document.querySelector('.outer');
           var center = document.querySelector('.center');
           var inner = document.querySelector('.inner');
           outer.onclick = function(){
            alert('我是outer');
           }
           center.onclick = function(){
            alert('我是center');
            event.stopPropagation();
           }
           inner.onclick = function(){
            alert('我是inner');
            //阻止事件冒泡 event 表示当前事件对象
            //console.log(event,'事件对象');
            event.stopPropagation();
           }
        }
    </script>
</head>
<body>
     <div class="outer">
        <div class="center">
            <div class="inner"></div>
        </div>
     </div>
</body>

此时点击页面中的id为inner的div元素时,不会触发其祖先元素事件

事件捕获

事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。如果前面的例子使用事件捕获,则点击div元素会以下列顺序触发 click 事件:

  1. document
  2. html
  3. body
  4. div

在事件捕获中,click 事件首先由 document 元素捕获,然后沿 DOM 树依次向下传播,直至到达实际的目标元素div。

掌握前端事件处理:全面了解 DOM 事件机制
<head>
    <style>
        .outer{
            width: 300px;
            height: 300px;
            background-image: radial-gradient(red,pink,cyan);
            margin: 0 auto;
            padding: 50px;
            box-sizing: border-box;
        }
        .center{
            width: 200px;
            height: 200px;
            background-color: red;
            padding: 50px;
            box-sizing: border-box;
        }
        .inner{
            width: 100px;
            height: 100px;
            background-color: blue;
        }
    </style>
    <script>
        window.onload = function(){
            var outer = document.querySelector('.outer');
            var center = document.querySelector('.center');
            var inner = document.querySelector('.inner');
            function handler1(){
                console.log('outer');
            }
            function handler2(){
                console.log('center');
            }
            function handler3(){
                console.log('inner');
            }
            /**
             * addEventListener(事件类型,事件处理程序,false/true 默认是false)
             * false 表示事件在冒泡阶段执行
             * true 表示事件在捕获阶段执行
            */
            outer.addEventListener('click',handler1,true);
            center.addEventListener('click',handler2,true);
            inner.addEventListener('click',handler3,true);

        }
    </script>
</head>
<body>
    <div class="outer">
        <div class="center">
            <div class="inner"></div>
        </div>
    </div>
</body>

事件流

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

分为事件冒泡和事件捕获

事件冒泡:事件从内向外触发

事件捕获:事件从外向内触发

事件流机制:事件捕获--到达目标--事件冒泡

事件处理程序

DOM0事件处理程序

dom0级事件绑定

on事件类型

不可以追加同类事件

只执行最后一次的事件处理程序

<head>
    <script>
        window.onload = function(){
            var btn = document.querySelector('button');
            btn.onclick = function(){
                console.log(this);
            }
            //追加事件
            btn.onclick = function(){
                alert('第二次事件');
            }
        }
    </script>
</head>
<body>
    <button>点击我</button>
</body>

追加事件会直接覆盖上一次事件,此时只执行追加事件

解绑

btn.onclick = null;

通过将事件处理程序属性的值设置为 null,可以移除通过 DOM0 方式添加的事件处理程序

DOM2事件处理程序

addEventListener('事件类型','事件处理程序',true/false)

true 表示执行事件捕获 false表示执行事件冒泡

可以追加同类事件

<head>
    <script>
        window.onload = function(){
            var btn = document.querySelector('button');
            function handler1(){
                console.log(this);
            }
            function handler2(){
                alert('第二次事件');
            }
            btn.addEventListener('click',handler1);
            btn.addEventListener('click',handler2);
        }
    </script>
</head>
<body>
    <button>点击我</button>
</body>

此时可以实现追加事件,两个事件按顺序执行

解绑

btn.removeEventListener('click',handler1);

removeEventListener('事件类型',具名函数事件处理程序)

通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添加时同样的参数来移除

DOM0和DOM2事件区别

1.dom0级事件绑定使用on关键字绑定 解绑on事件类型设置为null

2.dom2级事件绑定使用addEventLisrener 解绑使用removeEventListener

3.dom0级事件不可追加同类事件 dom2级可以追加同类事件

事件对象

在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。这个对象包含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信息。

event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。

阻止默认事件发生

preventDefault()方法

<head>
    <script>
        window.onload = function(){
            var input = document.querySelector('input');
            var a = document.querySelector('a');
            input.onclick = function(){
                //阻止input标签默认提交行为
                event.preventDefault();
            }
            a.onclick = function(){
                //阻止a标签默认跳转行为
                event.preventDefault();
            }
        }
    </script>
</head>
<body>
    <form action="text.php">
        <input type="submit" value="提交">
    </form>    
    <a href="https:///www.baidu.com">跳转到百度</a>
</body>
</html>

事件委托(事件代理)

利用事件冒泡 只指定一个事件处理程序 就可以管理某一类型的事件

将本应该绑定给子元素事件绑定给父元素

目的:优化页面性能减少事件的执行 减少浏览器的重排(回流)和重绘 给新增的元素绑定事件

案例一

<head>
    <script>
        window.onload = function(){
            var ul = document.querySelector('ul');

            //给所有li绑定事件 点击时背景色变成红色
            var lis = document.querySelectorAll('li');
            console.log(lis);
            for(var i = 0;i<lis.length;i++){
                lis[i].onclick = function(){
                    this.style.backgroundColor = 'red'
                }
            }
        }
    </script>
</head>
<body>
     <ul>
        <li>第1个li</li>
        <li>第2个li</li>
        <li>第3个li</li>
        <li>第4个li</li>
        <li>第5个li</li>
        <li>第6个li</li>
        <li>第7个li</li>
        <li>第8个li</li>
        <li>第9个li</li>
        <li>第10个li</li>
     </ul>
</body>

通常的做法是使用for循环遍历到每个li,添加点击事件,同时此方法对新增li不起作用。每个函数都是对象,都占用内存空间,对象越多,性能越差。其次,为指定事件处理程序所需访问 DOM 的次数会先期造成整个页面交互的延迟。

而使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题。

var newLi = document.createElement('li');
newLi.innerText = '第11个li';
ul.appendChild(newLi);
/**
 * 事件委托 将子元素事件代理给父元素 只绑定一次事件 管理一类事件
 */
ul.onclick = function(){
    //event 事件对象 event.terget
    event.target.style.backgroundColor = 'red';
}

案例二

<head>
    <script>
        window.onload = function(){
            var ul = document.querySelector('ul');
            //点击不同li内部内容发生不同的改变
            var item1 = document.querySelector('#first');
            var item2 = document.querySelector('#second');
            var item3 = document.querySelector('#three');

            // 原始方法
            // item1.onclick = function(){
            //     this.innerText = 'hello html';

            // }
            // item2.onclick = function(){
            //     this.innerText = 'hello css';
            // }
            // item3.onclick = function(){
            //     this.innerText = 'hello js';
            // }

            //事件委托
            ul.onclick = function(){
                //当前触发事件的目标元素
                var target = event.target;
                switch(target.id){
                    case 'first':
                        target.innerText = 'hello html';
                        break;
                    case 'second':
                        target.innerText = 'hello css';
                        break;
                    case 'three':
                        target.innerText = 'hello js';
                        break;
                    default:
                        target.innerText = '我是li标签';
                }
            }
        }
    </script>
</head>
<body>
    <ul>
        <li id="first">li标签</li>
        <li id="second">li标签</li>
        <li id="three">li标签</li>
    </ul>
</body>

事件类型

Web 浏览器中可以发生很多种事件。如前所述,所发生事件的类型决定了事件对象中会保存什么信息。DOM3 Events 定义了如下事件类型。

用户界面事件UIEvent

涉及与 BOM 交互的通用浏览器事件。

load

在 window 上当页面加载完成后触发。

<script>
  window.onload = function () {
    console.log('onload');
  }
</script>

unload

当页面完全卸载后在window上触发。

select

在文本框上当用户选择了一个或多个字符时触发。

<script>
        window.onload = function(){
            var input = document.querySelector('input');
            /**
             * 绑定选择事件 在输入框选择字符时候就触发
            */
            input.onselect = function(){
              console.log('我被选择了');
              // 可以通过window.getSelection()获取到选中的部分
              console.log(window.getSelection().toString()); //谷歌中支持
            }
        }
</script>
<input type="text">

resize

在 window 或窗格上当窗口或窗格被缩放时触发。

<script>
    document.body.onresize = function(){
        console.log(window.outerWidth,window.outerHeight);
    }
</script>

scroll

当用户滚动包含滚动条的元素时在元素上触发。body元素包含已加载页面的滚动条。

大多数 HTML 事件与 window 对象和表单控件有关。

<style>
    div{
            width: 100px;
            height: 100px;
            border: 1px solid royalblue;
            overflow: auto;
        }
</style>
<script>
    var div = document.querySelector('div');
    div.onscroll = function(){
        console.log('我在滚动');
    }
</script>
<body>
    <div>我是一个div我是一个div我是一个div我是一个div我是一个div我是一个div
        我是一个div我是一个div我是一个div我是一个div我是一个div我是一个div
        我是一个div我是一个div我是一个div我是一个div我是一个div我是一个div
    </div>
</body>

焦点事件FocusEvent

在元素获得和失去焦点时触发。

blur

当元素失去焦点时触发。这个事件不冒泡,所有浏览器都支持。

focus

当元素获得焦点时触发。这个事件不冒泡,所有浏览器都支持。

<input type="text" id="inp1">
<script>
  var inp1 = document.getElementById('inp1');
  // 失去焦点触发
  inp1.onblur = function () {
    console.log('失去焦点');
    console.log(this.value);
  }
  // 获得焦点触发
  inp1.onfocus = function () {
    console.log('获得焦点');
  }
</script>

focusin

当元素获得焦点时触发。这个事件是 focus 的冒泡版。

focusout

当元素失去焦点时触发。这个事件是 blur 的冒泡版。

鼠标MouseEvent和滚轮WheelEvent事件

使用鼠标在页面上执行某些操作时触发/使用鼠标滚轮(或类似设备)时触发。

click

在用户单击鼠标主键(通常是左键)或按键盘回车键时触发。这主要是基于无障碍的考虑,让键盘和鼠标都可以触发 onclick 事件处理程序。

dblclick

在用户双击鼠标主键(通常是左键)时触发。这个事件不是在 DOM2 Events 中定义的,但得到了很好的支持,DOM3 Events 将其进行了标准化。

mousedown

在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。

mouseenter

在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseenter 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events中新增的事件。

mouseleave

在用户把鼠标光标从元素内部移到元素外部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseleave 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events中新增的事件。

mousemove

在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。

mouseout

在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发。

mouseover

在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发。

mouseup

在用户释放鼠标键时触发。这个事件不能通过键盘触发。

mousewheel

鼠标滚轮事件

<head>
  <style>
    #d1{
      width: 300px;
      height: 300px;
      background-color: aqua;
    }
  </style>
</head>
<body>
  <div id="d1"></div>
  <script>
    var d1 = document.getElementById('d1');
    // 单击事件
    d1.onclick = function (event) {
      console.log('click');
      console.log(event);
    }
    // 双击事件
    d1.ondblclick = function () {
      console.log('dblclick');
    }
    // 鼠标移入事件
    d1.onmouseenter = function () {
      console.log('mouseenter');
    }
    // 鼠标移出事件
    d1.onmouseleave = function () {
      console.log('mouseleave');
    }
    // 鼠标在元素内部移动时触发
    d1.onmousemove = function () {
      console.log('mousemove');
    }
  </script>
</body>

键盘KeyboardEvent

使用键盘在页面上执行某些操作时/向文档中输入文本时触发。

键盘事件包含 3 个事件:

  • keydown,用户按下键盘上某个键时触发,而且持续按住会重复触发。
  • keypress,用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。Esc 键也会触发这个事件。DOM3 Events 废弃了 keypress 事件,而推荐 textInput 事件。
  • keyup,用户释放键盘上某个键时触发。
<head>
    <script>
        window.onload = function(){
            var input = document.querySelector('input');
            //键盘按下事件 keydown 按键 keyCode
            input.onkeydown = function(){
                console.log(event.keyCode);
            }
            //键盘抬起事件 keyup
            input.onkeyup = function(){
                console.log('我被抬起了');
            }
            //键盘持续按下事件 
            input.onkeypress = function(){
                console.log('持续按下');
            }
        }
    </script>
</head>
<body>
    <input type="text">
</body>

输入InputEvent事件

向文档中输入文本时触发。

这个事件是对 keypress 事件的扩展,用于在文本显示给用户之前更方便地截获文本输入。

<head>
    <script>
        window.onload = function(){
            var input = document.querySelector('input');
            //输入框输入事件 获取文本输入值
            input.addEventListener('textInput',function(){
                console.log(event.data);
            })
        }
    </script>
</head>
<body>
    <input type="text">
</body>