Last active
March 3, 2021 03:18
-
-
Save oscarduignan/f7a479c30c40ee1dd975d7393438f516 to your computer and use it in GitHub Desktop.
vanilla js input masking https://codesandbox.io/s/vanilla-js-input-masking-f2rux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const NON_DIGITS_OR_SPACES = /[^0-9 ]+/g; | |
const SPACES_OTHER_THAN_SINGLE_TRAILING = / (?!$)/g; | |
// could do it in one regex but then "1234 a" will be "1234" rather than "1234 " | |
// const NON_DIGITS_EXCEPT_SINGLE_TRAILING_SPACE = /[^0-9 ]+| (?!$)/g; | |
const maskCreditCardNumber = (value) => { | |
return ( | |
value | |
.replace(NON_DIGITS_OR_SPACES, "") | |
.replace(SPACES_OTHER_THAN_SINGLE_TRAILING, "") | |
.match(/[0-9]{1,4} ?/g) || [] | |
).join(" "); | |
}; | |
const expectedCursorPosition = ({ | |
updatedValue, | |
initialValue, | |
initialSelectionStart, | |
initialSelectionEnd, | |
inputTypeIsDeletion | |
}) => { | |
// the difference is used to account for chars we add | |
// when masking and to support instances where user | |
// pasted a value which had characters we removed | |
const difference = inputTypeIsDeletion | |
? 0 // otherwise "1234| 5" <del> would be "12|35" not "123|5" | |
: updatedValue.length - initialValue.length; | |
return { | |
start: initialSelectionStart + difference, | |
end: initialSelectionEnd + difference | |
}; | |
}; | |
const maskCreditCardInput = (input) => { | |
let inputTypeIsDeletion; | |
const applyMasking = ({ target }) => { | |
const { | |
value: initialValue, | |
selectionStart: initialSelectionStart, | |
selectionEnd: initialSelectionEnd | |
} = target; | |
const updatedValue = maskCreditCardNumber(initialValue); | |
const updatedSelection = expectedCursorPosition({ | |
updatedValue, | |
initialValue, | |
initialSelectionStart, | |
initialSelectionEnd, | |
inputTypeIsDeletion | |
}); | |
target.value = updatedValue; | |
// TODO confirm direct assign is most cross-browser compliant method | |
target.selectionStart = updatedSelection.start; | |
target.selectionEnd = updatedSelection.end; | |
}; | |
// TODO is this cross-browser compatible enough for my use-case? | |
const trackInputType = ({ key }) => | |
(inputTypeIsDeletion = key === "Backspace"); | |
input.addEventListener("keydown", trackInputType); | |
input.addEventListener("input", applyMasking); | |
// TODO does it need any other listeners? | |
return () => { | |
input.removeEventListener("keydown", trackInputType); | |
input.removeEventListener("input", applyMasking); | |
}; | |
}; | |
const stopMaskingInput = maskCreditCardInput( | |
document.getElementById("creditCard") | |
); | |
// stopMaskingInput(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
late night tinkering for some reason 🤷♂️