Skip to content

Commit 77b6545

Browse files
Unbind global listeners after the map is removed (patch) (#1434)
* fix global keyboard and window listeners are not removed after the map is destroyed * test: add a test case for keyboard mixin * change `map.on` to `map.once` & extract keyboard event listener unbind function Co-authored-by: Florian Bischof <design.falke@gmail.com> * fix missing dot operator * tweak the mixins test case Co-authored-by: Florian Bischof <design.falke@gmail.com> * Fix test naming --------- Co-authored-by: Florian Bischof <design.falke@gmail.com>
1 parent 9994568 commit 77b6545

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

cypress/integration/mixins.spec.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
describe('KeyboardMixin', () => {
2+
it('Should unbind event listeners that bound by the KeyboardMixin after the map is destroyed', () => {
3+
cy.window().then((window) => {
4+
const { map, document } = window;
5+
6+
map.remove();
7+
8+
const isWindowBlurEventUnbound = !Object.entries(
9+
window._leaflet_events
10+
).some(([name, handler]) => name.startsWith('blur') && handler);
11+
expect(
12+
isWindowBlurEventUnbound,
13+
'window blur event listener is not unbound'
14+
).to.eq(true);
15+
16+
const isKeyUpDownEventUnbound = !Object.entries(
17+
document._leaflet_events
18+
).some(([name, handler]) => name.startsWith('key') && handler);
19+
expect(
20+
isKeyUpDownEventUnbound,
21+
'document keyboard event listener is not unbound'
22+
).to.eq(true);
23+
});
24+
});
25+
});

src/js/L.PM.Map.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import GlobalDragMode from './Mixins/Modes/Mode.Drag';
55
import GlobalRemovalMode from './Mixins/Modes/Mode.Removal';
66
import GlobalRotateMode from './Mixins/Modes/Mode.Rotate';
77
import EventMixin from './Mixins/Events';
8-
import KeyboardMixins from './Mixins/Keyboard';
8+
import createKeyboardMixins from './Mixins/Keyboard';
99
import { getRenderer } from './helpers';
1010

1111
const Map = L.Class.extend({
@@ -20,7 +20,7 @@ const Map = L.Class.extend({
2020
this.map = map;
2121
this.Draw = new L.PM.Draw(map);
2222
this.Toolbar = new L.PM.Toolbar(map);
23-
this.Keyboard = KeyboardMixins;
23+
this.Keyboard = createKeyboardMixins();
2424

2525
this.globalOptions = {
2626
snappable: true,

src/js/Mixins/Keyboard.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
const KeyboardMixins = {
1+
// use function to create a new mixin object for keeping isolation
2+
// to make it work for multiple map instances
3+
const createKeyboardMixins = () => ({
24
_lastEvents: { keydown: undefined, keyup: undefined, current: undefined },
35
_initKeyListener(map) {
46
this.map = map;
57
L.DomEvent.on(document, 'keydown keyup', this._onKeyListener, this);
68
L.DomEvent.on(window, 'blur', this._onBlur, this);
9+
// clean up global listeners when current map instance is destroyed
10+
map.once('unload', this._unbindKeyListenerEvents, this);
11+
},
12+
_unbindKeyListenerEvents() {
13+
L.DomEvent.off(document, 'keydown keyup', this._onKeyListener, this);
14+
L.DomEvent.off(window, 'blur', this._onBlur, this);
715
},
816
_onKeyListener(e) {
917
let focusOn = 'document';
@@ -44,6 +52,6 @@ const KeyboardMixins = {
4452
getPressedKey() {
4553
return this._lastEvents.current?.event.key;
4654
},
47-
};
55+
});
4856

49-
export default KeyboardMixins;
57+
export default createKeyboardMixins;

0 commit comments

Comments
 (0)