JSFiddle - React, Tailwind, and code Playground

by jolleekin

HTML

<div>Format a credit card number as chunks of four digits depending on its cart type. The caret is handled correctly.</div>
<input type=text />
<div id=cardName></div>

CSS

body {
  font: 15px/1.3333 'Segoe UI', 'Open Sans', sans-serif;
  zoom: 2;
}
input {
  width: 100%;
}

JavaScript

var input = document.querySelector('input');

var cartTypes = [{
    'code': 'AMEX',
    'name': 'American Express',
    'startDigits': '34',
    'length': 15,
    'format': [4, 6, 5]
}, {
    'code': 'VISA',
    'name': 'Visa',
    'startDigits': '4',
    'length': 16,
    'format': [4, 4, 4, 4]
}
];

input.onkeyup = function (e) {
    if (this.value == this.lastValue) return;
    var caretPosition = this.selectionStart;
    var sanitizedValue = this.value.replace(/[^0-9]/gi, '');
    var parts = [];
    var cartType;
    for (var i = 0, len = cartTypes.length; i < len; i++) {
        if (sanitizedValue.indexOf(cartTypes[i].startDigits) == 0) {
            cartType = cartTypes[i];
            break;
        }
    }
    cardName.textContent = cartType ? cartType.name : '';
    
    if (!cartType) return;
    
    if (sanitizedValue.length > cartType.length) {
        sanitizedValue = sanitizedValue.substr(0, cartType.length);
    }
    
    for (var i = caretPosition - 1; i >= 0; i--) {
        var c = this.value[i];
        if (c < '0' || c > '9') {
            caretPosition--;
        }
    }
    
    var j = 0;
    var dp = 0;
    for (var i = 0, len = sanitizedValue.length; i < len;) {
        var partLength = cartType.format[j];
        parts.push(sanitizedValue.substring(i, i + partLength));
        i += partLength;
        if (caretPosition >= i) dp++;
        j++;
    }
    
    caretPosition += dp;
    
    this.value = this.lastValue = parts.join(' ');
    this.selectionStart = this.selectionEnd = caretPosition;
}

input.focus();