41

I want insert a dash after every 4th character in input. I have a credit card input box. When a user is typing and reaches each 4th character, then jQuery will insert a hyphen (-).

For example: 1234-5678-1234-1231

UPDATE: I'm trying some codes and i think i'm so close to correct code but i have some problems. Here is my code sample;

$('.creditCardText').keyup(function() {

var cardValue = $('.creditCardText').val(),
    cardLength = cardValue.length;

if ( cardLength < 5 ) {
    if ( cardLength % 4 == 0 ) {
        console.log('4 lük geldi');
        cardValue += "-";
        $('.creditCardText').val(cardValue);
    }
} else {
    if ( cardLength % 5 == 0 ) {
        console.log('5 lük geldi');
        cardValue += "-";
        $('.creditCardText').val(cardValue);

    }
}

});
mrsus
  • 5,446
  • 4
  • 31
  • 44
mrchad
  • 799
  • 1
  • 9
  • 18
  • 3
    Use four different input fields and combine them server-side. Doing it client-side is not worth the trouble and can confuse your users when they're not expecting it. – Blazemonger Jul 24 '12 at 14:01
  • Have you tried anything? Do you have code? – Ates Goral Jul 24 '12 at 14:02
  • 1
    @Blazemonger, I'm not a UX guy, but the sites that pull this off with placeholders certainly don't bother me. It is possible to do without irritating your users, I think. – Brad Jul 24 '12 at 14:03
  • Or use a single input, and strip out dashes / insert them server side. – David B Jul 24 '12 at 14:03
  • 4
    Not all Credit Cards are 4-4-4-4...... – Ohgodwhy Jul 24 '12 at 14:04
  • @Brad the usual approach is to add the dashes AFTER the user leaves the field (`.on('blur', ...)`), not while the user is still typing. This is better for a number of reasons. – Blazemonger Jul 24 '12 at 14:06
  • @Blazemonger, Can you elaborate? I'd like to learn the problem with having those dashes there already in place while the user types. (I'm referring to the sites where there is one text box with blanks in places of numbers, with dashes in place. I understand the confusion that occurs when unexpected things appear later.) – Brad Jul 24 '12 at 14:17
  • @Blazemonger it was previously, but now have changed. Because some credit card have different character size. Some has 16, some has 24 character. – mrchad Jul 24 '12 at 14:34
  • @AtesGoral `$('.creditCardText').keyup(function() { if ( $(this).val().length == 4 || $(this).val().length == 8 ) { $(this).val().join(', '); } })` – mrchad Jul 24 '12 at 14:57
  • @Brad The problems come when the user tries to delete the dashes, and can't; or when he tries to add his own dashes or spaces, and the programmer wasn't expecting it. Generally, it "feels rude" to edit someone's text while they're still typing it. If you're going to re-write my text entry, wait until I've had a chance to rewrite it myself. – Blazemonger Jul 24 '12 at 15:12
  • It's usually disorienting for users when their raw input is tampered with. Allow the users to enter the numbers either with dashes, spaces or nothing at all (remember that they could be copy/pasting an already typed in number that could be in some other form). Do your normalization transparently and process the data in the form you like. – Ates Goral Jul 24 '12 at 15:47

8 Answers8

28

I absolutely love this plugin for automatic formatting: here.

So long as you're already using JQuery, that is.

You could easily force the dashes in with a single line of code, like follows:

$("#credit").mask("9999-9999-9999-9999");

When the user types in the field, the dashes will automatically appear in the right spot, and they will not be able to delete them.

In addition, you can accommodate for different lengths or formats of credit cards with the ? character in your mask. For example, to accept inputs of 14 and 16 digits, you would do the following:

$("#credit").mask("9999-9999-9999-99?99");

Do keep in mind that this is only a client side validation


Edit: The mask plugin assumes that there is one, or finitely many, correct formats for the field. For example, there are only a few formats that credit card numbers come in. The plugin is there to ensure that your input will only be in one of those formats.

So technically, if you want a dash after every four digits, but for any number of digits, then this plugin is not right for you.

I would suggest you restrict the possible inputs to be reasonable, as there is certainly no such thing as a 1000-digit long credit card. But if you really want that functionality, you'll have to write the script yourself or find another plugin. As of this time I'm not aware of one.

Nick
  • 8,964
  • 4
  • 26
  • 37
  • 2
    Someone mentioned that not all credit cards are the same format. You can also specify optional characters too, so to accomodate 4-4-4-2 you could write `$("#credit").mask("9999-9999-9999-99?99");` – Nick Jul 24 '12 at 14:05
  • I was tried that but not enough for me. Because not all credit cards are the same format. Input area has 24 character and someone just put 18 character and it is enough for them. But someone have to 24 character. Is that a problem for this plugin? – mrchad Jul 24 '12 at 14:31
  • @mrchad I'm not sure I understand, but you can specify that the input must be of different lengths, yes. Can you give an example? Maybe update your question with the different rules you want to be in place? If you show me what you want it to allow and not allow, I can help you write the mask – Nick Jul 24 '12 at 14:43
  • I just want when a user is typing and reaches each 4th character, then jQuery will insert a hyphen. That's it no more, no less. – mrchad Jul 24 '12 at 14:54
  • @mrchad I understand now. I'll update my answer to accommodate. – Nick Jul 24 '12 at 14:59
  • Thanks!. When i'm setting a value to the text box, the mask not applied.. Any Suggestion plz? And when i click inside the text box the value is gone.. – Aruna Feb 27 '14 at 06:40
21

I've fixed up your code, but still strongly suggest server validation and using four text boxes, and smartly switching between them:

$('.creditCardText').keyup(function() {
  var foo = $(this).val().split("-").join(""); // remove hyphens
  if (foo.length > 0) {
    foo = foo.match(new RegExp('.{1,4}', 'g')).join("-");
  }
  $(this).val(foo);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="text" class="creditCardText" />

View on jsFiddle

showdev
  • 28,454
  • 37
  • 55
  • 73
Lucas
  • 16,930
  • 31
  • 110
  • 182
  • 1
    I upvoted because this is a quick solution, but you need an 'if' statement before calling .join("-") in the third line. If you don't, when you only have one character left in the input field and you delete it, your code calls .match which returns null and then you immediately call .join on that null value, which results in a javascript error. Please update your answer. Other than that, this is a working solution. – mkralla11 Nov 11 '13 at 00:32
  • @Mike.MKrallaProductions Thanks. Is the edit I just made what you mean? – Lucas Nov 11 '13 at 02:14
  • @think123, I tried your code to add dashes after every 4 digit in credit card but it fails if user want to delete specific digit like 3rd or 2nd. And also digit is not entered at a specific place like i want to add digit at 7th place it always append from last. – Megha Dec 18 '13 at 11:50
  • @Megha Yes, that's true. I personally would also recommend Nick's answer above, but in the case of the plugin not being able to be used, this would be a dirty, bad, but still reasonably usable solution. – Lucas Dec 18 '13 at 22:42
  • 1
    You can improve it a little bit by only setting the value if there was a change. This will make arrows, Shift-Home and stuff like that work. Add as first line in the function: `var curr = $jq(this).val();`. Replace the last line in the function with: `if (curr != foo) $jq(this).val(foo);` – TheStoryCoder Aug 23 '14 at 21:47
  • My text is 89050342 . i want to make it like 89-05-0342 . Can you please suggest some regex for it ? – Viku Mar 20 '15 at 08:40
  • 1
    @vivek sure! use this: `mytext.replace(/(\d{2})(\d{2})(\d{4})/, "$1-$2-$3");` – Lucas Mar 21 '15 at 06:06
  • @vivek I also found you a link that *may* be able to help you more: http://stackoverflow.com/questions/6981487/insert-hyphens-in-javascript – Lucas Mar 21 '15 at 06:08
  • @think123 i want to do the same in the keyup function as in your answer . i modified the regex like foo = foo.match(new RegExp('.{1,2}', 'g')).join("-"); but it inserting after every 2 characters . My text is 89050342 . i want to make it like 89-05-0342 on keyup function . i mean autodash inserter . Please help . – Viku Mar 21 '15 at 08:40
  • @vivek yeah, I'm pretty sure you can just use the code I highlighted in my answer, and replace line 4 with like `foo = foo.replace(/(\d{2})(\d{2})(\d{4})/, "$1-$2-$3");` - pretty sure it will work for you ;) – Lucas Mar 22 '15 at 07:50
  • @Yvette Glad I could help! – Lucas Jul 26 '16 at 00:17
  • Ctrl+A / Ctrl+Home / Ctrl+End and the Mac variants do not work. This code will automatically deselect your select all – zanderwar Nov 21 '16 at 03:33
  • thanks @think123 you should implement this into your code above if you wanted to make this answer more foolproof :D – zanderwar Nov 21 '16 at 04:41
  • 1
    @zanderwar I'll have a look later. Just tested it and found a few logic bugs in the link, you may want to do the same. I'll fix it up and update the answer in due time :) – Lucas Nov 21 '16 at 05:56
18

Based on @think123 answer, in vanilla JS, without JQuery:

document.querySelector('.creditCardText').addEventListener('input', function(e) {
  var foo = this.value.split("-").join("");
  if (foo.length > 0) {
    foo = foo.match(new RegExp('.{1,4}', 'g')).join("-");
  }
  this.value = foo;
});
<input class="creditCardText" type="text">

I know the question is about JQuery but I think this answer could help too.

showdev
  • 28,454
  • 37
  • 55
  • 73
rap-2-h
  • 30,204
  • 37
  • 167
  • 263
  • This helped me with a React project where I'm trying to stay away from jQuery. I'm using this since I'm trying to stay away from masked input components in npm. For my case, I don't need those since I'm dynamically tracking two types of strings on one input. – Martavis P. Jul 16 '17 at 00:50
7

If you are looking for a pure Javascript solution, look at my function below. It supports the American Express format (15 digits) as well as Visa, MasterCard and others (16 digits).

Watch out for the simple solutions that will replace the whole value and always put the focus at the end of the input: it can be annoying if the user edits what he previously entered.

input_credit_card = function(input) {
  var format_and_pos = function(char, backspace) {
    var start = 0;
    var end = 0;
    var pos = 0;
    var separator = " ";
    var value = input.value;

    if (char !== false) {
      start = input.selectionStart;
      end = input.selectionEnd;

      if (backspace && start > 0) // handle backspace onkeydown
      {
        start--;

        if (value[start] == separator) {
          start--;
        }
      }
      // To be able to replace the selection if there is one
      value = value.substring(0, start) + char + value.substring(end);

      pos = start + char.length; // caret position
    }

    var d = 0; // digit count
    var dd = 0; // total
    var gi = 0; // group index
    var newV = "";
    var groups = /^\D*3[47]/.test(value) ? // check for American Express
      [4, 6, 5] : [4, 4, 4, 4];

    for (var i = 0; i < value.length; i++) {
      if (/\D/.test(value[i])) {
        if (start > i) {
          pos--;
        }
      } else {
        if (d === groups[gi]) {
          newV += separator;
          d = 0;
          gi++;

          if (start >= i) {
            pos++;
          }
        }
        newV += value[i];
        d++;
        dd++;
      }
      if (d === groups[gi] && groups.length === gi + 1) // max length
      {
        break;
      }
    }
    input.value = newV;

    if (char !== false) {
      input.setSelectionRange(pos, pos);
    }
  };

  input.addEventListener('keypress', function(e) {
    var code = e.charCode || e.keyCode || e.which;

    // Check for tab and arrow keys (needed in Firefox)
    if (code !== 9 && (code < 37 || code > 40) &&
      // and CTRL+C / CTRL+V
      !(e.ctrlKey && (code === 99 || code === 118))) {
      e.preventDefault();

      var char = String.fromCharCode(code);

      // if the character is non-digit
      // OR
      // if the value already contains 15/16 digits and there is no selection
      // -> return false (the character is not inserted)

      if (/\D/.test(char) || (this.selectionStart === this.selectionEnd &&
          this.value.replace(/\D/g, '').length >=
          (/^\D*3[47]/.test(this.value) ? 15 : 16))) // 15 digits if Amex
      {
        return false;
      }
      format_and_pos(char);
    }
  });

  // backspace doesn't fire the keypress event
  input.addEventListener('keydown', function(e) {
    if (e.keyCode === 8 || e.keyCode === 46) // backspace or delete
    {
      e.preventDefault();
      format_and_pos('', this.selectionStart === this.selectionEnd);
    }
  });

  input.addEventListener('paste', function() {
    // A timeout is needed to get the new value pasted
    setTimeout(function() {
      format_and_pos('');
    }, 50);
  });

  input.addEventListener('blur', function() {
    // reformat onblur just in case (optional)
    format_and_pos(this, false);
  });
};

input_credit_card(document.getElementById('credit-card'));
<form action="" method="post">
  <fieldset>
    <legend>Payment</legend>
    <div>
      <label for="credit-card">Credit card</label>
      <input id="credit-card" type="text" autocomplete="off" />
    </div>
  </fieldset>
</form>

View on jsFiddle

showdev
  • 28,454
  • 37
  • 55
  • 73
pmrotule
  • 9,065
  • 4
  • 50
  • 58
3

By modifying the suggestion of @think123 and @TheStoryCoder, I added selectionStart and SelectionEnd for cursor. After that increase cursor position at suitable place and modify the cursor position. It should solve the cursor position is at not as expected position.

$('.creditCardText').keyup(function() {

  var ss, se, obj;
  obj = $(this);
  ss = obj[0].selectionStart;
  se = obj[0].selectionEnd;

  var curr = obj.val();

  var foo = $(this).val().split("-").join(""); // remove hyphens
  if (foo.length > 0) {
    foo = foo.match(new RegExp('.{1,4}', 'g')).join("-");
  }

  if (((curr.length % 5 == 0) && ss == se && ss == curr.length) || (ss == se && (ss % 5 == 0))) {
    ss += 1;
    se += 1;
  }

  if (curr != foo) {
    $(this).val(foo);
    obj[0].selectionStart = ss;
    obj[0].selectionEnd = se;
  }

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="creditCardText" type="text">
showdev
  • 28,454
  • 37
  • 55
  • 73
Su Ming Yuan
  • 125
  • 12
2

Most easiest way is the following using simple javascript onkey and function... it will put a dash hyphen after every 3 characters you input/type.

function addDash(element) {
  let ele = document.getElementById(element.id);
  ele = ele.value.split('-').join(''); // Remove dash (-) if mistakenly entered.

  let finalVal = ele.match(/.{1,3}/g).join('-');
  document.getElementById(element.id).value = finalVal;
}
<input type="text" class="form-control" name="sector" id="sector" onkeyup="addDash(this)" required>
showdev
  • 28,454
  • 37
  • 55
  • 73
Hassan Qasim
  • 463
  • 5
  • 5
1

All others who have answered above me are right and their code is definitely short and neat, but is a bit advance, in the sense that they are either using regular expressions or a plugin. Something like this can also be achieved with basic js/jquery, which, judging from your sample code you are trying to achieve. As this question is around 3 years old, you must've gotten what you had wanted by now, but yes, you were close.. This is what you should have tried:

$('.creditCardText').keyup(function() {
  var cctlength = $(this).val().length; // get character length

  switch (cctlength) {
    case 4:
      var cctVal = $(this).val();
      var cctNewVal = cctVal + '-';
      $(this).val(cctNewVal);
      break;
    case 9:
      var cctVal = $(this).val();
      var cctNewVal = cctVal + '-';
      $(this).val(cctNewVal);
      break;
    case 14:
      var cctVal = $(this).val();
      var cctNewVal = cctVal + '-';
      $(this).val(cctNewVal);
      break;
    default:
      break;
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<input type="text" class="creditCardText" maxlength="19" />

JSFiddle Link

showdev
  • 28,454
  • 37
  • 55
  • 73
Brajinder Singh
  • 159
  • 3
  • 8
0

You can achieved with vanilla JS and do not require plugin to do that. Refer to the code below. You can change the var split to suit your needs. Credits to https://webdesign.tutsplus.com/tutorials/auto-formatting-input-value--cms-26745.

(function($, undefined) {

    "use strict";

    // When ready.
    $(function() {
        
        var $form = $( "#form" );
        var $input = $form.find( "input" );

        $input.on( "keyup", function( event ) {
            
            console.log('sss')
            // When user select text in the document, also abort.
            var selection = window.getSelection().toString();
            if ( selection !== '' ) {
                return;
            }
            
            // When the arrow keys are pressed, abort.
            if ( $.inArray( event.keyCode, [38,40,37,39] ) !== -1 ) {
                return;
            }
            
            var $this = $(this);
            var input = $this.val();
                    input = input.replace(/[\W\s\._\-]+/g, '');
                
                var split = 4;
                var chunk = [];
               
                for (var i = 0, len = input.length; i < len; i += split) {                              chunk.push( input.substr( i, split ) );
                }

                 console.log(chunk)
                $this.val(function() {
                    return chunk.join("-");
                });
        
        } );
        
        /**
         * ==================================
         * When Form Submitted
         * ==================================
         */
        $form.on( "submit", function( event ) {
            
            var $this = $( this );
            var arr = $this.serializeArray();
        
            for (var i = 0; i < arr.length; i++) {
                    arr[i].value = arr[i].value.replace(/[($)\s\._\-]+/g, ''); // Sanitize the values.
            };
            
            console.log( arr );
            
            event.preventDefault();
        });
        
    });
})(jQuery);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="form" method="post" action="">

        <label for="number">Enter number</label>
        <div class="flex">
            <input id="number" name="number" type="text" maxlength="15" />
        </div>

</form>
tnkh
  • 1,749
  • 2
  • 14
  • 30