29

How to format and validate a credit card number with spaces between each 4 digit while typing:

eg: 4464 6846 4354 3564

I have tried:

$('.creditno').keyup(function() {
    cc = $(this).val().split("-").join("");

    cc = cc.match(new RegExp('.{1,4}$|.{1,4}', 'g')).join("-");

    $(this).val(cc);

});

Please help

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
mevr
  • 1,075
  • 3
  • 14
  • 32
  • 1
    What have u tried so far? :) – Ahs N Apr 25 '16 at 06:27
  • 4
    Possible duplicate of [What is the best way to validate a credit card in PHP?](http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php) – Daan Apr 25 '16 at 06:27
  • Thanks for replying, i have tried formance.js mot working then jquery.payment.js not working then tried some codes but always issues i need to fix it urgently so – mevr Apr 25 '16 at 06:34

20 Answers20

49

Try this:

function cc_format(value) {
    var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
    var matches = v.match(/\d{4,16}/g);
    var match = matches && matches[0] || ''
    var parts = []

    for (i=0, len=match.length; i<len; i+=4) {
        parts.push(match.substring(i, i+4))
    }

    if (parts.length) {
        return parts.join(' ')
    } else {
        return value
    }
}

Note: Check this for detailed information https://www.peterbe.com/plog/cc-formatter.

To restrict the user to enter number only:

Javascript Way

<input type="text" id="txt_cardNumber" name="txt_cardNumber" onkeypress="return checkDigit(event)">

function checkDigit(event) {
    var code = (event.which) ? event.which : event.keyCode;

    if ((code < 48 || code > 57) && (code > 31)) {
        return false;
    }

    return true;
}

OR

function checkDigit() {
    var allowedChars = "0123456789";
    var entryVal = document.getElementById('txt_cardNumber').value();
    var flag;

    for(var i=0; i<entryVal.length; i++){       
        flag = false;

        for(var j=0; j<allowedChars.length; j++){
            if(entryVal.charAt(i) == allowedChars.charAt(j)) {
                flag = true; 
            }
        }

        if(flag == false) { 
            entryVal = entryVal.replace(entryVal.charAt(i),""); i--; 
        }
    }

    return true;
}

HTML5 Way

<input type="text" id="txt_cardNumber" name="txt_cardNumber" pattern="[0-9.]+">
<input type="number" id="txt_cardNumber" name="txt_cardNumber">

jQuery Way

$("#txt_cardNumber").keypress(function (e) {
    if ((e.which < 48 || e.which > 57) && (e.which !== 8) && (e.which !== 0)) {
        return false;
    }

    return true;
});

Note: Please check here to get more information about various key code.

prava
  • 3,916
  • 2
  • 24
  • 35
  • 3
    Why should OP try this? Give some more information about your piece of code. – Daan Apr 25 '16 at 06:29
  • That's why I added the link to get more info about the solution. – prava Apr 25 '16 at 06:30
  • The link has no detailed information. To *REALLY* check creditcards you need a merchant account which you probably shoud've mentioned. – Daan Apr 25 '16 at 06:32
  • 4
    Before giving any comment and negative vote, please check OP's requirement carefully. OP has mentioned `How to format and validate a credit card number with spaces between each 4 digit while typing:`. As of me, OP wants the javascript validation. – prava Apr 25 '16 at 06:33
  • and I do not feel for validation while typing, server side validation will be a wise decision. – prava Apr 25 '16 at 06:35
  • Besides you didn't even write the code. Just copy pasted it. I think you have no idea what the code really does if you can explain that to me I will upvote you. – Daan Apr 25 '16 at 06:36
  • @Daan, how come you know !! Feel pity for your assumption. I already implemented the same logic and it worked for me. By the way - I already have added the link for the reference. – prava Apr 25 '16 at 06:40
  • 1
    I'm guessing my assumption is right, since you don't want to explain what the code actually does. – Daan Apr 25 '16 at 06:43
  • No choosing sides here but adding some more information about your answer will improve it drastically. – Thomas Bormans Apr 25 '16 at 10:54
  • how can i restrict user to enter number only? type=number not working – mevr Apr 25 '16 at 13:30
  • @youv, I edited my answer and added the code to restrict the user to enter numeric digit only. – prava Apr 25 '16 at 14:29
  • @prava Thank you so much. – mevr Apr 25 '16 at 14:32
  • 3
    What can be done for AMEX cards which is of the form xxxx-xxxxxx-xxxxx ,ie 4-6-5 – sajanyamaha Jun 21 '18 at 10:16
  • @prava `var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')` can be simplified to `var v = value.replace(/\D/g, '');` – thybzi Jul 02 '18 at 14:19
  • hi here is a problem with while i am using this code it is working till 4 digits but after fourth digit when it add space then backspace all the code please give me solution – Nadeem Ijaz May 15 '20 at 11:09
14

Just wrote this to handle Visa, Discover, Master Card and Amex (with formatting and card type identification).

// SAMPLE FIELD:   <input type="text" name="cstCCNumber" id="cstCCNumber" value=""onkeyup="cc_format('cstCCNumber','cstCCardType');">
    function cc_format(ccid,ctid) {
        // supports Amex, Master Card, Visa, and Discover
        // parameter 1 ccid= id of credit card number field
        // parameter 2 ctid= id of credit card type field

        var ccNumString=document.getElementById(ccid).value;
        ccNumString=ccNumString.replace(/[^0-9]/g, '');
        // mc, starts with - 51 to 55
        // v, starts with - 4
        // dsc, starts with 6011, 622126-622925, 644-649, 65
        // amex, starts with 34 or 37
        var typeCheck = ccNumString.substring(0, 2);
        var cType='';
        var block1='';
        var block2='';
        var block3='';
        var block4='';
        var formatted='';

        if  (typeCheck.length==2) {
            typeCheck=parseInt(typeCheck);
            if (typeCheck >= 40 && typeCheck <= 49) {
                cType='Visa';
            }
            else if (typeCheck >= 51 && typeCheck <= 55) {
                cType='Master Card';
            }
            else if ((typeCheck >= 60 && typeCheck <= 62) || (typeCheck == 64) || (typeCheck == 65)) {
                cType='Discover';
            }
            else if (typeCheck==34 || typeCheck==37) {
                cType='American Express';
            }
            else {
                cType='Invalid';
            }
        }

        // all support card types have a 4 digit firt block
        block1 = ccNumString.substring(0, 4);
        if (block1.length==4) {
            block1=block1 + ' ';
        }

        if (cType == 'Visa' || cType == 'Master Card' || cType == 'Discover') {
            // for 4X4 cards
            block2 = ccNumString.substring(4, 8);
            if (block2.length==4) {
                block2=block2 + ' ';
            }
            block3 = ccNumString.substring(8, 12);
            if (block3.length==4) {
                block3=block3 + ' ';
            }
            block4 = ccNumString.substring(12, 16);
        }
        else if (cType == 'American Express') {
            // for Amex cards
            block2 =  ccNumString.substring(4, 10);
            if (block2.length==6) {
                block2=block2 + ' ';
            }
            block3 =  ccNumString.substring(10, 15);
            block4='';
        }
        else if (cType == 'Invalid') {
            // for Amex cards
            block1 =  typeCheck;
            block2='';
            block3='';
            block4='';
            alert('Invalid Card Number');
        }

        formatted=block1 + block2 + block3 + block4;
        document.getElementById(ccid).value=formatted;
        document.getElementById(ctid).value=cType;
    }
Robert H
  • 176
  • 1
  • 6
12

With ES6

export const formatCardNumber = value => {
  const regex = /^(\d{0,4})(\d{0,4})(\d{0,4})(\d{0,4})$/g
  const onlyNumbers = value.replace(/[^\d]/g, '')

  return onlyNumbers.replace(regex, (regex, $1, $2, $3, $4) =>
    [$1, $2, $3, $4].filter(group => !!group).join(' ')
  )
}
Juliano Reis
  • 161
  • 1
  • 4
11

I couldn't find a reasonable solution that works with text editing, so here you go:

$("#cardNumber").on("keydown", function(e) {
    var cursor = this.selectionStart;
    if (this.selectionEnd != cursor) return;
    if (e.which == 46) {
        if (this.value[cursor] == " ") this.selectionStart++;
    } else if (e.which == 8) {
        if (cursor && this.value[cursor - 1] == " ") this.selectionEnd--;
    }
}).on("input", function() {
    var value = this.value;
    var cursor = this.selectionStart;
    var matches = value.substring(0, cursor).match(/[^0-9]/g);
    if (matches) cursor -= matches.length;
    value = value.replace(/[^0-9]/g, "").substring(0, 16);
    var formatted = "";
    for (var i=0, n=value.length; i<n; i++) {
        if (i && i % 4 == 0) {
            if (formatted.length <= cursor) cursor++;
            formatted += " ";
        }
        formatted += value[i];
    }
    if (formatted == this.value) return;
    this.value = formatted;
    this.selectionEnd = cursor;
});

The keydown listener is needed to adjust the cursor position for backspace and delete to move past spaces. It should not be used to restrict character entry, as you don't want to use key codes for that.

The input listener rebuilds the text, strips non-numbers, adds spaces every 4 characters, and preserves the cursor position.

NateS
  • 5,751
  • 4
  • 49
  • 59
5

function cc_format(value) {
  var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
  var matches = v.match(/\d{4,16}/g);
  var match = matches && matches[0] || ''
  var parts = []
  for (i=0, len=match.length; i<len; i+=4) {
    parts.push(match.substring(i, i+4))
  }
  if (parts.length) {
    return parts.join(' ')
  } else {
    return value
  }
}

onload = function() {
  document.getElementById('cc').oninput = function() {
    this.value = cc_format(this.value)
  }
}
function checkDigit(event) {
    var code = (event.which) ? event.which : event.keyCode;

    if ((code < 48 || code > 57) && (code > 31)) {
        return false;
    }

    return true;
}
<form>
  <input id="cc" value="" placeholder="1234 1234 1234 1234" onkeypress="return checkDigit(event)">
</form>

Live demo of check digit and formatting of CC card number

Gauravbhai Daxini
  • 2,032
  • 2
  • 22
  • 28
2

Find

Plunker for Formatting Credit Card Numbers

using angularjs directive. Format Card Numbers in xxxxxxxxxxxx3456 Fromat.

angular.module('myApp', [])

   .directive('maskInput', function() {
    return {
            require: "ngModel",
            restrict: "AE",
            scope: {
                ngModel: '=',
             },
            link: function(scope, elem, attrs) {
                var orig = scope.ngModel;
                var edited = orig;
                scope.ngModel = edited.slice(4).replace(/\d/g, 'x') + edited.slice(-4);

                elem.bind("blur", function() {
                    var temp;
                    orig  = elem.val();
                    temp = elem.val();
                    elem.val(temp.slice(4).replace(/\d/g, 'x') + temp.slice(-4));
                });

                elem.bind("focus", function() {
                    elem.val(orig);
               });  
            }
       };
   })
  .controller('myCtrl', ['$scope', '$interval', function($scope, $interval) {
    $scope.creditCardNumber = "1234567890123456";
  }]);
Sagar Bhosale
  • 407
  • 1
  • 5
  • 19
2

using vanilla js

javascript:

function formatCreditCardOnKey(event) {
    //on keyup, check for backspace to skip processing
    var code = (event.which) ? event.which : event.keyCode;
    if(code != 8)
        formatCreditCard();
    else{
        //trim whitespace from end; trimEnd() doesn't work in IE
        document.getElementById("cardNumber").value = document.getElementById("cardNumber").value.replace(/\s+$/, '');
    }

}

function formatCreditCard() {
    var cardField = document.getElementById("cardNumber");
    //remove all non-numeric characters
    var realNumber = cardField.value.replace(/\D/g,'');
    var newNumber = "";
    for(var x = 1; x <= realNumber.length; x++){
        //make sure input is a digit
        if (isNumeric(realNumber.charAt(x-1)))
            newNumber += realNumber.charAt(x-1);
        //add space every 4 numeric digits
        if(x % 4 == 0 && x > 0 && x < 15)
            newNumber += " ";
    }
    cardField.value = newNumber;
}

function isNumeric(char){
    return('0123456789'.indexOf(char) !== -1);
}

HTML:

<input type="text" id="cardNumber" maxlength="19" onKeyUp="formatCreditCardOnKey(event)" onBlur="formatCreditCard()" onFocus="formatCreditCard()"/>

This works (for me) with autofill, allows the user to use backspace as expected (they don't have to delete whitespace), and doesn't allow (other) non-numeric characters.

Russell Chisholm
  • 645
  • 9
  • 13
1
<?php


function luhn_check($number) {

  // Strip any non-digits (useful for credit card numbers with spaces and hyphens)
  $number=preg_replace('/\D/', '', $number);

  // Set the string length and parity
  $number_length=strlen($number);
  $parity=$number_length % 2;

  // Loop through each digit and do the maths
  $total=0;
  for ($i=0; $i<$number_length; $i++) {
    $digit=$number[$i];
    // Multiply alternate digits by two
    if ($i % 2 == $parity) {
      $digit*=2;
      // If the sum is two digits, add them together (in effect)
      if ($digit > 9) {
        $digit-=9;
      }
    }
    // Total up the digits
    $total+=$digit;
  }

  // If the total mod 10 equals 0, the number is valid
  return ($total % 10 == 0) ? TRUE : FALSE;

}
?>
Waruna Manjula
  • 3,067
  • 1
  • 34
  • 33
1
function testCreditCard () {
  myCardNo = document.getElementById('CardNumber').value;
  myCardType = document.getElementById('CardType').value;
  if (checkCreditCard (myCardNo,myCardType)) {
    alert ("Credit card has a valid format")
  } 
  else {alert (ccErrors[ccErrorNo])};
}

check this link for lib http://www.braemoor.co.uk/software/creditcard.shtml

Bhanu
  • 44
  • 2
1

This may simple way:

function numberFormat(x){
    return x.replace(/(.{4})/g, "$1 ");
}

If you want to connect dash every 4 digits,

function numberFormat(x){
    return x.replace(/(.{4})/g, "$1-");
}
1

If you want to add space every 4 numbers you can user regular expressions and the replace method. Here's an example

function addSpaceAfterFourNumbers(input) {
// Remove any existing spaces from the input
var strippedInput = input.replace(/\s/g, '');

// Insert a space after every 4 numbers using regular expressions
var formattedInput = strippedInput.replace(/(\d{4})(?=\d)/g, '$1 ');

return formattedInput;
}

// Example usage
var input = '1234567890123456';
var formattedInput = addSpaceAfterFourNumbers(input);
console.log(formattedInput); // Output: 1234 5678 9012 3456

In the above code, the addSpaceAfterFourNumbers() function takes an input string, removes any existing spaces using the regular expression /\s/g, and then inserts a space after every 4 numbers using the regular expression (\d{4})(?=\d). The $1 in the replace() method is a backreference to the matched group of four numbers, and the space is added after it.

You can modify the input variable to test different input strings. The resulting formattedInput variable will contain the input string with spaces inserted after every 4 numbers.

aries
  • 64
  • 1
  • 12
0
   export const removeNonNumber = (string = "") => string.replace(/[^\d]/g, "");

   export const removeLeadingSpaces = (string = "") => string.replace(/^\s+/g, "");
   const limitLength = (string = "", maxLength) => string.substr(0, maxLength);
   const FALLBACK_CARD = { gaps: [4, 8, 12], lengths: [16], code: { size: 3 } };
   const addGaps = (string = "", gaps) => {
   const offsets = [0].concat(gaps).concat([string.length]);

   return offsets
     .map((end, index) => {
     if (index === 0) return "";
     const start = offsets[index - 1];
     return string.substr(start, end - start);
     })
    .filter((part) => part !== "")
    .join(" ");
  };

  //this method to call

_formatNumber = (number, card) => {
const numberSanitized = removeNonNumber(number);
const maxLength = card.lengths[card.lengths.length - 1];
const lengthSanitized = limitLength(numberSanitized, maxLength);
const formatted = addGaps(lengthSanitized, card.gaps);

//set your state here

return formatted;
};

//use above method like this in text input

cardEnter(strings = "") {
this._formatNumber(strings, FALLBACK_CARD);

}

Antier Solutions
  • 1,326
  • 7
  • 10
0

Let's try this. This code will replace your edit number in real-time.

$(document).ready(function() {
    document.getElementById('card_no').onkeyup = function (e) {
      if (this.value == this.lastValue) return;
      var caretPosition = this.selectionStart;
      var sanitizedValue = this.value.replace(/[^0-9]/gi, '');
      var parts = [];
      for (var i = 0, len = sanitizedValue.length; i < len; i += 4) {
          parts.push(sanitizedValue.substring(i, i + 4));
      }
      for (var i = caretPosition - 1; i >= 0; i--) {
          var c = this.value[i];
          if (c < '0' || c > '9') {
              caretPosition--;
          }
      }
      caretPosition += Math.floor(caretPosition / 4);
      this.value = this.lastValue = parts.join(' ');
      this.selectionStart = this.selectionEnd = caretPosition;
    }
});
  • Welcome to Stack Overflow! Code-only answers are not particularly helpful. Please add some descriptions of how this code solves the problem. – Sven Eberth Jun 07 '21 at 18:42
0

You can find here everything about credit card : open source

open source here (:

/*
See on github: https://github.com/muhammederdem/credit-card-form
*/

new Vue({
  el: "#app",
  data() {
    return {
      currentCardBackground: Math.floor(Math.random()* 25 + 1), // just for fun :D
      cardName: "",
      cardNumber: "",
      cardMonth: "",
      cardYear: "",
      cardCvv: "",
      minCardYear: new Date().getFullYear(),
      amexCardMask: "#### ###### #####",
      otherCardMask: "#### #### #### ####",
      cardNumberTemp: "",
      isCardFlipped: false,
      focusElementStyle: null,
      isInputFocused: false
    };
  },
  mounted() {
    this.cardNumberTemp = this.otherCardMask;
    document.getElementById("cardNumber").focus();
  },
  computed: {
    getCardType () {
      let number = this.cardNumber;
      let re = new RegExp("^4");
      if (number.match(re) != null) return "visa";

      re = new RegExp("^(34|37)");
      if (number.match(re) != null) return "amex";

      re = new RegExp("^5[1-5]");
      if (number.match(re) != null) return "mastercard";

      re = new RegExp("^6011");
      if (number.match(re) != null) return "discover";
      
      re = new RegExp('^9792')
      if (number.match(re) != null) return 'troy'

      return "visa"; // default type
    },
        generateCardNumberMask () {
            return this.getCardType === "amex" ? this.amexCardMask : this.otherCardMask;
    },
    minCardMonth () {
      if (this.cardYear === this.minCardYear) return new Date().getMonth() + 1;
      return 1;
    }
  },
  watch: {
    cardYear () {
      if (this.cardMonth < this.minCardMonth) {
        this.cardMonth = "";
      }
    }
  },
  methods: {
    flipCard (status) {
      this.isCardFlipped = status;
    },
    focusInput (e) {
      this.isInputFocused = true;
      let targetRef = e.target.dataset.ref;
      let target = this.$refs[targetRef];
      this.focusElementStyle = {
        width: `${target.offsetWidth}px`,
        height: `${target.offsetHeight}px`,
        transform: `translateX(${target.offsetLeft}px) translateY(${target.offsetTop}px)`
      }
    },
    blurInput() {
      let vm = this;
      setTimeout(() => {
        if (!vm.isInputFocused) {
          vm.focusElementStyle = null;
        }
      }, 300);
      vm.isInputFocused = false;
    }
  }
});
@import url("https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600,700|Source+Sans+Pro:400,600,700&display=swap");
body {
  background: #ddeefc;
  font-family: "Source Sans Pro", sans-serif;
  font-size: 16px;
}

* {
  box-sizing: border-box;
}
*:focus {
  outline: none;
}

.wrapper {
  min-height: 100vh;
  display: flex;
  padding: 50px 15px;
}
@media screen and (max-width: 700px), (max-height: 500px) {
  .wrapper {
    flex-wrap: wrap;
    flex-direction: column;
  }
}

.card-form {
  max-width: 570px;
  margin: auto;
  width: 100%;
}
@media screen and (max-width: 576px) {
  .card-form {
    margin: 0 auto;
  }
}
.card-form__inner {
  background: #fff;
  box-shadow: 0 30px 60px 0 rgba(90, 116, 148, 0.4);
  border-radius: 10px;
  padding: 35px;
  padding-top: 180px;
}
@media screen and (max-width: 480px) {
  .card-form__inner {
    padding: 25px;
    padding-top: 165px;
  }
}
@media screen and (max-width: 360px) {
  .card-form__inner {
    padding: 15px;
    padding-top: 165px;
  }
}
.card-form__row {
  display: flex;
  align-items: flex-start;
}
@media screen and (max-width: 480px) {
  .card-form__row {
    flex-wrap: wrap;
  }
}
.card-form__col {
  flex: auto;
  margin-right: 35px;
}
.card-form__col:last-child {
  margin-right: 0;
}
@media screen and (max-width: 480px) {
  .card-form__col {
    margin-right: 0;
    flex: unset;
    width: 100%;
    margin-bottom: 20px;
  }
  .card-form__col:last-child {
    margin-bottom: 0;
  }
}
.card-form__col.-cvv {
  max-width: 150px;
}
@media screen and (max-width: 480px) {
  .card-form__col.-cvv {
    max-width: initial;
  }
}
.card-form__group {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
}
.card-form__group .card-input__input {
  flex: 1;
  margin-right: 15px;
}
.card-form__group .card-input__input:last-child {
  margin-right: 0;
}
.card-form__button {
  width: 100%;
  height: 55px;
  background: #2364d2;
  border: none;
  border-radius: 5px;
  font-size: 22px;
  font-weight: 500;
  font-family: "Source Sans Pro", sans-serif;
  box-shadow: 3px 10px 20px 0px rgba(35, 100, 210, 0.3);
  color: #fff;
  margin-top: 20px;
  cursor: pointer;
}
@media screen and (max-width: 480px) {
  .card-form__button {
    margin-top: 10px;
  }
}

.card-item {
  max-width: 430px;
  height: 270px;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  z-index: 2;
  width: 100%;
}
@media screen and (max-width: 480px) {
  .card-item {
    max-width: 310px;
    height: 220px;
    width: 90%;
  }
}
@media screen and (max-width: 360px) {
  .card-item {
    height: 180px;
  }
}
.card-item.-active .card-item__side.-front {
  transform: perspective(1000px) rotateY(180deg) rotateX(0deg) rotateZ(0deg);
}
.card-item.-active .card-item__side.-back {
  transform: perspective(1000px) rotateY(0) rotateX(0deg) rotateZ(0deg);
}
.card-item__focus {
  position: absolute;
  z-index: 3;
  border-radius: 5px;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transition: all 0.35s cubic-bezier(0.71, 0.03, 0.56, 0.85);
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
  border: 2px solid rgba(255, 255, 255, 0.65);
}
.card-item__focus:after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  background: #08142f;
  height: 100%;
  border-radius: 5px;
  filter: blur(25px);
  opacity: 0.5;
}
.card-item__focus.-active {
  opacity: 1;
}
.card-item__side {
  border-radius: 15px;
  overflow: hidden;
  box-shadow: 0 20px 60px 0 rgba(14, 42, 90, 0.55);
  transform: perspective(2000px) rotateY(0deg) rotateX(0deg) rotate(0deg);
  transform-style: preserve-3d;
  transition: all 0.8s cubic-bezier(0.71, 0.03, 0.56, 0.85);
  backface-visibility: hidden;
  height: 100%;
}
.card-item__side.-back {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  transform: perspective(2000px) rotateY(-180deg) rotateX(0deg) rotate(0deg);
  z-index: 2;
  padding: 0;
  height: 100%;
}
.card-item__side.-back .card-item__cover {
  transform: rotateY(-180deg);
}
.card-item__bg {
  max-width: 100%;
  display: block;
  max-height: 100%;
  height: 100%;
  width: 100%;
  object-fit: cover;
}
.card-item__cover {
  height: 100%;
  background-color: #1c1d27;
  position: absolute;
  height: 100%;
  background-color: #1c1d27;
  left: 0;
  top: 0;
  width: 100%;
  border-radius: 15px;
  overflow: hidden;
}
.card-item__cover:after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: rgba(6, 2, 29, 0.45);
}
.card-item__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 40px;
  padding: 0 10px;
}
@media screen and (max-width: 480px) {
  .card-item__top {
    margin-bottom: 25px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__top {
    margin-bottom: 15px;
  }
}
.card-item__chip {
  width: 60px;
}
@media screen and (max-width: 480px) {
  .card-item__chip {
    width: 50px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__chip {
    width: 40px;
  }
}
.card-item__type {
  height: 45px;
  position: relative;
  display: flex;
  justify-content: flex-end;
  max-width: 100px;
  margin-left: auto;
  width: 100%;
}
@media screen and (max-width: 480px) {
  .card-item__type {
    height: 40px;
    max-width: 90px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__type {
    height: 30px;
  }
}
.card-item__typeImg {
  max-width: 100%;
  object-fit: contain;
  max-height: 100%;
  object-position: top right;
}
.card-item__info {
  color: #fff;
  width: 100%;
  max-width: calc(100% - 85px);
  padding: 10px 15px;
  font-weight: 500;
  display: block;
  cursor: pointer;
}
@media screen and (max-width: 480px) {
  .card-item__info {
    padding: 10px;
  }
}
.card-item__holder {
  opacity: 0.7;
  font-size: 13px;
  margin-bottom: 6px;
}
@media screen and (max-width: 480px) {
  .card-item__holder {
    font-size: 12px;
    margin-bottom: 5px;
  }
}
.card-item__wrapper {
  font-family: "Source Code Pro", monospace;
  padding: 25px 15px;
  position: relative;
  z-index: 4;
  height: 100%;
  text-shadow: 7px 6px 10px rgba(14, 42, 90, 0.8);
  user-select: none;
}
@media screen and (max-width: 480px) {
  .card-item__wrapper {
    padding: 20px 10px;
  }
}
.card-item__name {
  font-size: 18px;
  line-height: 1;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  text-transform: uppercase;
}
@media screen and (max-width: 480px) {
  .card-item__name {
    font-size: 16px;
  }
}
.card-item__nameItem {
  display: inline-block;
  min-width: 8px;
  position: relative;
}
.card-item__number {
  font-weight: 500;
  line-height: 1;
  color: #fff;
  font-size: 27px;
  margin-bottom: 35px;
  display: inline-block;
  padding: 10px 15px;
  cursor: pointer;
}
@media screen and (max-width: 480px) {
  .card-item__number {
    font-size: 21px;
    margin-bottom: 15px;
    padding: 10px 10px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__number {
    font-size: 19px;
    margin-bottom: 10px;
    padding: 10px 10px;
  }
}
.card-item__numberItem {
  width: 16px;
  display: inline-block;
}
.card-item__numberItem.-active {
  width: 30px;
}
@media screen and (max-width: 480px) {
  .card-item__numberItem {
    width: 13px;
  }
  .card-item__numberItem.-active {
    width: 16px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__numberItem {
    width: 12px;
  }
  .card-item__numberItem.-active {
    width: 8px;
  }
}
.card-item__content {
  color: #fff;
  display: flex;
  align-items: flex-start;
}
.card-item__date {
  flex-wrap: wrap;
  font-size: 18px;
  margin-left: auto;
  padding: 10px;
  display: inline-flex;
  width: 80px;
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
}
@media screen and (max-width: 480px) {
  .card-item__date {
    font-size: 16px;
  }
}
.card-item__dateItem {
  position: relative;
}
.card-item__dateItem span {
  width: 22px;
  display: inline-block;
}
.card-item__dateTitle {
  opacity: 0.7;
  font-size: 13px;
  padding-bottom: 6px;
  width: 100%;
}
@media screen and (max-width: 480px) {
  .card-item__dateTitle {
    font-size: 12px;
    padding-bottom: 5px;
  }
}
.card-item__band {
  background: rgba(0, 0, 19, 0.8);
  width: 100%;
  height: 50px;
  margin-top: 30px;
  position: relative;
  z-index: 2;
}
@media screen and (max-width: 480px) {
  .card-item__band {
    margin-top: 20px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__band {
    height: 40px;
    margin-top: 10px;
  }
}
.card-item__cvv {
  text-align: right;
  position: relative;
  z-index: 2;
  padding: 15px;
}
.card-item__cvv .card-item__type {
  opacity: 0.7;
}
@media screen and (max-width: 360px) {
  .card-item__cvv {
    padding: 10px 15px;
  }
}
.card-item__cvvTitle {
  padding-right: 10px;
  font-size: 15px;
  font-weight: 500;
  color: #fff;
  margin-bottom: 5px;
}
.card-item__cvvBand {
  height: 45px;
  background: #fff;
  margin-bottom: 30px;
  text-align: right;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 10px;
  color: #1a3b5d;
  font-size: 18px;
  border-radius: 4px;
  box-shadow: 0px 10px 20px -7px rgba(32, 56, 117, 0.35);
}
@media screen and (max-width: 480px) {
  .card-item__cvvBand {
    height: 40px;
    margin-bottom: 20px;
  }
}
@media screen and (max-width: 360px) {
  .card-item__cvvBand {
    margin-bottom: 15px;
  }
}

.card-list {
  margin-bottom: -130px;
}
@media screen and (max-width: 480px) {
  .card-list {
    margin-bottom: -120px;
  }
}

.card-input {
  margin-bottom: 20px;
}
.card-input__label {
  font-size: 14px;
  margin-bottom: 5px;
  font-weight: 500;
  color: #1a3b5d;
  width: 100%;
  display: block;
  user-select: none;
}
.card-input__input {
  width: 100%;
  height: 50px;
  border-radius: 5px;
  box-shadow: none;
  border: 1px solid #ced6e0;
  transition: all 0.3s ease-in-out;
  font-size: 18px;
  padding: 5px 15px;
  background: none;
  color: #1a3b5d;
  font-family: "Source Sans Pro", sans-serif;
}
.card-input__input:hover, .card-input__input:focus {
  border-color: #3d9cff;
}
.card-input__input:focus {
  box-shadow: 0px 10px 20px -13px rgba(32, 56, 117, 0.35);
}
.card-input__input.-select {
  -webkit-appearance: none;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUxJREFUeNrM1sEJwkAQBdCsngXPHsQO9O5FS7AAMVYgdqAd2IGCDWgFnryLFQiCZ8EGnJUNimiyM/tnk4HNEAg/8y6ZmMRVqz9eUJvRaSbvutCZ347bXVJy/ZnvTmdJ862Me+hAbZCTs6GHpyUi1tTSvPnqTpoWZPUa7W7ncT3vK4h4zVejy8QzM3WhVUO8ykI6jOxoGA4ig3BLHcNFSCGqGAkig2yqgpEiMsjSfY9LxYQg7L6r0X6wS29YJiYQYecemY+wHrXD1+bklGhpAhBDeu/JfIVGxaAQ9sb8CI+CQSJ+QmJg0Ii/EE2MBiIXooHRQhRCkBhNhBcEhLkwf05ZCG8ICCOpk0MULmvDSY2M8UawIRExLIQIEgHDRoghihgRIgiigBEjgiFATBACAgFgghEwSAAGgoBCBBgYAg5hYKAIFYgHBo6w9RRgAFfy160QuV8NAAAAAElFTkSuQmCC");
  background-size: 12px;
  background-position: 90% center;
  background-repeat: no-repeat;
  padding-right: 30px;
}

.slide-fade-up-enter-active {
  transition: all 0.25s ease-in-out;
  transition-delay: 0.1s;
  position: relative;
}

.slide-fade-up-leave-active {
  transition: all 0.25s ease-in-out;
  position: absolute;
}

.slide-fade-up-enter {
  opacity: 0;
  transform: translateY(15px);
  pointer-events: none;
}

.slide-fade-up-leave-to {
  opacity: 0;
  transform: translateY(-15px);
  pointer-events: none;
}

.slide-fade-right-enter-active {
  transition: all 0.25s ease-in-out;
  transition-delay: 0.1s;
  position: relative;
}

.slide-fade-right-leave-active {
  transition: all 0.25s ease-in-out;
  position: absolute;
}

.slide-fade-right-enter {
  opacity: 0;
  transform: translateX(10px) rotate(45deg);
  pointer-events: none;
}

.slide-fade-right-leave-to {
  opacity: 0;
  transform: translateX(-10px) rotate(45deg);
  pointer-events: none;
}

.github-btn {
  position: absolute;
  right: 40px;
  bottom: 50px;
  text-decoration: none;
  padding: 15px 25px;
  border-radius: 4px;
  box-shadow: 0px 4px 30px -6px rgba(36, 52, 70, 0.65);
  background: #24292e;
  color: #fff;
  font-weight: bold;
  letter-spacing: 1px;
  font-size: 16px;
  text-align: center;
  transition: all 0.3s ease-in-out;
}
@media screen and (min-width: 500px) {
  .github-btn:hover {
    transform: scale(1.1);
    box-shadow: 0px 17px 20px -6px rgba(36, 52, 70, 0.36);
  }
}
@media screen and (max-width: 700px) {
  .github-btn {
    position: relative;
    bottom: auto;
    right: auto;
    margin-top: 20px;
  }
  .github-btn:active {
    transform: scale(1.1);
    box-shadow: 0px 17px 20px -6px rgba(36, 52, 70, 0.36);
  }
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
 <div class="wrapper" id="app">
    <div class="card-form">
      <div class="card-list">
        <div class="card-item" v-bind:class="{ '-active' : isCardFlipped }">
          <div class="card-item__side -front">
            <div class="card-item__focus" v-bind:class="{'-active' : focusElementStyle }" v-bind:style="focusElementStyle" ref="focusElement"></div>
            <div class="card-item__cover">
              <img
              v-bind:src="'https://raw.githubusercontent.com/muhammederdem/credit-card-form/master/src/assets/images/' + currentCardBackground + '.jpeg'" class="card-item__bg">
            </div>
            
            <div class="card-item__wrapper">
              <div class="card-item__top">
                <img src="https://raw.githubusercontent.com/muhammederdem/credit-card-form/master/src/assets/images/chip.png" class="card-item__chip">
                <div class="card-item__type">
                  <transition name="slide-fade-up">
                    <img v-bind:src="'https://raw.githubusercontent.com/muhammederdem/credit-card-form/master/src/assets/images/' + getCardType + '.png'" v-if="getCardType" v-bind:key="getCardType" alt="" class="card-item__typeImg">
                  </transition>
                </div>
              </div>
              <label for="cardNumber" class="card-item__number" ref="cardNumber">
                <template v-if="getCardType === 'amex'">
                 <span v-for="(n, $index) in amexCardMask" :key="$index">
                  <transition name="slide-fade-up">
                    <div
                      class="card-item__numberItem"
                      v-if="$index > 4 && $index < 14 && cardNumber.length > $index && n.trim() !== ''"
                    >*</div>
                    <div class="card-item__numberItem"
                      :class="{ '-active' : n.trim() === '' }"
                      :key="$index" v-else-if="cardNumber.length > $index">
                      {{cardNumber[$index]}}
                    </div>
                    <div
                      class="card-item__numberItem"
                      :class="{ '-active' : n.trim() === '' }"
                      v-else
                      :key="$index + 1"
                    >{{n}}</div>
                  </transition>
                </span>
                </template>

                <template v-else>
                  <span v-for="(n, $index) in otherCardMask" :key="$index">
                    <transition name="slide-fade-up">
                      <div
                        class="card-item__numberItem"
                        v-if="$index > 4 && $index < 15 && cardNumber.length > $index && n.trim() !== ''"
                      >*</div>
                      <div class="card-item__numberItem"
                        :class="{ '-active' : n.trim() === '' }"
                        :key="$index" v-else-if="cardNumber.length > $index">
                        {{cardNumber[$index]}}
                      </div>
                      <div
                        class="card-item__numberItem"
                        :class="{ '-active' : n.trim() === '' }"
                        v-else
                        :key="$index + 1"
                      >{{n}}</div>
                    </transition>
                  </span>
                </template>
              </label>
              <div class="card-item__content">
                <label for="cardName" class="card-item__info" ref="cardName">
                  <div class="card-item__holder">Card Holder</div>
                  <transition name="slide-fade-up">
                    <div class="card-item__name" v-if="cardName.length" key="1">
                      <transition-group name="slide-fade-right">
                        <span class="card-item__nameItem" v-for="(n, $index) in cardName.replace(/\s\s+/g, ' ')" v-if="$index === $index" v-bind:key="$index + 1">{{n}}</span>
                      </transition-group>
                    </div>
                    <div class="card-item__name" v-else key="2">Full Name</div>
                  </transition>
                </label>
                <div class="card-item__date" ref="cardDate">
                  <label for="cardMonth" class="card-item__dateTitle">Expires</label>
                  <label for="cardMonth" class="card-item__dateItem">
                    <transition name="slide-fade-up">
                      <span v-if="cardMonth" v-bind:key="cardMonth">{{cardMonth}}</span>
                      <span v-else key="2">MM</span>
                    </transition>
                  </label>
                  /
                  <label for="cardYear" class="card-item__dateItem">
                    <transition name="slide-fade-up">
                      <span v-if="cardYear" v-bind:key="cardYear">{{String(cardYear).slice(2,4)}}</span>
                      <span v-else key="2">YY</span>
                    </transition>
                  </label>
                </div>
              </div>
            </div>
          </div>
          <div class="card-item__side -back">
            <div class="card-item__cover">
              <img
              v-bind:src="'https://raw.githubusercontent.com/muhammederdem/credit-card-form/master/src/assets/images/' + currentCardBackground + '.jpeg'" class="card-item__bg">
            </div>
            <div class="card-item__band"></div>
            <div class="card-item__cvv">
                <div class="card-item__cvvTitle">CVV</div>
                <div class="card-item__cvvBand">
                  <span v-for="(n, $index) in cardCvv" :key="$index">
                    *
                  </span>

              </div>
                <div class="card-item__type">
                    <img v-bind:src="'https://raw.githubusercontent.com/muhammederdem/credit-card-form/master/src/assets/images/' + getCardType + '.png'" v-if="getCardType" class="card-item__typeImg">
                </div>
            </div>
          </div>
        </div>
      </div>
      <div class="card-form__inner">
        <div class="card-input">
          <label for="cardNumber" class="card-input__label">Card Number</label>
          <input type="text" id="cardNumber" class="card-input__input" v-mask="generateCardNumberMask" v-model="cardNumber" v-on:focus="focusInput" v-on:blur="blurInput" data-ref="cardNumber" autocomplete="off">
        </div>
        <div class="card-input">
          <label for="cardName" class="card-input__label">Card Holders</label>
          <input type="text" id="cardName" class="card-input__input" v-model="cardName" v-on:focus="focusInput" v-on:blur="blurInput" data-ref="cardName" autocomplete="off">
        </div>
        <div class="card-form__row">
          <div class="card-form__col">
            <div class="card-form__group">
              <label for="cardMonth" class="card-input__label">Expiration Date</label>
              <select class="card-input__input -select" id="cardMonth" v-model="cardMonth" v-on:focus="focusInput" v-on:blur="blurInput" data-ref="cardDate">
                <option value="" disabled selected>Month</option>
                <option v-bind:value="n < 10 ? '0' + n : n" v-for="n in 12" v-bind:disabled="n < minCardMonth" v-bind:key="n">
                    {{n < 10 ? '0' + n : n}}
                </option>
              </select>
              <select class="card-input__input -select" id="cardYear" v-model="cardYear" v-on:focus="focusInput" v-on:blur="blurInput" data-ref="cardDate">
                <option value="" disabled selected>Year</option>
                <option v-bind:value="$index + minCardYear" v-for="(n, $index) in 12" v-bind:key="n">
                    {{$index + minCardYear}}
                </option>
              </select>
            </div>
          </div>
          <div class="card-form__col -cvv">
            <div class="card-input">
              <label for="cardCvv" class="card-input__label">CVV</label>
              <input type="text" class="card-input__input" id="cardCvv" v-mask="'####'" maxlength="4" v-model="cardCvv" v-on:focus="flipCard(true)" v-on:blur="flipCard(false)" autocomplete="off">
            </div>
          </div>
        </div>

        <button class="card-form__button">
          Submit
        </button>
      </div>
    </div>
    
     
  </div>
KING-CODE
  • 39
  • 5
0

This should work :

const handleKeyup=(value)=>{
  //Remove whitespace
 let newValue = value.split(" ").join("")

 var format =newValue.split("").map((data, index) => {

  if ((index + 1) % 4 == 0) {
    data = data + " "
  }

  return data
})

format= format.join("")
console.log("format", format)}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 12 '22 at 19:05
0

This should work:

var format = [9, 2, 3,5,5,5,5,5,5,5,4, 5, 5, 5, 54, 4, 4, 4, 4, 4, 4,4,4, 4].map((data, index) => {
 if ((index + 1) % 4 == 0) {
data = data + " "
 }

 return data
 })

format= format.join("")
 console.log("format", format)
0

This will do the job

$('#card-number').on('keypress change blur', function () {
    $(this).val(function (index, value) {
        
        var trimValue = value.trim();
        var cardDivider = trimValue.replace(/ /g,'').length % 4;
    
        if (trimValue.length < 19 && trimValue !== "") {
            if (cardDivider === 0) {
                return trimValue + " ";
            }
        }
        
        return trimValue;
    });
});
cross_flame
  • 840
  • 2
  • 11
  • 24
0

just learned js for 15 days, and encountered the same problem doing a project.

here is my absolutely rubbish solution, it's js only, don't know why even mention that since I don't know what jquery, etc means.

however, it works.

ps: feel free to judge.

const number = document.getElementById("card-number");

//get the new array every time there is
//an input in the field
let listened_number = [];

//put spaces in the array
function number_format(){
  listened_number.splice(4,0," ");
  listened_number.splice(9,0," ");
  listened_number.splice(14,0," ");
}

//"input" type, the function gets activated every time something is entered or deleted.
number.addEventListener("input",  e => {

//update the array
  listened_number = number.value.replace(/\s+/g,"").split('');

// input caret position before any changes
// 'variable' represents the action to be 
//applied on the caret later on
  let caret_pos = number.selectionStart;
  let variable = 0;
  
  if(e.data === null){
    variable = -1;
  }else{
    variable = 1;
  }

// add spaces into the array.
  number_format();
  
// reduced together but trimmed
  number.value = listened_number.reduce((pv, cv) => pv + cv).trim();
  
//!!!!
//because the number.value(content in the input
//field) is reassigned, the input caret will appear
//at the very end, which is not user-friendly at all
//!!!!
  switch(caret_pos){
    case 5:
    setSelection(5 + variable);
    break;
    case 10:
    setSelection(10 + variable);
    break;
    case 15:
    setSelection(15 + variable);
    break;
    default:
      setSelection(caret_pos);
  }
})

// set the caret where it supposes to be.
function setSelection(caretPos){
  number.setSelectionRange(caretPos,caretPos);
  number.focus();
}
      <input maxlength="19" id="card-number" type="text" placeholder="e.g. 1234 5678 9123 0000">  

it's messy... i know.

DaY
  • 1
0

I found everything above never worked so I wrote a new one for fomatting to

0000 0000 0000 0000

 //JS credit card formatter for onChange handler
 
 "97181237removed12891237192random3712".replace(/[\D]/g, '').match(/.{1,4}/g)?.join(' ').substring(0, 19) || '';
 
 // '9718 1237 1289 1237'

https://gist.github.com/zakcroft/5c045ebbfa0d3e4aacc4d21fe0196ffa

Zak Croft
  • 16
  • 2
-1

Format credit card number will be 16 digits and having automatic spacing between them will get by trying the below code for me. try it once

handlecard(text) {
    let formattedText = text.split(' ').join('');
    if (formattedText.length <= 16) {
      if (formattedText.length > 0) {
        formattedText = formattedText.match(new RegExp('.{1,4}', 'g')).join(' ');
      }
    } else {
      alert("plz stop here")
    }
    this.setState({ creditCard: formattedText });
    return formattedText;
  }
ravi
  • 370
  • 4
  • 11