Skip to content

Commit b0a482f

Browse files
authored
Merge pull request #1175 from cthulahoops/duplicate_event_handlers
Remove stale event handlers when nodes are readded to the document.
2 parents 0f6ebc4 + 5540e22 commit b0a482f

File tree

5 files changed

+57
-4
lines changed

5 files changed

+57
-4
lines changed

dist/alpine-ie11.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6929,6 +6929,8 @@
69296929
event = camelCase(event);
69306930
}
69316931

6932+
var node_add_count = el.__x_node_add_count;
6933+
69326934
var _handler2, listenerTarget;
69336935

69346936
if (modifiers.includes('away')) {
@@ -6966,6 +6968,11 @@
69666968
}
69676969
}
69686970

6971+
if (el.__x_node_add_count !== node_add_count) {
6972+
listenerTarget.removeEventListener(event, _handler2, options);
6973+
return;
6974+
}
6975+
69696976
if (isKeyEvent(event)) {
69706977
if (isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers)) {
69716978
return;
@@ -7806,6 +7813,7 @@
78067813
return;
78077814
}
78087815

7816+
node.__x_node_add_count = (node.__x_node_add_count || 0) + 1;
78097817
this.initializeElements(node);
78107818
}.bind(this));
78117819
}

dist/alpine.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@
863863
event = camelCase(event);
864864
}
865865

866+
const node_add_count = el.__x_node_add_count;
866867
let handler, listenerTarget;
867868

868869
if (modifiers.includes('away')) {
@@ -894,6 +895,11 @@
894895
}
895896
}
896897

898+
if (el.__x_node_add_count !== node_add_count) {
899+
listenerTarget.removeEventListener(event, handler, options);
900+
return;
901+
}
902+
897903
if (isKeyEvent(event)) {
898904
if (isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers)) {
899905
return;
@@ -1805,6 +1811,7 @@
18051811
return;
18061812
}
18071813

1814+
node.__x_node_add_count = (node.__x_node_add_count || 0) + 1;
18081815
this.initializeElements(node);
18091816
});
18101817
}

src/component.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ export default class Component {
412412
return
413413
}
414414

415+
node.__x_node_add_count = (node.__x_node_add_count || 0) + 1
416+
415417
this.initializeElements(node)
416418
})
417419
}

src/directives/on.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export function registerListener(component, el, event, modifiers, expression, ex
99
event = camelCase(event);
1010
}
1111

12+
const node_add_count = el.__x_node_add_count
13+
1214
let handler, listenerTarget
1315

1416
if (modifiers.includes('away')) {
@@ -43,6 +45,11 @@ export function registerListener(component, el, event, modifiers, expression, ex
4345
}
4446
}
4547

48+
if (el.__x_node_add_count !== node_add_count) {
49+
listenerTarget.removeEventListener(event, handler, options);
50+
return
51+
}
52+
4653
if (isKeyEvent(event)) {
4754
if (isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers)) {
4855
return

test/on.spec.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ import Alpine from 'alpinejs'
22
import { wait, fireEvent } from '@testing-library/dom'
33
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms))
44

5-
global.MutationObserver = class {
6-
observe() {}
7-
}
8-
95
test('data modified in event listener updates affected attribute bindings', async () => {
106
document.body.innerHTML = `
117
<div x-data="{ foo: 'bar' }">
@@ -560,3 +556,36 @@ test('.camel modifier correctly binds event listener with namespace', async () =
560556
expect(document.querySelector('p').textContent).toEqual('bob');
561557
});
562558
})
559+
560+
test('event handlers only fire once when nodes have been readded to the document.', async () => {
561+
document.body.innerHTML = `
562+
<div id="container" x-data="{'x': 0}">
563+
<span x-text="x">0</span>
564+
<button id="a" x-on:click="x += 1; $el.appendChild(document.getElementById('a'))">A</button>
565+
<button id="b" x-on:click="x += 1; $el.appendChild(document.getElementById('b'))">B</button>
566+
<button id="c" x-on:click="x += 1; $el.appendChild(document.getElementById('c'))">C</button>
567+
</div>
568+
`
569+
Alpine.start()
570+
const span = document.querySelector('span')
571+
572+
expect(span.textContent).toEqual('0')
573+
574+
document.querySelector('#a').click()
575+
await wait(() => {
576+
expect(span.textContent).toEqual('1')
577+
})
578+
expect(document.querySelector('#container').lastChild).toEqual(document.querySelector('#a'))
579+
580+
document.querySelector('#a').click()
581+
await wait(() => {
582+
expect(span.textContent).toEqual('2')
583+
})
584+
expect(document.querySelector('#container').lastChild).toEqual(document.querySelector('#a'))
585+
586+
document.querySelector('#a').click()
587+
await wait(() => {
588+
expect(span.textContent).toEqual('3')
589+
})
590+
expect(document.querySelector('#container').lastChild).toEqual(document.querySelector('#a'))
591+
})

0 commit comments

Comments
 (0)