从前有匹马叫代码
心若没有栖息的地方,到哪里都是流浪

在 JavaScript 中事件可以被捕获和进行冒泡,捕获和冒泡允许我们实现一种被称为 事件委托 的强大的事件处理模式。

这个想法是,如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上。

假如我们有一个表格,该表格有9999+个单元格,我们想给每个被点击的单元格设置上选中颜色,与其为每个 <td>(可能有很多)分配一个 onclick 处理程序 —— 我们可以在 <table> 元素上设置一个“捕获所有”的处理程序。

代码如下:

var selectedTd;

table.onclick = function(event) {
  let target = event.target; // 在哪里点击的?

  if (target.tagName != 'TD') return; // 不在 TD 上?那么我们就不会在意

  highlight(target); // 高亮显示它
};

function highlight(td) {
  if (selectedTd) { // 移除现有的高亮显示,如果有的话
    selectedTd.classList.remove('highlight');
  }
  selectedTd = td;
  selectedTd.classList.add('highlight'); // 高亮显示新的 td
}

这样可以解决数量上的问题,但是还有个问题就是如果<td>中还嵌套了其他的标签呢,类似<strong></strong>

自然地,如果在该 <strong> 上点击,那么它将成为 event.target 的值。

为了规避掉这种情况,我们应该确定是否实在<td>内,所以改进代码如下所示:

table.onclick = function(event) {
  var td = event.target.closest('td'); // (1)

  if (!td) return; // (2)

  if (!table.contains(td)) return; // (3)

  highlight(td); // (4)
};

elem.closest(selector) 方法返回与 selector 匹配的最近的祖先。在我们的例子中,我们从源元素开始向上寻找 <td>
<div id="box">
    Box
    <strong>strong</strong>
</div>
<script>
    box.onclick = function (event) {
        console.log('event.target:',event.target.closest("div"));
    }
</script>

用事件委托来实现一个可以展开关闭的树形菜单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript 代理模式</title>
    <script src="index.js" defer ></script>
    <style>
        li {
            cursor: pointer;
        }
    </style>
</head>
<body>


<ul id="classify">
    <li>
       动物
        <ul>
            <li>
                鱼类
                <ul>
                    <li>草鱼</li>
                    <li>鲤鱼</li>
                    <li>黑鱼</li>
                </ul>
            </li>
            <li>
                鸟类
                <ul>
                    <li>喜鹊</li>
                    <li>乌鸦</li>
                    <li>孔雀</li>
                </ul>
            </li>
        </ul>
    </li>
    <li>
        植物
        <ul>
            <li>
                花类
                <ul>
                    <li>月季</li>
                    <li>樱花</li>
                    <li>牡丹</li>
                </ul>
            </li>
            <li>
                草类
                <ul>
                    <li>水草</li>
                    <li>野草</li>
                    <li>小草</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>
</body>
</html>

 

// index.js
let classify = document.getElementById('classify');

classify.onclick = function ({target}) {

    if (target.tagName !== 'LI') {
        return;
    }
    let firstElementChild = target.querySelector("ul");
    if (!firstElementChild) {
        return;
    }

    firstElementChild.hidden = !firstElementChild.hidden;
}

总结:

好处:

  • 简化初始化并节省内存:无需添加许多处理程序。
  • 更少的代码:添加或移除元素时,无需添加/移除处理程序
  • DOM 修改 :我们可以使用 innerHTML 等,来批量添加/移除元素

坏处:

  • 首先事件必须得冒泡,但是有些事件并不能冒泡
  • 容器级别的处理程序会对容器中任意位置的事件做出反应,不管我们是否需要,可能会增加性能消耗


posted on 2021-03-20 14:52  从前有匹马叫代码  阅读(62)  评论(0)    收藏  举报