Skip to content

Instantly share code, notes, and snippets.

@oscarduignan
Last active March 3, 2021 03:18
Show Gist options
  • Save oscarduignan/f7a479c30c40ee1dd975d7393438f516 to your computer and use it in GitHub Desktop.
Save oscarduignan/f7a479c30c40ee1dd975d7393438f516 to your computer and use it in GitHub Desktop.
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();
@oscarduignan
Copy link
Author

late night tinkering for some reason 🤷‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment