Skip to content

一文搞懂 js 事件绑定和 dom 操作

1.事件冒泡和捕获

1.js 事件处理流程

JavaScript 事件处理流程涉及以下几个主要步骤:

  1. 选择目标元素:首先,需要选择要绑定事件的目标元素。可以使用 DOM 方法(例如 getElementByIdquerySelector 等)或其他方式选择元素。
  2. 绑定事件处理函数:选择目标元素后,可以通过多种方式将事件处理函数绑定到目标元素上。
  3. 事件捕获:JavaScript 中的事件模型允许通过事件冒泡或事件捕获的方式处理事件。——> 事件流
  4. 事件触发:当用户执行与绑定事件相关的操作时(例如点击按钮、鼠标悬停、按下键盘等),浏览器会触发相应的事件。
  5. 事件冒泡:JavaScript 中的事件模型允许通过事件冒泡或事件捕获的方式处理事件。——> 事件流
  6. 执行事件处理函数:一旦事件触发,绑定的事件处理函数就会被执行。在执行事件处理函数时,事件对象(Event Object)会作为参数传递给事件处理函数,以提供有关事件的详细信息(例如事件类型、目标元素等)。
  7. 处理事件逻辑:在事件处理函数中,可以编写相应的代码来处理事件触发时的逻辑。这可能涉及更改元素样式、处理用户输入、发送请求等各种操作。

以上就是 JavaScript 事件处理流程的基本步骤。通过这个流程,可以实现丰富的交互体验和用户互动功能。

总结:捕获 ——> 目标元素事件触发(执行回调函数) ——> 冒泡 ——> 父元素事件触发(执行回调函数)

2.捕获和冒泡的区别

在 JavaScript 中,事件流(Event Flow)分为捕获阶段和冒泡阶段,这两者是事件处理的不同阶段,它们的触发顺序相反。

JavaScript 中的事件流遵循先捕获后冒泡的规则,称为 "事件捕获-冒泡" 模型。这意味着在一个事件发生时,会先触发捕获阶段的事件处理函数,然后再触发冒泡阶段的事件处理函数。

具体流程如下:

  1. 事件捕获阶段(Capture Phase):事件从最外层的祖先元素(通常是文档根元素)开始,依次向下传播到事件的目标元素。在捕获阶段,不会触发目标元素自身的事件处理函数。
  2. 目标元素自身的事件处理函数(Target Phase):事件到达目标元素时,会触发目标元素自身的事件处理函数。
  3. 事件冒泡阶段(Bubble Phase):事件从目标元素开始向上冒泡,依次触发目标元素的父元素的事件处理函数,一直传播到最外层的祖先元素。

默认情况下,JavaScript 中的事件处理函数会在冒泡阶段执行。可以通过 addEventListener() 方法的第三个参数来指定事件处理函数的触发阶段:true 表示捕获阶段,false(或不指定)表示冒泡阶段。

3.事件的阻止

事件可以被阻止或取消,以控制事件在 DOM 中的传播和默认行为的执行。在 JavaScript 中,可以通过以下方式来阻止事件的传播和默认行为的执行:

event.stopPropagation() 和 event.preventDefault()

  1. 阻止事件传播(Event Propagation):

    使用stopPropagation()方法可以阻止事件在 DOM 树中的传播,即停止事件的冒泡或捕获。

    js
    const button = document.getElementById("myButton");
    
    button.addEventListener("click", function (event) {
      console.log("Button clicked");
      event.stopPropagation(); // 阻止事件继续向上冒泡
    });
    
    document.body.addEventListener("click", function () {
      console.log("Body clicked");
    });

    在上面的例子中,点击按钮时,事件处理函数会输出Button clicked,但不会输出Body clicked,因为event.stopPropagation()阻止了事件的继续传播,即阻止了事件冒泡。

  2. 阻止默认行为(Default Action):

    使用preventDefault()方法可以阻止元素的默认行为,例如阻止链接的跳转、表单的提交等。

    html
    <a href="https://www.example.com" id="myLink">Click Me</a>
    js
    const link = document.getElementById("myLink");
    
    link.addEventListener("click", function (event) {
      event.preventDefault(); // 阻止链接的默认跳转行为
    });

    在上面的例子中,点击链接时,事件处理函数会阻止链接的默认跳转行为,从而不会跳转到指定的链接地址。

通过阻止事件的传播和默认行为,我们可以控制事件的处理方式,以实现更复杂的交互和用户体验。但是,需要小心使用阻止事件的功能,以确保不会影响到其他正常的页面行为。

image-20230823025618312

4.addEventListener 第三个参数

addEventListener 函数的第三个参数是一个布尔值,用于控制事件处理程序是在捕获阶段执行还是在冒泡阶段执行。这个参数叫做 useCapture,如果为 true,事件处理程序将在捕获阶段执行;如果为 false,事件处理程序将在冒泡阶段执行。

正确的形式是:

js
element.addEventListener(eventType, callback, useCapture);
  • eventType:要监听的事件类型,如 "click""keydown" 等。
  • callback:事件触发时要执行的函数。
  • useCapture:一个布尔值,表示事件处理程序是在捕获阶段执行(true)还是在冒泡阶段执行(false)。

例如,如果要在捕获阶段执行事件处理程序,可以这样使用:

js
element.addEventListener("click", myFunction, true);

如果要在冒泡阶段执行事件处理程序,可以这样使用:

js
element.addEventListener("click", myFunction, false);

注意:JavaScript 标准事件模型中,不能直接控制事件回调函数在冒泡阶段或者捕获阶段触发。事件的触发顺序是固定的,首先是捕获阶段,然后是目标阶段,最后是冒泡阶段。虽然可以使用 addEventListener 函数的第三个参数来控制事件处理程序在捕获阶段还是冒泡阶段执行,但不能自由切换它们的触发顺序。

一句话来说就是 js 可以控制事件回调的执行时机,但是不能控制触发时机,只要该触发了就立马触发!

2.事件委托

1.基本介绍

基本概念

事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。

顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父

元素,让父元素担当事件监听的职务。事件代理的原理是 DOM 元素的事件冒泡。

事件委托的好处:

这样做有几个好处:

  1. 减少内存占用:将事件处理程序绑定到父元素上,而不是每个子元素都绑定一个处理程序,可以节省内存
  2. 动态元素:如果您的子元素是通过 JavaScript 动态添加的,您不需要单独为每个元素绑定事件,只需绑定到父元素即可。——> 重点:可以让子元素动态添加
  3. 代码简洁性:事件委托可以减少代码重复,提高代码的可维护性。

2.事件委托例题:页面有 ul、n 个 li,点击 li 打印 li 的内容(querySelectorAll)

如果是绑定在 li 上面的:querySelectorAll方法

html
<body>
  <ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <!-- 添加更多的 <li> 元素 -->
  </ul>

  <script>
    const listItems = document.querySelectorAll("#list li"); //listItems是一个数组

    listItems.forEach((item) => {
      item.addEventListener("click", () => {
        console.log(item.textContent);
      });
    });
  </script>
</body>

如果是绑定在 ul 上面的:利用事件委托的机制

事件委托是一种在父元素上绑定事件处理程序,以处理其子元素上的事件的技术。

通过将事件处理程序绑定到父元素,您可以利用事件冒泡来监听子元素的事件。

html
<body>
  <ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <!-- 添加更多的 <li> 元素 -->
  </ul>

  <script>
    const list = document.getElementById("list");

    list.addEventListener("click", (event) => {
      if (event.target.tagName === "LI") {
        console.log(event.target.textContent);
      }
    });
  </script>
</body>

4.事件绑定

1.html 中事件绑定的所有方式

HTML 中可以使用以下几种方式来绑定事件处理程序:

  1. 直接在标签中添加事件属性
html
<button onclick="myFunction()">点击我</button>

直接通过内联事件处理程序(不推荐,已不常用):

html
<button onclick="console.log('Button clicked')">点击我</button>

2.通过 DOM 属性绑定

js
const button = document.getElementById("myButton");
button.onclick = function () {
  console.log("Button clicked");
};
  1. 使用addEventListener方法
js
const button = document.getElementById("myButton");
button.addEventListener("click", function () {
  console.log("Button clicked");
});
  1. 事件委托
js
const parentElement = document.getElementById("parent");
parentElement.addEventListener("click", function (event) {
  if (event.target.tagName === "BUTTON") {
    console.log("Button clicked");
  }
});

这些是一些常见的 HTML 事件绑定方式。需要注意的是,虽然您可以使用多种方式来绑定事件,但是推荐使用 addEventListener 方法,因为它是更灵活、更具可维护性的一种方式,可以在同一个元素上绑定多个事件处理程序,而且不会覆盖已有的处理程序。

2.js 事件绑定的 event 对象操作

在事件委托机制中,我们怎么分别获取当前绑定了事件的元素(被冒泡的),和真正点击的元素(绑定了事件的)呢?

JavaScript 中,您可以通过事件对象的属性来获取触发事件的元素(子元素)以及绑定事件的元素(父元素)。常用的事件对象属性包括 event.targetevent.currentTarget

  • event.target:表示触发事件的元素,即实际点击的元素。
  • event.currentTarget:表示绑定事件的元素,即当前事件处理程序所附加的元素。

注意:event.target.tagName 返回的标签名是大写的

当点击 <p> 元素时,控制台会输出 P,这是大写的标签名。同样地,如果您点击 <div> 元素,标签名会是 DIV

event.target 和 event.currentTarget 里面分别有哪些属性呢?

event.targetevent.currentTarget 都是事件对象中的属性,它们提供了有关事件触发的元素的信息。以下是它们的一些常见属性:

  1. event.target
    • target.nodeName:返回触发事件的元素的标签名(大写)。
    • target.id:返回触发事件的元素的 id 属性值。
    • target.className:返回触发事件的元素的 class 属性值。
    • target.value:适用于表单元素,返回输入框的值(例如,<input> 元素的输入值)。——> 常用
    • target.href:返回 <a> 元素的 href 属性值。
    • target.src:返回图片或其他资源的 src 属性值。
  2. event.currentTarget
    • currentTarget.nodeName:返回绑定事件处理程序的元素的标签名(大写)。
    • currentTarget.id:返回绑定事件处理程序的元素的 id 属性值。
    • currentTarget.className:返回绑定事件处理程序的元素的 class 属性值。

这些属性可以根据事件类型和触发的元素类型而有所不同。请注意,上述属性只是一些常见的属性,实际上事件对象还有许多其他属性,具体取决于事件类型和浏览器实现。

您可以使用以下方式来查看事件对象中的属性和信息:

js
element.addEventListener("click", function (event) {
  console.log("event.target:", event.target);
  console.log("event.currentTarget:", event.currentTarget);
});

在实际开发中,可以根据需要来使用这些属性,以便在事件处理程序中获取相关的元素信息。

3.js 操作 dom

1.js 获取 dom 的 5 种方式

1-1 getElementById

  • getElementById 是通过标签的 id 名称来获取标签的

  • 因为在一个页面中 id 是唯一的,所以获取到的就是一个元素

    html
    <body>
      <div id="box"></div>
      <script>
        var box = document.getElementById("box");
        console.log(box); // <div></div>
      </script>
    </body>
    • 获取到的就是页面中的那个 id 为 box 的 div 标签

1-2 getElementsByClassName

  • getElementsByClassName 是用过标签的 class 名称来获取标签的

  • 因为页面中可能有多个元素的 class 名称一样,所以获取到的是一组元素

  • 哪怕你获取的 class 只有一个,那也是获取一组元素,只不过这一组中只有一个 DOM 元素而已

    html
    <body>
      <div calss="box"></div>
      <script>
        var box = document.getElementsByClassName("box");
        console.log(box); // [<div></div>]
        console.log(box[0]); // <div></div>
      </script>
    </body>
    • 获取到的是一组元素,是一个长得和数组一样的数据结构,但是不是数组,是 伪数组
    • 这个一组数据也是按照索引排列的,所以我们想要准确的拿到这个 div,需要用索引来获取

1-3 getElementsByTagName

  • getElementsByTagName 是用过标签的 标签 名称来获取标签的

  • 因为页面中可能有多个元素的 标签 名称一样,所以获取到的是一组元素

  • 哪怕真的只有一个这个标签名,那么也是获取一组元素,只不过这一组中只有一个 DOM 元素而已

    html
    <body>
      <div></div>
      <script>
        var box = document.getElementsByTagName("div");
        console.log(box); // [<div></div>]
        console.log(box[0]); // <div></div>
      </script>
    </body>
    • getElementsByClassName 一样,获取到的是一个长得很像数组的元素
    • 必须要用索引才能得到准确的 DOM 元素

1-4 querySelector

  • querySelector 是按照选择器的方式来获取元素

  • 也就是说,按照我们写 css 的时候的选择器来获取

  • 这个方法只能获取到一个元素,并且是页面中第一个满足条件的元素

    javascript
    console.log(document.querySelector("div")); // 获取页面中的第一个 div 元素
    console.log(docuemnt.querySelector(".box")); // 获取页面中第一个有 box 类名的元素
    console.log(document.querySelector("#box")); // 获取页面中第一个 id 名为 box 的元素

1-5 querySelectorAll

  • querySelectorAll 是按照选择器的方式来获取元素

  • 这个方法能获取到所有满足条件的元素,以一个伪数组的形式返回

    javascript
    console.log(document.querySelectorAll("div")); // 获取页面中的所有的 div 元素
    console.log(docuemnt.querySelectorAll(".box")); // 获取页面中所有有 box 类名的元素
    • 获取到的是一组数据,也是需要用索引来获取到准确的每一个 DOM 元素

2.获取的 dom 有哪些属性

一个通过 document.选择器 这种方式获取的 dom 元素,上面有哪些属性呢?

普通的 DOM 元素通常都具有一些常见的属性,可以通过 JavaScript 来访问和操作。以下是一些常见的 DOM 元素属性:

  1. id:元素的唯一标识符。
  2. className:元素的类名,可以包含多个类名,用空格分隔。
  3. textContent:元素中的文本内容(它返回元素及其所有子元素中的纯文本内容,忽略所有 HTML 标签),包括其所有子元素的文本。——> 常用
  4. innerHTML:元素的 HTML 内容,包括其所有子元素的 HTML 结构。——> 常用
  5. style:表示元素的内联样式属性的对象。——> 常用
  6. value:适用于表单元素,表示元素的值,如 <input> 元素的输入值。
  7. src:适用于图片(<img>)和其他媒体元素,表示资源的 URL。
  8. href:适用于链接(<a>)元素,表示链接的目标 URL。
  9. tagName:标签名,表示元素的标签名,以大写形式显示。
  10. parentNode:表示父元素。——> 常用(获取所有兄弟元素的时候,可以先获取父元素,再获取父元素的所有子元素)
  11. children:表示子元素的集合。——> 常用
  12. nextSiblingpreviousSibling:分别表示下一个和上一个同级元素。——> 常用
  13. classList:表示元素的类名列表,可以用于添加、删除和切换类名。——> 常用

这只是一些常见的 DOM 元素属性。DOM API 提供了很多属性和方法来操作和查询元素的各个方面。您可以通过使用这些属性,根据需要访问和修改元素的内容、样式、属性等信息。