Skip to content

Instantly share code, notes, and snippets.

@sertraline
Last active September 3, 2021 06:36
Show Gist options
  • Save sertraline/4bde068c19d8b3a029958127b7ba814e to your computer and use it in GitHub Desktop.
Save sertraline/4bde068c19d8b3a029958127b7ba814e to your computer and use it in GitHub Desktop.
Pure javascript phone mask. Supports backspacing as well as selection backspacing.
export default class PhoneField {
constructor(
handler,
code = '+7',
mask = '(___)___-__-__',
placeholder = '_'
) {
this.handler = handler;
this.mask = code + mask;
this.code = code;
this.placeholder = placeholder;
this.setLength();
this.setValue();
this.start = this.placeHolderPosition() - 1;
this.handler.addEventListener('focusin', () => {
this.focused();
});
this.handler.addEventListener('keydown', (d) => {
this.input(d);
});
}
focused() {
let a = this.placeHolderPosition();
(this.handler.selectionStart = a), (this.handler.selectionEnd = a);
}
input(a) {
if (
(this.isDirectionKey(a.key) || a.preventDefault(),
this.isNum(a.key))
)
this.changeChar(a.key);
else if (this.isDeletionKey(a.key))
if ('Backspace' === a.key) {
let text = this.getSelectionText();
if (text.length === 0) {
this.changeChar(this.placeholder, -1, this.start);
} else {
this.clear(this.getSelectionText());
}
} else this.changeChar(this.placeholder);
}
getSelectionText() {
let text = '';
if (window.getSelection) {
text = window.getSelection().toString();
} else if (
document.selection &&
document.selection.type !== 'Control'
) {
text = document.selection.createRange().text;
}
return text;
}
setLength() {
this.handler.maxLength = this.mask.length;
}
setValue() {
this.handler.value = this.mask;
}
isNum(a) {
return (
!isNaN(a) && parseInt(+a) === parseInt(a) && !isNaN(parseInt(a, 10))
);
}
isDeletionKey(a) {
return 'Delete' === a || 'Backspace' === a;
}
isDirectionKey(a) {
return (
'ArrowUp' === a ||
'ArrowDown' === a ||
'ArrowRight' === a ||
'ArrowLeft' === a ||
'Tab' === a
);
}
isPlaceholder(a) {
return a === this.placeholder;
}
placeHolderPosition() {
return this.handler.value.indexOf(this.placeholder);
}
clear(text) {
let start = this.handler.selectionStart - this.code.length;
let value = this.handler.value;
value = value.slice(this.code.length);
let start_string = value.substring(0, start);
let replace_string = value.substring(start, start + text.length);
let end_string = value.substring(start + text.length, value.length);
replace_string = replace_string.split('');
for (let i = 0; i < replace_string.length; i++) {
if (!isNaN(replace_string[i])) {
replace_string[i] = this.placeholder;
}
}
this.handler.value =
this.code + start_string + replace_string.join('') + end_string;
this.handler.selectionStart = start + this.code.length;
this.handler.selectionEnd = start + this.code.length;
if (this.handler.selectionEnd < 1) {
this.handler.selectionStart = this.code.length + 1;
this.handler.selectionEnd = this.code.length + 1;
}
}
changeChar(a, b = 1, len = this.mask.length) {
let value = this.handler.value;
let sel_start =
0 < b
? this.handler.selectionStart
: this.handler.selectionStart - 1;
let g = '';
if (sel_start === len) return !1;
if (sel_start <= this.code.length - 1) return !1;
if (
!this.isNum(value[sel_start]) &&
!this.isPlaceholder(value[sel_start])
) {
do
if (((sel_start += b), sel_start === len)) {
return !1;
}
while (
!this.isNum(value[sel_start]) &&
!this.isPlaceholder(value[sel_start])
);
}
g = this.replaceAt(value, sel_start, a);
this.handler.value = g;
0 < b && (sel_start += b);
this.handler.selectionStart = sel_start;
this.handler.selectionEnd = sel_start;
}
replaceAt(a, b, c) {
return a.substring(0, b) + c + a.substring(++b);
}
}
// DOM
<input
type="tel"
name="tel[]"
class="masked-phone"
data-phonemask="(___)___-__-__"
data-code="+38"
/>
let a = document.getElementsByClassName('masked-phone'),
b = [];
for (let c = 0; c < a.length; c++)
b.push(
new PhoneField(
a[c],
a[c].dataset.code,
a[c].dataset.phonemask,
a[c].dataset.placeholder
)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment