JavaScript Day11 DOM事件机制

事件机制

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

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

<div id='div1'>我是一个divdiv>
<button id='btn'>点我啊button>

<button id='btn2' onclick="clickFun()">点我啊2button>
<script>
  // 点击按钮,改变div的内容,大小,背景颜色
  // 分别获取div和button的节点
  var div = document.getElementById('div1');
  var btn = document.getElementById('btn');
  // 如需向 HTML 元素分配事件,您能够使用事件属性。给按钮节点绑定点击事件
  btn.onclick = function () {
    div.innerHTML = '点击按钮改变的';
    div.style.width = '200px';
    div.style.height = '200px';
    div.style.backgroundColor = 'red';
  }
  // 执行在btn2中绑定的事件
  function clickFun() {
    console.log('我是btn2绑定的事件');
  }
script>

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

事件是由三部分组成

事件源 事件类型 事件处理程序 我们也称为事件三要素
1.事件源:事件被触发的对象 -->按钮对象
2.事件类型:如何触发?触发什么事件?例如鼠标点击,键盘按下等…
3.事件处理程序:通过一个函数赋值的方式

执行事件的步骤

1.获取事件源
2.注册事件(绑定事件)
3.采用函数赋值形式添加事件处理程序

常用事件:
  • 当用户点击鼠标时
  • 当网页加载后
  • 当图像加载后
  • 当鼠标移至元素上时
  • 当输入字段被改变时
  • 当 HTML 表单被提交时
  • 当用户敲击按键时
  • 。。。

1.事件流

​ 在第四代 Web 浏览器(IE4 和 Netscape Communicator 4)开始开发时,开发团队碰到了一个有意思的问题:页面哪个部分拥有特定的事件呢?要理解这个问题,可以在一张纸上画几个同心圆。把手指放到圆心上,则手指不仅是在一个圆圈里,而且是在所有的圆圈里。两家浏览器的开发团队都是以同样的方式看待浏览器事件的。当你点击一个按钮时,实际上不光点击了这个按钮,还点击了它的容器以及整个页面。

​ 事件流描述了页面接收事件的顺序。结果非常有意思,IE 和 Netscape 开发团队提出了几乎完全相反的事件流方案。IE 将支持事件冒泡流,而 Netscape Communicator 将支持事件捕获流。

1.1.事件冒泡(IE事件流)

IE 事件流被称为事件冒泡,这是因为事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。

例如:

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Documenttitle>
  <style>
    *{
      color: white;
      font-size: 20px;
    }
    #outer{
      width: 300px;
      height: 300px;
      background-color: red;
    }
    #center{
      width: 200px;
      height: 200px;
      background-color: blue;
    }
    #inner{
      width: 100px;
      height: 100px;
      background-color: green;
    }
  style>
head>
<body>
  <div id="outer">outer
    <div id="center">center
      <div id="inner">innerdiv>
    div>
  div>
  <script>
    var inner = document.getElementById('inner');
    var center = document.getElementById('center');
    var outer = document.getElementById('outer');
    // 当我们只有一个inner点击方法的时候 我们发现想要实现的效果和我们预期的一样
    inner.onclick = function () {
      console.log('我是inner点击的');
    }
    // 但是当我们给inner的父元素和祖先元素也添加点击事件时 一点击inner 所有祖先元素的事件都会被触发,这就是事件冒泡现象
    center.onclick = function () {
      console.log('我是center点击的');
    }
    outer.onclick = function () {
      console.log('我是outer点击的');
    }
  script>
body>
html>

在点击页面中的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 对象。

JavaScript Day11 DOM事件机制_第1张图片

所有现代浏览器都支持事件冒泡,只是在实现方式上会有一些变化。IE5.5及早期版本会跳过html元素(从body直接到 document)。现代浏览器中的事件会一直冒泡到 window 对象。

阻止事件冒泡

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

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

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

https://www.w3school.com.cn/htmldom/dom_obj_event.asp

event.stopPropagation()

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

注意:如果点击方法时需要同时传递其他参数和event,直接传递event这个单词即可
οnclick="test(123,event)"

1.2.事件捕获(Netscape事件流)

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

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

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

JavaScript Day11 DOM事件机制_第2张图片

由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情况下可以使用事件捕获。

1.3.DOM事件流

​ DOM2 Events 规范规定事件流分为 3 个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个阶段响应事件。

​ 点击div元素会以如图所示的顺序触发事件。
JavaScript Day11 DOM事件机制_第3张图片

在 DOM 事件流中,实际的目标(div元素)在捕获阶段不会接收到事件。这是因为捕获阶段从document 到html再到body就结束了。下一阶段,即会在div元素上触发事件的“到达目标”阶段,通常在事件处理时被认为是冒泡阶段的一部分。然后,冒泡阶段开始,事件反向传播至文档。大多数支持 DOM 事件流的浏览器实现了一个小小的拓展。虽然 DOM2 Events 规范明确捕获阶段不命中事件目标,但现代浏览器都会在捕获阶段在事件目标上触发事件。最终结果是在事件目标上有两个机会来处理事件。

注意:所有现代浏览器都支持 DOM 事件流,只有 IE8 及更早版本不支持。

2.事件处理程序

事件意味着用户或浏览器执行的某种动作。比如,单击(click)、加载(load)、鼠标悬停(mouseover)。为响应事件而调用的函数被称为事件处理程序(或事件监听器)。事件处理程序的名字以"on"开头,因此 click 事件的处理程序叫作 onclick,而 load 事件的处理程序叫作 onload。有很多方式可以指定事件处理程序。

2.1.HTML 事件处理程序

​ 特定元素支持的每个事件都可以使用事件处理程序的名字以 HTML 属性的形式来指定。此时属性的值必须是能够执行的 JavaScript 代码。例如,要在按钮被点击时执行某些 JavaScript 代码,可以使用以下 HTML 属性:

<button onclick="console.log('Clicked')">点我啊button>

点击这个按钮后,控制台会输出一条消息。这种交互能力是通过为 onclick 属性指定 JavaScript代码值来实现的。注意,因为属性的值是 JavaScript 代码,所以不能在未经转义的情况下使用 HTML 语法字符,比如和号(&)、双引号(")、小于号(<)和大于号(>)。此时,为了避免使用 HTML 实体,可以使用单引号代替双引号。如果确实需要使用双引号,则要把代码改成下面这样:

<button onclick="console.log(&quot;Clicked&quot;)">点我啊button>

在 HTML 中定义的事件处理程序可以包含精确的动作指令,也可以调用在页面其他地方定义的方法。


<button onclick="showMsg()">点我啊button>
<script>
  function showMsg() {
    console.log('Hello Wolrd!');
  }
script>

在这个函数中,this 值相当于事件的目标元素,如下面的例子所示:

<button onclick="this.innerHTML = '我被改变了'">点我啊button>

2.2.DOM0 事件处理程序

​ 在 JavaScript 中指定事件处理程序的传统方式是把一个函数赋值给(DOM 元素的)一个事件处理程序属性。这也是在第四代 Web 浏览器中开始支持的事件处理程序赋值方法,直到现在所有现代浏览器仍然都支持此方法,主要原因是简单。要使用 JavaScript 指定事件处理程序,必须先取得要操作对象的引用。每个元素(包括 window 和 document)都有通常小写的事件处理程序属性,比如 onclick。只要把这个属性赋值为一个函数即可:

<button id='btn'>点我啊button>
<script>
  var btn = document.getElementById('btn');
  btn.onclick = function () {
    console.log('我被点击了');
  }
script>

​ 这里先从文档中取得按钮,然后给它的 onclick 事件处理程序赋值一个函数。注意,前面的代码在运行之后才会给事件处理程序赋值。因此如果在页面中上面的代码出现在按钮之后,则有可能出现用户点击按钮没有反应的情况。

​ 像这样使用 DOM0 方式为事件处理程序赋值时,所赋函数被视为元素的方法。因此,事件处理程序会在元素的作用域中运行,即 this 等于元素。下面的例子演示了使用 this 引用元素本身:

<button id='btn'>点我啊button>
<script>
  var btn = document.getElementById('btn');
  btn.onclick = function () {
    console.log('我被点击了');
    console.log(this.id); //btn
  }
script>

​ 点击按钮,这段代码会显示元素的 ID。这个 ID 是通过 this.id 获取的。不仅仅是 id,在事件处理程序里通过 this 可以访问元素的任何属性和方法。以这种方式添加事件处理程序是注册在事件流的冒泡阶段的。

通过将事件处理程序属性的值设置为 null,可以移除通过 DOM0 方式添加的事件处理程序,如下面的例子所示:

btn.onclick = null; // 移除事件处理程序

把事件处理程序设置为 null,再点击按钮就不会执行任何操作了。

注意:

如果事件处理程序是在 HTML 中指定的,则 onclick 属性的值是一个包装相应HTML 事件处理程序属性值的函数。这些事件处理程序也可以通过在 JavaScript 中将相应属性设置为 null 来移除。

2.3.DOM2 事件处理程序

DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:

addEventListener()和removeEventListener()。

这两个方法暴露在所有 DOM 节点上,它们接收 3 个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序。

addEventListener()

仍以给按钮添加 click 事件处理程序为例,可以这样写:

<button id='btn'>点我啊button>
<script>
  var btn = document.getElementById("btn");
  btn.addEventListener("click", function () {
    console.log('我被点击了');
  }, false);
script>

以上代码为按钮添加了会在事件冒泡阶段触发的 onclick 事件处理程序(因为最后一个参数值为false)。与 DOM0 方式类似,这个事件处理程序同样在被附加到的元素的作用域中运行。使用 DOM2方式的主要优势是可以为同一个事件添加多个事件处理程序。来看下面的例子:

<button id='btn'>点我啊button>
<script>
  var btn = document.getElementById("btn");
  btn.addEventListener("click", function () {
    console.log('我被点击了');
  }, false);
  btn.addEventListener("click", function () {
    console.log(this.id); // btn
  });
script>

这里给按钮添加了两个事件处理程序。多个事件处理程序以添加顺序来触发,因此前面的代码会先打印’我被点击了’,然后显示元素ID。

removeEventListener()

通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添加时同样的参数来移除。这意味着使用 addEventListener()添加的匿名函数无法移除,如下面的例子所示:

<button id='btn'>点我啊button>
<script>
  var btn = document.getElementById("btn");
  btn.addEventListener("click", function () {
    console.log('我被点击了');
  }, false);
  // 移除
  btn.removeEventListener("click", function () {
    console.log(this.id); //没有效果
  })
script>

这个例子通过 addEventListener()添加了一个匿名函数作为事件处理程序。然后,又以看起来相同的参数调用了 removeEventListener()。但实际上,第二个参数与传给 addEventListener()的完全不是一回事。传给 removeEventListener()的事件处理函数必须与传给 addEventListener()的是同一个,如下面的例子所示:

<button id='btn'>点我啊button>
<script>
  var btn = document.getElementById("btn");
  // 新增一个方法
  var handler = function () {
    console.log(this.id);
  }
  btn.addEventListener("click", handler, false);
  // 移除
  btn.removeEventListener("click", handler, false); // 有效果
script>

这个例子有效,因为调用 addEventListener()和 removeEventListener()时传入的是同一个函数。大多数情况下,事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好。把事件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件。如果不需要拦截,则不要使用事件捕获。

3.事件对象

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

详见:https://www.w3school.com.cn/htmldom/dom_obj_event.asp

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

3.1.阻止默认事件发生

preventDefault()方法

用于阻止特定事件的默认动作。比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL或是修改表单提交的默认事件。如果想阻止这些行为,可以在 onclick 事件处理程序中取消,如下面的例子所示:

<a href="http://www.baidu.com">跳转a>
<form action="./1-HTML事件处理程序.html">
  <button id="btn">提交按钮button>
form>
<script>
  var a = document.getElementsByTagName('a')[0];
  var btn = document.getElementById('btn');
  a.onclick = function (event) {
    // alert(1);
    // 阻止事件默认行为
    event.preventDefault();
  }
  btn.onclick = function (event) {
    // 阻止事件默认行为
    event.preventDefault();
  }++++++++++
script>

4.事件委托

在 JavaScript 中,页面中事件处理程序的数量与页面整体性能直接相关。原因有很多。首先,每个函数都是对象,都占用内存空间,对象越多,性能越差。其次,为指定事件处理程序所需访问 DOM 的次数会先期造成整个页面交互的延迟。只要在使用事件处理程序时多注意一些方法,就可以改善页面性能。

事件委托就是当事件触发时,把要做的事委托给父元素(或父元素的父元素)来处理。也就是:利用冒泡的原理,把事件加到父级上,通过判断事件来源的子集,执行相应的操作。使用事件委托技术能让你避免对特定的每个节点添加事件监听器。

事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。例如,click 事件冒泡到 document。这意味着可以为整个页面指定一个 onclick 事件处理程序,而不用为每个可点击元素分别指定事件处理程序。比如有以下HTML:

<ul id="myLinks">
  <li id="li1">Go somewhereli>
  <li id="li2">Do somethingli>
  <li id="li3">Say hili>
ul>

这里的 HTML 包含 3 个列表项,在被点击时应该执行某个操作。对此,通常的做法是像这样指定 3个事件处理程序:


如果对页面中所有需要使用 onclick 事件处理程序的元素都如法炮制,结果就会出现大片雷同的只为指定事件处理程序的代码。使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题。

比如:


这里只给

扩展:

只要可行,就应该考虑只给 document 添加一个事件处理程序,通过它处理页面中所有某种类型的事件。相对于之前的技术,事件委托具有如下优点。

  • document 对象随时可用,任何时候都可以给它添加事件处理程序(不用等待 DOMContentLoaded或 load 事件)。这意味着只要页面渲染出可点击的元素,就可以无延迟地起作用。

  • 节省花在设置页面事件处理程序上的时间。只指定一个事件处理程序既可以节省 DOM 引用,也可以节省时间。

  • 减少整个页面所需的内存,提升整体性能。

    最适合使用事件委托的事件包括:click、mousedown、mouseup、keydown 和 keypress。

5.事件类型

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

用户界面事件(UIEvent):涉及与 BOM 交互的通用浏览器事件。

焦点事件(FocusEvent):在元素获得和失去焦点时触发。

鼠标事件(MouseEvent):使用鼠标在页面上执行某些操作时触发。

滚轮事件(WheelEvent):使用鼠标滚轮(或类似设备)时触发。

键盘事件(KeyboardEvent):使用键盘在页面上执行某些操作时触发。

输入事件(InputEvent):向文档中输入文本时触发。

https://developer.mozilla.org/zh-CN/docs/Web/API/Event

5.1.用户界面事件-UIEvent

  • load

    在 window 上当页面加载完成后触发,在窗套()上当所有窗格()都加载完成后触发,在元素上当图片加载完成后触发,在元素上当相应对象加载完成后触发。

    
    
  • unload

    当页面完全卸载后在window上触发,当所有框架都卸载后在框架集上触发,当嵌入的内容卸载完毕后在上触发。

  • select

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

    
    
    
  • resize

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

    
      
    
    
  • scroll

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

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

    我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签

5.2.焦点事件-FocusEvent

  • blur

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

  • focus

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

    
    
    
  • focusin

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

  • focusout

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

5.3.鼠标事件-MouseEvent和鼠标滚轮事件-WheelEvent

鼠标事件是 Web 开发中最常用的一组事件,这是因为鼠标是用户的主要定位设备。DOM3 Events定义了 9 种鼠标事件。

  • 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

    鼠标滚轮事件

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Documenttitle>
  <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>
html>

5.4.键盘事件-KeyboardEvent与输入事件-InputEvent

​ 键盘事件是用户操作键盘时触发的。DOM2 Events 最初定义了键盘事件,但该规范在最终发布前删除了相应内容。因此,键盘事件很大程度上是基于原始的 DOM0 实现的。

​ DOM3 Events 为键盘事件提供了一个首先在 IE9 中完全实现的规范。其他浏览器也开始实现该规范,但仍然存在很多遗留的实现。

键盘事件包含 3 个事件:

  • keydown,用户按下键盘上某个键时触发,而且持续按住会重复触发。

  • keypress,用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。Esc 键也会触发这个事件。DOM3 Events 废弃了 keypress 事件,而推荐 textInput 事件。

  • keyup,用户释放键盘上某个键时触发。

虽然所有元素都支持这些事件,但当用户在文本框中输入内容时最容易看到。

输入事件只有一个,即 textInput。这个事件是对 keypress 事件的扩展,用于在文本显示给用户之前更方便地截获文本输入。textInput 会在文本被插入到文本框之前触发。当用户按下键盘上的某个字符键时,首先会触发 keydown 事件,然后触发 keypress 事件,最后触发 keyup 事件。注意,这里 keydown 和 keypress 事件会在文本框出现变化之前触发,而 keyup事件会在文本框出现变化之后触发。如果一个字符键被按住不放,keydown 和 keypress 就会重复触发,直到这个键被释放。对于非字符键,在键盘上按一下这个键,会先触发 keydown 事件,然后触发 keyup 事件。如果按住某个非字符键不放,则会重复触发 keydown 事件,直到这个键被释放,此时会触发 keyup 事件。

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Documenttitle>
head>
<body>
  <input type="text" id="inp1">
  <script>
    var inp1 = document.getElementById('inp1');
    // 按下某个按键是触发
    inp1.onkeydown = function (event) {
      if (event.keyCode == 13) {
        console.log('确认');
        console.log(this.value);
      }
    }
    // 按下键盘上某个键并产生字符时触发,而且持续按住会重复触发
    inp1.onkeypress = function (event) {
      console.log(event.keyCode);
    }
    // 用户释放键盘上某个键时触发
    inp1.onkeyup = function (event) {
      console.log(event.keyCode);
    }
    // 键盘输入事件 必须使用DOM2级事件
    inp1.addEventListener('textInput', function (event) {
      console.log(event.data);
    })
  script>
body>
html>

你可能感兴趣的:(JavaScript基础详解,javascript,前端,es6,java基础详解,vue.js)