我创建了一个自定义元素:
const templ = document.createElement('template');
templ.innerHTML = `
<span><slot></slot></span>
`;
class SlideButton extends HTMLElement {
constructor() {
super();
// Attach a shadow root to the element.
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(tmpl.content.cloneNode(true));
this.span = shadowRoot.querySelector('span');
this.triggerEvent = new CustomEvent("trigger", {
bubbles: false,
cancelable: false,
});
this.initMouseEvents();
}
initMouseEvents() {
this.span.addEventListener('mousedown', (e) => {
//Watch and calculate slide amount..
this.addEventListener('mousemove', this.slide, false);
});
//When button is released...
document.addEventListener('mouseup', handleMouseUp = (e) => {
this.removeEventListener('mousemove', this.slide, false);
document.removeEventListener('mouseup', handleMouseUp);
//If slided enough, dispatch event...
if (Math.abs(this.slideAmount) > (this.maxSlide * 0.75)) {
console.log('firing event');
this.dispatchEvent(this.triggerEvent);
}
//Reset button to normal state...
}, false);
}
}
代码中的其他地方。
class SpotLightModal {
//Constructor..
//code..
//code..
init() {
this.actions.querySelector('slide-button[type="den"]').addEventListener('trigger', e => {
console.log(e);
console.log('den');
//Do stuff..
});
}
//code...
//code...
}
一切正常,但事件侦听器中的回调运行两次,并且输出为:
firing event
CustomEvent {...}
den
CustomEvent {...}
den
e.stopPropagation()
和e.preventDefault()
均无效,而尝试使用它们则无济于事。
我已经进行了编辑,以包含this.span
,并且还将“ mouseup”事件侦听器移到了“ mousedown”事件侦听器的外部,但这没有用,实际上在登录this
时,它给出了另一个不同的元素(相同类型,<slide-button>
,页面上的第一个元素)不会删除“ mouseover”侦听器,并且不会触发该事件。
我在这里做错什么了吗?还是我到底想念什么?
先谢谢了。
答案 0 :(得分:0)
问题出在代码的嵌套上。
首先,您添加第一个mousedown
事件侦听器,该事件侦听器将在单击并释放鼠标时触发。
this.span.addEventListener('mousedown', (e) => { ...
然后在您的mousedown
事件监听器中,您在mouseup
上监听document
事件。
document.addEventListener('mouseup', handleMouseUp = (e) => { ...
因此,现在,无论您同时单击mousedown
和mouseup
都会被触发。即使您在mouseup
事件监听器中删除了事件监听器,也是如此。其中的代码已经在执行。
这就是导致您的代码两次触发CustomEvent
的原因。
防止事件侦听器嵌套,除非其中一个会依赖另一个。就像在mousedown
事件监听器中一样,您需要在其中添加mousemove
事件监听器。现在,不是嵌套,而是添加另一个事件监听器,该事件监听器侦听mouseup
事件监听器附近的mousedown
。在那里删除mousemove
事件。下面的示例:
class SlideButton extends HTMLElement {
constructor() {
super();
// Attach a shadow root to the element.
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(tmpl.content.cloneNode(true));
this.span = shadowRoot.querySelector('span');
this.triggerEvent = new CustomEvent("trigger", {
bubbles: false,
cancelable: false,
});
}
connectedCallback() {
// It's preferred to set the event listeners in the connectedCallback method.
// This method is called whenever the current element is connected to the document.
this.initMouseEvents();
}
initMouseEvents() {
// Bind slider-button as this context to the slide method.
// In case you use the this keyword inside of the slide method
// you would need to do this to keep using the methods of this class.
const slide = this.slide.bind(this);
// Create a toggle to indicate that the slide-button has been clicked so that ONLY then the event listeners will be added.
let targetIsClicked = false;
// Mouse is clicked, mousemove added.
document.addEventListener('mousedown', (e) => {
// Check if this button has been clicked.
const slideButton = e.target.closest('slide-button');
if (slideButton === this) {
// Toggle true and add mousemove event.
targetIsClicked = true;
document.addEventListener('mousemove', slide, false);
}
});
// Mouse is released, remove mousemove and fire event.
document.addEventListener('mouseup', (e) => {
// Only do something if this button has been clicked in the mousedown event.
if (targetIsClicked === true) {
document.removeEventListener('mousemove', slide, false);
// Reset toggle value.
targetIsClicked = false;
//If slided enough, dispatch event...
if (Math.abs(this.slideAmount) > (this.maxSlide * 0.75)) {
console.log('firing event');
this.dispatchEvent(this.triggerEvent);
}
//Reset button to normal state...
}
});
}
}
我已将事件侦听器的目标更改为document
。您说明了要在按钮外触发mousemove
和mouseup
事件的情况。
在mousedown
中,我检查是否单击的当前目标实际上是此按钮。如果是这样,targetIsClicked
值将被切换为表明已单击正确的按钮。
在mouseup
事件中,首先检查targetIsClicked
是否为true
。意味着您单击了此按钮并执行其余代码。
尽管,如果您有多个<slide-button>
元素,则会在mousedown
上附加多个mousemove
,mouseup
和document
侦听器,这可能产生一些奇怪的结果。
我已将this.initMouseEvents()
函数调用移至connectedCallback()
方法。这样做是个好习惯,因为现在您正在与自定义元素的生命周期进行交互。在创建元素时,将事件监听器添加到connectedCallback()
中,在元素被删除时,将事件监听器添加到disconnectedCallback()
中。元素本身会触发这些方法,因此您不必调用它们。
答案 1 :(得分:0)
有一个“气泡”选项,您可以将其设置为false,这将消除鼠标向下/向上冒泡的阶段:
var evt = new CustomEvent(name, {
detail : data,
bubbles: true, // <-- try setting to false
cancelable: true
});
document.dispatchEvent(evt);
答案 2 :(得分:0)
如果其他人遇到类似问题,请尝试使用:
event.stopImmediatePropagation()
像这样进入你的回调函数
window.addEventListener('message', (event) => {
event.stopImmediatePropagation();
console.log('detail:', event.detail);
})
就我而言,这似乎可以解决问题,但它绝对是超级黑客,如果需要超级可靠,建议您尝试找出问题的根本原因。
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation