绕过捕获和冒泡向元素发送事件

Dispatch event to element bypassing capturing and bubbling

dispatchEvent 起,根据 docs,将应用:

normal event processing rules (including the capturing and optional bubbling phase)

我正在寻找类似的东西,但有一种方法可以跳过这个过程并直接在元素上触发事件。在绕过处理阶段时触发默认元素事件行为。

例如,在 window 级别捕获事件(在它到达其他捕获触发器之前)并将其直接传递给直接调用它的组件(文本区域)。

(例如不通过层次结构触发文本区域的默认按键)

我一直在尝试这样做,但如果在 window 级别有另一个事件,这将不起作用:

window.addEventListener("keydown", this.keyDown, true);

keyDown = (event) => {
      event.preventDefault();
      event.nativeEvent && event.nativeEvent.stopImmediatePropagation();
      event.stopImmediatePropagation && event.stopImmediatePropagation();
      event.stopPropagation();

      // Pass event straight to the element

      return false;
  };

我希望在绕过处理时触发默认元素事件行为

可能有更优雅的方法来做到这一点,但一个选择是先从 DOM 中删除元素,将事件分派给它,然后将其放回 DOM:

document.body.addEventListener('keydown', () => {
  console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
  console.log('body keydown bubbling');
});


const input = document.querySelector('input');
input.addEventListener('keydown', () => {
  console.log('input keydown');
});
const node = document.createTextNode('');
input.replaceWith(node);
input.dispatchEvent(new Event('keydown'));
node.replaceWith(input);
<input>

由于在分派事件时该元素不在 DOM 中,因此 作为其祖先的元素将看不到该事件。

请注意,事件被分派到分离的元素 ,甚至没有被分派到事件被分派到的元素的父元素或子元素。

如果不事先完全从 DOM 中删除元素,如果输入可以存在于影子 DOM 中,您也可以在那里向它调度一个事件,并且该事件不会捕获向下或向上冒泡(虽然用户输入不是自定义事件,但会传播):

document.body.addEventListener('keydown', () => {
  console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
  console.log('body keydown bubbling');
});


outer.attachShadow({mode: 'open'});

const input = document.createElement('input');
outer.shadowRoot.append(input);
input.addEventListener('keydown', () => {
  console.log('input keydown');
});

input.dispatchEvent(new Event('keydown'));
<div id="outer"></div>

另一种方法是在 捕获 阶段调用 stopPropagationstopImmediatePropagation,在最开始,当事件处于 window,然后手动调用你想要的监听函数。确保在页面 运行 上的任何其他脚本之前附加 您的 window 侦听器,以确保页面的 none 侦听器可以看到该事件第一:

// Your script:
const input = document.body.appendChild(document.createElement('input'));
input.className = 'custom-extension-element';
const handler = (e) => {
  setTimeout(() => {
    console.log(e.target.value);
  });
};
window.addEventListener(
  'keydown',
  (e) => {
    if (e.target.closest('.custom-extension-element')) {
      e.stopImmediatePropagation(); // Stop other capturing listeners on window from seeing event
      e.stopPropagation(); // Stop all other listeners
      handler(e);
    }
  },
  true // add listener in capturing phase
);


// Example page script
// which tries to attach listener to window in capturing phase:
window.addEventListener(
  'keydown',
  (e) => {
    console.log('page sees keydown');
  },
  true
);
document.body.addEventListener('keydown', () => {
  console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
  console.log('body keydown bubbling');
});

解决传播问题的最佳方法是只执行函数:

function tester(){
  console.log("Just fire the function! Don't dispatchEvent!");
}
tester();
document.getElementById('test').onkeyup = function(e){
  e.stopPropagation();
  console.log(this.id); console.log(e.target); tester();
}
document.body.onkeyup = ()=>{
  console.log("This shouldn't fire when on #test");
}
<input id='test' type='text' value='' />