parent 节点上的事件侦听器也在所有 children 节点上触发
Event listener on parent node is triggering on all children nodes too
我正在从头开始用 vanilla js 编写一个可拖动的 class(这与 jQuery 可拖动无关),它工作得很好,只有我的听众在 [=40= 上触发]ren 所选节点。我已经在侦听器内部尝试 e.stopPropagation()
,但它似乎并没有改变结果。
这是 class 中的一段代码:
setListeners() {
const targets = document.getElementsByClassName("-jsDraggable");
[...targets].forEach(elem => {
elem.addEventListener("mousemove", e => this.handleMouseMove(e));
elem.addEventListener("mousedown", e => this.handleMouseDown(e));
elem.addEventListener("mouseup", e => this.handleMouseUp(e));
});
}
...例如:
<ul class="-jsDraggable">
<li>No drag</li>
<li>No drag</li>
<li class="-jsDraggable">Can drag this item</li>
<li>No drag</li>
</ul>
在这种情况下,我希望 <ul class="-jsDraggable">...</ul>
和内部的单个 <li class="-jsDraggable">...</li>
都受到影响,从其他列表项之一拖动只会拖动主 <ul class="-jsDraggable">...</ul>
元素。
但是,无论我与哪个 child 节点交互,无论它在 DOM 中与具有 class 的节点相比有多远,它都会触发处理程序.
如有任何帮助,我们将不胜感激!
编辑:另一个需要说明的例子:
<article class="-jsDraggable">
<header>
<h1>Heading</h1>
</header>
<main>
<p>...</p>
</main>
</article>
无论我将这个 <article>
元素拖到哪里,它都应该将整个 HTML 组作为一个拖入(因为其中的 parent 都有 class).然而,目前它的表现更像这样:
<article class="-jsDraggable">
<header class="-jsDraggable">
<h1 class="-jsDraggable">Heading</h1>
</header>
<main class="-jsDraggable">
<p class="-jsDraggable">...</p>
</main>
</article>
...并且每个 child 也在移动,而它应该只移动附有 class 的 parent 容器。
如果我正确理解你的问题,那么一个选项(除其他外)是区分事件的来源,并通过 currentTarget
和 target
字段过滤事件处理 event
像这样的对象:
if(event.currentTarget === event.target) {
/* The user is interacting with the actual -jsDraggable element */
}
else {
/* The user is interacting with a descendant of a -jsDraggable element */
}
这实际上意味着,如果生成事件的元素(即 currentTarget
)与事件处理程序绑定到的元素相同(即 target
),那么我们确定该事件与实际的 -jsDraggable
元素本身有关(而不是后代)。这种方法补充了您当前的代码,如下所示:
function handleMouseMove(event) {
if(event.currentTarget === event.target) {
console.log(Date.now(), 'Mouse move over draggable');
}
}
function handleMouseDown(event) {
if(event.currentTarget === event.target) {
console.log(Date.now(), 'Drag start on draggable');
}
}
function handleMouseUp(event) {
if(event.currentTarget === event.target) {
console.log(Date.now(), 'Drag end on draggable');
}
}
function setListeners() {
const targets = document.querySelectorAll(".-jsDraggable");
targets.forEach(elem => {
elem.addEventListener("mousemove", e => handleMouseMove(e));
elem.addEventListener("mousedown", e => handleMouseDown(e));
elem.addEventListener("mouseup", e => handleMouseUp(e));
});
}
setListeners();
ul {
padding:1rem;
background:lightgreen;
}
li {
padding:1rem;
background:pink;
}
.-jsDraggable {
background:lightblue;
}
<ul class="-jsDraggable">
<li>No drag</li>
<li>No drag</li>
<li class="-jsDraggable">Can drag this item</li>
<li>No drag</li>
</ul>
我解决了它,它不起作用,因为我将当前元素设置为 this.elem = e.target
而不是 this.elem = e.currentTarget
。此后,mousedown 中的 e.stopPropagation()
按预期工作。
感谢您让我了解 e.currentTarget
,在阅读此线程中的帮助之前我并不知道它。
我正在从头开始用 vanilla js 编写一个可拖动的 class(这与 jQuery 可拖动无关),它工作得很好,只有我的听众在 [=40= 上触发]ren 所选节点。我已经在侦听器内部尝试 e.stopPropagation()
,但它似乎并没有改变结果。
这是 class 中的一段代码:
setListeners() {
const targets = document.getElementsByClassName("-jsDraggable");
[...targets].forEach(elem => {
elem.addEventListener("mousemove", e => this.handleMouseMove(e));
elem.addEventListener("mousedown", e => this.handleMouseDown(e));
elem.addEventListener("mouseup", e => this.handleMouseUp(e));
});
}
...例如:
<ul class="-jsDraggable">
<li>No drag</li>
<li>No drag</li>
<li class="-jsDraggable">Can drag this item</li>
<li>No drag</li>
</ul>
在这种情况下,我希望 <ul class="-jsDraggable">...</ul>
和内部的单个 <li class="-jsDraggable">...</li>
都受到影响,从其他列表项之一拖动只会拖动主 <ul class="-jsDraggable">...</ul>
元素。
但是,无论我与哪个 child 节点交互,无论它在 DOM 中与具有 class 的节点相比有多远,它都会触发处理程序.
如有任何帮助,我们将不胜感激!
编辑:另一个需要说明的例子:
<article class="-jsDraggable">
<header>
<h1>Heading</h1>
</header>
<main>
<p>...</p>
</main>
</article>
无论我将这个 <article>
元素拖到哪里,它都应该将整个 HTML 组作为一个拖入(因为其中的 parent 都有 class).然而,目前它的表现更像这样:
<article class="-jsDraggable">
<header class="-jsDraggable">
<h1 class="-jsDraggable">Heading</h1>
</header>
<main class="-jsDraggable">
<p class="-jsDraggable">...</p>
</main>
</article>
...并且每个 child 也在移动,而它应该只移动附有 class 的 parent 容器。
如果我正确理解你的问题,那么一个选项(除其他外)是区分事件的来源,并通过 currentTarget
和 target
字段过滤事件处理 event
像这样的对象:
if(event.currentTarget === event.target) {
/* The user is interacting with the actual -jsDraggable element */
}
else {
/* The user is interacting with a descendant of a -jsDraggable element */
}
这实际上意味着,如果生成事件的元素(即 currentTarget
)与事件处理程序绑定到的元素相同(即 target
),那么我们确定该事件与实际的 -jsDraggable
元素本身有关(而不是后代)。这种方法补充了您当前的代码,如下所示:
function handleMouseMove(event) {
if(event.currentTarget === event.target) {
console.log(Date.now(), 'Mouse move over draggable');
}
}
function handleMouseDown(event) {
if(event.currentTarget === event.target) {
console.log(Date.now(), 'Drag start on draggable');
}
}
function handleMouseUp(event) {
if(event.currentTarget === event.target) {
console.log(Date.now(), 'Drag end on draggable');
}
}
function setListeners() {
const targets = document.querySelectorAll(".-jsDraggable");
targets.forEach(elem => {
elem.addEventListener("mousemove", e => handleMouseMove(e));
elem.addEventListener("mousedown", e => handleMouseDown(e));
elem.addEventListener("mouseup", e => handleMouseUp(e));
});
}
setListeners();
ul {
padding:1rem;
background:lightgreen;
}
li {
padding:1rem;
background:pink;
}
.-jsDraggable {
background:lightblue;
}
<ul class="-jsDraggable">
<li>No drag</li>
<li>No drag</li>
<li class="-jsDraggable">Can drag this item</li>
<li>No drag</li>
</ul>
我解决了它,它不起作用,因为我将当前元素设置为 this.elem = e.target
而不是 this.elem = e.currentTarget
。此后,mousedown 中的 e.stopPropagation()
按预期工作。
感谢您让我了解 e.currentTarget
,在阅读此线程中的帮助之前我并不知道它。