【问题标题】:Dispatch event to element bypassing capturing and bubbling绕过捕获和冒泡向元素分发事件
【发布时间】:2020-08-07 11:15:36
【问题描述】:

由于dispatchEvent,根据docs,将应用:

正常的事件处理规则(包括捕获和可选 冒泡阶段)

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

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

(例如在不经过层次结构的情况下触发文本区域的默认keydown)

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

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;
  };

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

【问题讨论】:

  • 你在考虑 stopPropagation() 吗?
  • 我正在研究如何 - 在窗口级别劫持事件后 - 直接在元素上触发它。我确实已经在使用stopPropagation()
  • 最少的可生产代码将帮助我们弄清楚您的用例是什么,以及如何最好地帮助您。 :)

标签: javascript html event-handling dom-events


【解决方案1】:

可能有一种更优雅的方式来做到这一点,但一种选择是先从 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 中,因此 用作其祖先的元素不会看到该事件。

请注意,事件被分派到分离的元素do not capture/bubble regardless,甚至不是事件被分派到的元素的父级或子级。

如果不事先从 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时,然后手动调用监听函数你要。确保在页面上的任何其他脚本运行之前附加 您的 窗口侦听器,以确保页面的任何侦听器都不能首先看到该事件:

// 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');
});

【讨论】:

  • 这似乎是我正在寻找的行为,但有没有办法让用户输入直接进入分离的元素?
  • “直入”,什么意思?输入附加了事件侦听器,因此它需要保持原样,我们不能创建不同的输入。输入必须手动分离,以便在调度事件后可以在同一位置重新附加(使用replaceWith)。
  • 分离的元素不会触发捕获和冒泡,这就是我正在寻找的。但是我可以在不将其放入 DOM 的情况下获得用户输入吗?
  • 你可以用Javascript设置它的value,但是用户不能输入分离的元素,因为它们不在DOM中。这是一件很奇怪的事情。通常,管理父侦听器以确保它们不会引起问题会更有意义。
  • 还有影子 DOM,您可以对其进行配置,使其内部的某些事件不会冒泡javascript.info/shadow-dom-events
【解决方案2】:

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

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='' />

【讨论】:

  • 我希望在绕过处理的同时触发默认元素事件行为
  • 否,例如文本区域的默认keydown不经过层次结构
  • eventObject.stopPropagation()。顺便说一句,如果你想要所有字符,你应该使用keyup
猜你喜欢
  • 2014-03-07
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
  • 2011-02-09
  • 2017-01-13
相关资源
最近更新 更多