[译] 模块化系统中的 event.stopPropagation()

在 Moxio,咱们经过叫 widgets 的模块来构建网络应用。一个 widget 里面包含一些逻辑,它将控制一小部分 HTML。就像是 checkbox 元素或者一组其它的 widgets。一个 widget 能够申明它须要的数据和依赖关系,而且能够选择传递资源去它的子组件。模块化能够很好的来管理复杂度,由于全部的资源传输的渠道都被很明确的定义了。模块化也能够容许你经过不一样的组合方式来复用 widgets。JavaScript 想要真正的确保模块化约定是有点小困难的,由于你老是能够访问全局做用域,固然,咱们也有办法来解决这个问题。javascript

JavaScript 中的模块化设计

原生 JavaScript 的 API 在设计中并无考虑到模块化;默认状况下,你能够访问到全局做用域(global_ scope)。咱们经过将全局资源封装在根目录并向下层传递的方式,来让 wigets 得到到这些资源。咱们对一些资源进行了封装,好比 LocalStorage,页面的 URL 以及 viewport(为了观察在页面内的坐标)。咱们还封装 DOMElements 和事件。经过这些封装器,咱们能够限制和调整功能,进而保证模块化约定的完整。例如:一个 click 事件 可能知道 shift 键是否被按,可是你无法知道 click 事件的目标是什么,由于该点击事件的目标多是在另外一个 widget 内。这个看起来可能有很是大的限制性,可是直到目前,咱们尚未发现须要直接暴露目标的需求。html

对于每个特征,咱们都找到了一种不破化模块化约定的方法来表达它们。这也引出了我对于 event.stopPropagation() 的分析。咱们是否须要它?咱们如何可以提供它的功能?前端

stopPropagation 的栗子🌰

思考一下这个 HTML 的例子:java

<div class="table">
    <div class="body">
        <div class="row open">
            <div class="columns">
                <div class="cell">
                    <span class="bullet"></span>
                    <input type="checkbox" />
                    Lorem ipsum dolor sit amet
                </div>
                <div class="cell"><a href="/lorem-ipsum">Lorem ipsum</a></div>
            </div>
            <div class="contents">
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
            </div>
        </div>
        <!-- more rows -->
    </div>
</div>
复制代码

加了一点 CSS 后它变成了这样:android

咱们有以下一些交互:ios

  • 点击 checkbox 将选中和取消它,而且使得所在行被“选择”
  • 点击第二格里的连接将会打开对应地址
  • 点击任何一行将会打开或者关闭该行下显示“内容”

JavaScript 的事件模型

让咱们一块儿快速的过一遍事件在 JavaScript 中是怎么运做的。当你点击一个元素节点(例如一个 checkbox),一个事件诞生,首先它沿着节点树向下传递:table > body > row > columns > cell > input。这是捕捉(capturing)阶段。而后,这个事件按照相反的顺序向上传递,这个冒泡(bubble)阶段:input > cell > columns > row > body > table.git

这意味着,对于 checkbox 的点击会形成一个在 checkbox 和 row 上的 click 事件。咱们不但愿点击 checkbox 会打开/关闭 row,因此咱们须要查明这一点。这里咱们也就引入了 stopPropagation。github

function on_checkbox_click(event) {
    toggle_checkbox_state();
    event.stopPropagation(); // prevent this event from bubbling up
}
复制代码

若是处于冒泡(bubble)阶段时,你在 checkbox 中的 click 事件的监听器中加入了 event.stopPropagation(),那么这个事件将不会继续向上冒泡传递,也就永远不会到达 row 节点。也就简单明了实现了咱们所期待的交互。后端

预期以外的交互

然而,使用 stopPropagation 有一个反作用。点击 checkbox 的事件将 彻底 再也不向上传递。咱们的初衷是屏蔽在 row 节点上的点击事件,但咱们也屏蔽了全部的父节点。例如说咱们有一个若是点击在其余地方,就会被关闭的打开着的菜单。那么那个简单明了的 click 监听器就再也不适用,由于咱们的 click 事件可能会“消失”。咱们依旧可以使用捕捉(capturing)阶段,可是又有什么可以阻止位于父节点中的一个 widget 来屏蔽掉那个事件呢?stopPropagation 给咱们的模块化带来了矛盾。彷佛,在捕捉(capturing)和 冒泡(bubble)阶段中,禁止在 widgets 中加入 event propagation 的才是众望所归的选择。网络

若是咱们从封装器中移除对于 stopPropagation 的支持,咱们还可以实现咱们的上述的交互么?能够的,可是将会很混乱。咱们能够作一些簿记,经过记录的方式知晓何时咱们应该忽略 row 节点上的 click 事件,或者咱们能够新建一个事件的目标,又或者咱们让你知道事件在哪发生。咱们实验了一些解决方法,可是咱们并不太喜欢它们。

经过簿记来解决的例子:

var checkbox_was_clicked = false;

function on_checkbox_click() {
    checkbox_was_clicked = true;
    handle_checkbox_click();
}

function on_row_click() {
    if (checkbox_was_clicked === false) {
        handle_row_click();
    }
    checkbox_was_clicked = false;
}
复制代码

你能够看出,当咱们但愿屏蔽更多的元素节点(例如第二行的连接),或者但愿屏蔽的元素在次级的 widget 时,这个解决方法将会变得多么的笨重。

一个概念上的解决方式

咱们能够作的更好。这里有这样一个概念。咱们尚未给它想好一个名字,但咱们考虑叫它 significant action(重大的动做) 相似的名字。当你 click 时,你老是有一个最主要的动做:不论是打开/关闭 row 节点 仍是 checkbox,但历来不会是两者同时发生。从 UX 设计的角度来讲这很道理的。个人第一个想法是 stopPropagation 不该该中止冒泡(bubble),而应该在事件中设定一个标志来代表,一个重要的动做已经被执行了。这个方法的缺点是对于每个可交互的元素节点来讲(checkbox,link,button 等等),你都须要为它们添加一个事件触发(handler)来设置这个标志。那看起来会非常很大的工做量。咱们能够稍微改进一点:对于交互元素节点,咱们已经知道它们有significant action,因此若是目标是交互元素节点,那么就自动设定 significant 标志。当咱们把这样的逻辑实如今咱们的事件封装器时,row 节点如今只须要去检查 significant 标志,那么咱们就能够忽略来自第一列的 checkbox 和 第二列的连接的点击事件了。

咱们能够这样实现咱们 row 的 click 事件触发:

function on_row_click(event) {
    if (event.is_handled() === false) { // this event had no significant action
        toggle_row_open_state();
    }
}
复制代码

总结

我常常被 JavaScript 和它的原生库的设计中的前瞻性所惊艳。整体来讲,它工做的很好。它那一种选择你本身的冒险式的 API 支持不少的工做流程,也包括咱们的。咱们的模块化设计和封装让咱们能够在原生库上增长咱们的概念。咱们能够填海移山。

咱们依旧容许 stopPropagation 的使用,可是咱们不鼓励。significant - 标志已经在不少的 checkbox-table 中实现了,欢乐多多哟。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索