Skip to content

JavaScript 事件

一: 事件绑定和触发是通过DOM(文档对象模型)操作来实现的。以下是一些常见的方法:

事件绑定:

1. addEventListener 方法:

javascript
// 获取元素
const myElement = document.getElementById('myElement');

// 添加事件监听器
myElement.addEventListener('click', function(event) {
  // 处理点击事件的代码
});

2. 直接赋值属性:

javascript
// 获取元素
const myElement = document.getElementById('myElement');

// 设置事件处理函数
myElement.onclick = function(event) {
  // 处理点击事件的代码
};

事件触发:

1. dispatchEvent 方法:

javascript
// 创建自定义事件
const customEvent = new Event('customEvent');

// 获取元素
const myElement = document.getElementById('myElement');

// 触发事件
myElement.dispatchEvent(customEvent);

2. 直接调用事件处理函数:

javascript
// 获取元素
const myElement = document.getElementById('myElement');

// 定义事件处理函数
function handleClick(event) {
  // 处理点击事件的代码
}

// 添加事件监听器
myElement.addEventListener('click', handleClick);

// 直接调用事件处理函数触发事件
handleClick();

示例说明:

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Event Example</title>
</head>
<body>

<button id="myButton">Click me</button>

<script>
  // 获取按钮元素
  const myButton = document.getElementById('myButton');

  // 添加点击事件监听器
  myButton.addEventListener('click', function(event) {
    alert('Button clicked!');
  });

  // 触发按钮的点击事件
  myButton.click();
</script>

</body>
</html>

在这个示例中,使用addEventListener方法为按钮元素绑定了点击事件的监听器。然后,通过click方法触发了按钮的点击事件,从而执行了相应的处理函数。

需要注意的是,使用addEventListener可以绑定多个事件处理函数,而使用属性赋值的方式只能设置一个处理函数。在现代Web开发中,推荐使用addEventListener,因为它更加灵活且支持多个事件处理函数。

二: 事件冒泡(Event Bubbling)和事件捕获(Event Capturing)是描述事件传播阶段的两个不同的概念。

在DOM中,一个事件在传播过程中经历了三个阶段:捕获阶段、目标阶段和冒泡阶段。这三个阶段构成了事件流。

事件流阶段:

  1. 捕获阶段(Event Capturing):

    • 事件从文档的根节点开始向下传播至目标元素之前的阶段。
  2. 目标阶段(Target Phase):

    • 事件到达目标元素。
  3. 冒泡阶段(Event Bubbling):

    • 事件从目标元素开始向上冒泡至文档的根节点的阶段。

区别:

  1. 事件冒泡(Bubbling):

    • 事件从目标元素开始冒泡至父级元素,一直到达文档的根节点。
    • 在事件冒泡阶段,事件会依次触发目标元素的父级元素的相同事件处理函数。
    html
    <div id="parent">
      <button id="child">Click me</button>
    </div>
    
    <script>
      document.getElementById('parent').addEventListener('click', function() {
        console.log('Parent div clicked');
      });
    
      document.getElementById('child').addEventListener('click', function() {
        console.log('Button clicked');
      });
    </script>

    在上述例子中,当点击按钮时,事件会先触发按钮上的事件处理函数,然后冒泡至父级div,触发父级div上的事件处理函数。

  2. 事件捕获(Capturing):

    • 事件从文档的根节点开始捕获至目标元素之前的阶段。
    • 在事件捕获阶段,事件会依次触发文档根节点到目标元素的各级祖先元素上的相同事件处理函数。
    html
    <div id="parent">
      <button id="child">Click me</button>
    </div>
    
    <script>
      document.getElementById('parent').addEventListener('click', function() {
        console.log('Parent div clicked');
      }, true); // 第三个参数设置为true,表示在捕获阶段处理事件
    
      document.getElementById('child').addEventListener('click', function() {
        console.log('Button clicked');
      });
    </script>

    在上述例子中,当点击按钮时,事件会先在捕获阶段触发父级div上的事件处理函数,然后再冒泡至按钮,触发按钮上的事件处理函数。

默认情况下,addEventListener的第三个参数是false,表示在冒泡阶段处理事件。如果将其设置为true,则在捕获阶段处理事件。在实际开发中,一般较少使用事件捕获,而更多地使用事件冒泡。

三: 事件委托

事件委托(Event Delegation)是一种利用事件冒泡的机制,将事件处理程序绑定到一个父元素上,通过判断事件的目标来执行相应的处理逻辑。换句话说,事件不是直接绑定到目标元素上,而是委托给其父元素或更上层的祖先元素来处理。

为什么使用事件委托是个好主意?

  1. 性能优化:

    • 减少了事件处理程序的数量,特别是在大型文档中。相比直接将事件处理程序绑定到每个子元素,只需在父元素上绑定一个事件处理程序,可以提高性能。
  2. 动态元素:

    • 对于动态添加或移除的元素,不需要重新绑定事件处理程序,因为事件处理是委托给父元素的。
  3. 简化代码结构:

    • 减少了在多个子元素上编写相似的事件处理逻辑的工作,代码更为简洁。
  4. 处理一类元素的事件:

    • 当有一类元素需要共享相同的事件处理逻辑时,事件委托非常方便。例如,列表中的所有列表项共享相同的点击事件处理。

示例:

考虑以下HTML结构:

html
<ul id="myList">
  <li key="1">Item 1</li>
  <li key="2">Item 2</li>
  <li key="3">Item 3</li>
</ul>

使用事件委托的方式:

javascript
// 获取父元素
const myList = document.getElementById('myList');
const lists = myList.getElementsByTagName('li')
console.log(lists, 'lists')
// 添加事件监听器(委托给父元素)
myList.addEventListener('click', function(event) {
  // 检查点击的是否是列表项
  console.log(event, 'event.target')
  if (event.target.tagName === 'LI') {
    // 处理列表项点击事件
    console.log('Clicked on:', event.target.textContent); // 打印li的文本内容
    console.log('Clicked on:', event.target.innerHTML); // 打印li的内容
    console.log('Clicked on:', event.target.attributes.key.value); // 打印li的key值
  }
  for (let i = 0; len= lists.length, i<len; i ++ ) {
    if (event.target == lists[i]) {
      console.log(i, '第'+i+'个') // 打印第几个
    }
  }
});

在这个例子中,点击任何列表项都会冒泡到父元素myList上,然后通过判断event.target.tagName来确定点击的是哪个列表项,从而执行相应的事件处理逻辑。这样,无论是现有的列表项还是将来添加的新列表项,都会共享相同的事件处理程序。

四: 在使用事件委托时,确保事件目标是你所期望的是非常重要的,以避免潜在的问题。

以下是一些常用的方法来确保事件目标符合预期:

  1. 使用合适的条件判断:

    • 在事件处理程序中使用条件判断,例如检查目标元素的标签名、类名、或其他属性,以确保事件目标是你所期望的。
    javascript
    myList.addEventListener('click', function(event) {
      if (event.target.tagName === 'LI') {
        // 处理点击列表项的逻辑
      }
    });
  2. 使用closest方法:

    • 使用Element.closest方法可以查找离当前元素最近的匹配选择器的祖先元素。这可以帮助确定事件目标是否是你所期望的元素或其祖先。
    javascript
    myList.addEventListener('click', function(event) {
      const listItem = event.target.closest('li');
      if (listItem) {
        // 处理点击列表项的逻辑
      }
    });
  3. 使用事件代理的选择器:

    • 在绑定事件监听器时,可以使用事件代理的选择器,仅当事件目标匹配选择器时才触发事件处理程序。
    javascript
    myList.addEventListener('click', function(event) {
      if (event.target.matches('li')) {
        // 处理点击列表项的逻辑
      }
    });
  4. 阻止事件冒泡:

    • 如果你不希望事件继续冒泡到父元素,可以在事件处理程序中调用event.stopPropagation()来阻止事件冒泡。
    javascript
    myList.addEventListener('click', function(event) {
      event.stopPropagation(); // 阻止事件冒泡
      // 处理点击列表项的逻辑
    });
  5. 阻止默认事件:

    • 一般都是checkbox, a, 事件keypress等有默认行为
javascript
  myList.addEventListener('click', function(event) {
   // 处理点击列表项的逻辑
    event.preventDefault(); // 第一种 阻止默认行为
   // return false; // 第二种 阻止默认行为
  });

通过以上方法,可以有效地确保事件目标是你所期望的元素,从而提高代码的健壮性和可维护性。