Skip to content

Commit 7e20362

Browse files
authored
fix: firefox issue where masked value can overflow
* add input event listener to parentElement to ensure proper execution order * do not store handler on config object
1 parent d114a8f commit 7e20362

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

src/directive.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,28 @@ const CONFIG_KEY = core.CONFIG_KEY
44
export default {
55
bind: (el, { value, modifiers }, vnode) => {
66
el = core.getInputElement(el)
7-
el.addEventListener('input', core.inputHandler, true)
8-
97
const config = core.normalizeConfig(value, modifiers)
108
el[CONFIG_KEY] = { config }
119

1210
// set initial value
1311
core.updateValue(el, vnode, { force: config.prefill })
1412
},
1513

14+
inserted: (el) => {
15+
el = core.getInputElement(el)
16+
const config = el[CONFIG_KEY]
17+
// prefer adding event listener to parent element to avoid Firefox bug which does not
18+
// execute `useCapture: true` event handlers before non-capturing event handlers
19+
const handlerOwner = el.parentElement || el
20+
21+
// use anonymous event handler to avoid inadvertently removing masking for all inputs within a container
22+
const handler = (e) => core.inputHandler(e)
23+
24+
handlerOwner.addEventListener('input', handler, true)
25+
26+
config.cleanup = () => handlerOwner.removeEventListener('input', handler, true)
27+
},
28+
1629
update: (el, { value, oldValue, modifiers }, vnode) => {
1730
el = core.getInputElement(el)
1831

@@ -24,5 +37,7 @@ export default {
2437
}
2538
},
2639

27-
unbind: (el) => el.removeEventListener('input', core.inputHandler, true)
40+
unbind: (el) => {
41+
core.getInputElement(el)[CONFIG_KEY].cleanup()
42+
}
2843
}

tests/directive.test.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('Directive', () => {
1515
directives: { facade },
1616
methods: { inputListener },
1717
data() {
18-
return { mask }
18+
return { mask, flag: true }
1919
}
2020
}
2121

@@ -57,6 +57,29 @@ describe('Directive', () => {
5757
expect(wrapper.find('#second').element.value).toBe('3344')
5858
})
5959

60+
test('Removing a masked input from the DOM should not impact other masked inputs in the same container', async () => {
61+
const template = `<div id='owner'>
62+
<input v-facade="mask" id="first" />
63+
<input v-if="flag" v-facade="mask" id="second" />
64+
</div>`
65+
66+
buildWrapper({ template, mask: '##.##' })
67+
68+
wrapper.find('#first').setValue('1111')
69+
wrapper.find('#second').setValue('1111')
70+
71+
expect(wrapper.find('#first').element.value).toBe('11.11')
72+
expect(wrapper.find('#second').element.value).toBe('11.11')
73+
74+
// remove #second input from DOM, triggering directive unbind
75+
await wrapper.setData({ flag: false })
76+
expect(wrapper.find('#second').exists()).toBeFalsy()
77+
78+
// ensure #first input is still being masked
79+
wrapper.find('#first').setValue('1122')
80+
expect(wrapper.find('#first').element.value).toBe('11.22')
81+
})
82+
6083
test('Should update element value on input', async () => {
6184
buildWrapper({ value: 1234 })
6285
expect(wrapper.element.value).toBe('12.34')

0 commit comments

Comments
 (0)