1

目标阶段的冒泡和捕获事件顺序是否在任何地方定义?

几乎任何你看的地方,你都会发现首先是捕获阶段,然后是冒泡阶段。一些消息来源还提到目标阶段是一个单独的阶段。

但是,提及目标阶段的消息来源并未指定此阶段的事件顺序。我假设注册为 Capturing 的事件将先于注册为 Bubbling 的事件。事实证明并非总是如此!

事实上——根据我的小实验结果——Target 阶段的事件处理程序执行完全取决于它们的注册顺序。

const extBox = document.querySelector('.external-box');
const intBox = document.querySelector('.internal-box');
const par = document.querySelector('p');

extBox.addEventListener('click', bubblingEvent, false);
intBox.addEventListener('click', bubblingEvent, false);
par.addEventListener('click', bubblingEvent, false);

par.addEventListener('click', capturingEvent, true);
extBox.addEventListener('click', capturingEvent, true);
intBox.addEventListener('click', capturingEvent, true);      
      
function bubblingEvent(event) {
  console.log(event.currentTarget.className + ': bubbling event, ' + getPhaseStr(event.eventPhase));
}
      
function capturingEvent(event) {
  console.log(event.currentTarget.className + ': capturing event, ' + getPhaseStr(event.eventPhase));
}

function getPhaseStr (eventPhase) {
  let eventPhaseStr;

  switch (eventPhase) {
    case 0:
      eventPhaseStr = 'NONE';
      break;
    case 1:
      eventPhaseStr = 'CAPTURING_PHASE';
      break;
    case 2:
      eventPhaseStr = 'AT_TARGET';
      break;
    case 3:
      eventPhaseStr = 'BUBBLING_PHASE';
      break;
    default:
      eventPhaseStr = 'ERROR';
  }

  return eventPhaseStr;
}
* {
  box-sizing: border-box;
}

.external-box, .internal-box {
  width: 50%;
  margin: 1rem;
  padding: 1rem;
}

.external-box {
  background-color: aquamarine;
}

.internal-box {
  background-color: blueviolet;
}

p {
  background-color: cornsilk;
  padding: 1rem;
}
<div class='external-box'>external-box
  <div class='internal-box'>internal-box
    <p class='par'>paragraph</p>
  </div>
</div>

在上面的示例中,我们可以看到,在目标阶段期间,为冒泡阶段添加的事件在捕获阶段之前执行。这是因为我先注册了冒泡阶段的事件。如果我先为捕获阶段注册事件,然后再为冒泡阶段注册事件,则顺序将是“正确的”。

我的问题再次:目标阶段期间的事件顺序是否在任何地方定义?

4

1 回答 1

0

你是对的。当一个事件监听器附加到一个元素上,并且该元素是目标时,事件监听器是设置为在捕获期间激活还是在冒泡期间激活都没有关系。官方规范在此处对其进行了描述。

  1. 对于 listeners 中的每个 listeners,其删除为 false:

  2. 如果阶段是“正在捕获”并且侦听器的捕获是错误的,则继续。

  3. 如果阶段是“冒泡”并且侦听器的捕获为真,则继续。

  4. (最终调用附加的监听器)

在目标时,该阶段既不捕获也不冒泡,如您的getPhaseStr函数如何解析eventPhase. 所以既没有3. continue也没有4. continue被激活,所以监听器最终被调用,按照监听器连接的顺序。(监听器顺序保存在“事件监听器列表”中,即附加到特定元素的监听器列表)

b1.addEventListener('click', () => console.log('bubbling'));
b1.addEventListener('click', () => console.log('capturing'), true);

b2.addEventListener('click', () => console.log('capturing'), true);
b2.addEventListener('click', () => console.log('bubbling'));
<button id="b1">click</button>
<button id="b2">click</button>

于 2020-04-16T12:20:11.547 回答