How to Optimize big Data Tables VueJS - javascript

I have this table that I'm using to intelligently render over 4000 records, but I want to know how to do it in a way that there is no lag whatsoever. I did a performance recording using so I could see how the memory was being used and I ended up with this.
This is actually much more optimized than what I had before and I'm achieving this by conditionally rendering all but the first td in each row in order to maintain height for scrolling, but also to reduce the amount of nodes that are being iterated over. Is there any way that I can optimize this further?
Here is my Vue Template
<template>
<div class="table-container" >
<div class="table-header" ref="tableHead">
<table class="data-table" border="0">
<thead>
<tr>
<th
v-for="(head, index) in contents.order"
:key="index"
class="col"
:class="contents.stickyCols.indexOf(head) === 0 ? 'sticky-left' : contents.stickyCols.indexOf(head) === 1 ? 'sticky-right' : ''"> {{ head }}
</th>
</tr>
</thead>
</table>
</div>
<div class="table-body" ref="tableBody" #scroll="changeScrollVal" :style="'height: ' + height + ';'">
<table id="bodytable" width="100%" cellpadding="5" cellspacing="5">
<tbody>
<tr
v-for="(row, idx) in theData"
:key="idx">
<td
:class="contents.stickyCols.indexOf(key) === 0 ? 'sticky-left' : contents.stickyCols.indexOf(key) === 1 ? 'sticky-right' : ''"
:style="contents.colorCols.indexOf(key) > -1 ? 'color:' + contents.colorRefs[row[contents.colorRefs.label]] : '' "
class="body-cell col"
v-for="(key, index) in contents.order"
v-if="idx >= (scrollBottom / 6 - 100 < 0 ? 0 : scrollBottom / 6 - 100) && idx <= scrollBottom - 1 || key === contents.order[0]"
:key="index"
>{{ row[key] }}
</td>
</tr>
</tbody>
<!-- > {{ row[key] }} -->
</table>
</div>
</div>
<script>
export default {
props: ['contents', 'dashData', 'height'],
data () {
return {
theData: [],
scrollBottom: 100
}
},
methods: {
changeScrollVal (e) {
this.$refs.tableHead.scrollLeft = e.target.scrollLeft
this.scrollBottom = Math.floor(this.$refs.tableBody.scrollTop / 9 < 100 ? 100 : this.$refs.tableBody.scrollTop / 9 > this.dashData.length - 1 ? this.dashData.length - 1 : this.$refs.tableBody.scrollTop / 9)
this.theData = this.dashData.slice(0, this.scrollBottom)
}
},
mounted () {
this.theData = this.dashData// .slice(0, 17)
}
}
</script>
<style lang="scss">
table
{
border-collapse: collapse;
}
td, th {
border-bottom: 1px solid rgba(0,0,0,0.2);
margin-bottom: 2px;
}
.sticky-right {
position: sticky;
background: white;
right: 0;
padding-left: 20px!important;
}
.sticky-left {
position: sticky;
background: white;
left: 0;
}
.outer-container
{
background-color: #ccc;
position: absolute;
top:0;
left: 0;
right: 300px;
bottom: 40px;
}
.inner-container
{
height: 100%;
overflow: hidden;
}
.table-header
{
position: relative;
&::-webkit-scrollbar {
display: none;
}
}
.table-body
{
overflow: scroll;
display:inline-block;
width: 79vw;
}
.table-header {
overflow: scroll;
width: 79vw;
}
th
{
background-color: white;
text-align: left;
height: 40px;
}
.body-cell
{
text-align: left;
color: rgba(0,0,0,0.6);
padding-top: 10px;
padding-bottom: 10px;
}
.col
{
width:120px;
min-width: 265px;
max-width: 265px;
padding-left: 0px;
padding-right: 0px;
font-weight: 500;
// background-color: white;
}
</style>
Thanks!

Related

How to load messages in the last hour only?

In the json file there are messages of this kind (in the "time" date in milliseconds)
$.getJSON('data/messages.json', callback);
callback([{
"time": "1499204237",
"user": "Max",
"message": "Hello"
}, {
"time": "1499204238",
"user": "Mike",
"message": "Hi"
}]);
function callback(respond) {
for (var i = 0; i < respond.length; i++) {
var data = respond[i];
var now = new Date();
var time = (data.time);
for(var j = 0; j < time.length; j++){
var time_diff = time[j] - now;
while(time_diff < 3600){
var date = new Date(+time * 1000);
var new_date = new Date(date);
var res = [new_date.getHours(), new_date.getMinutes(), new_date.getSeconds()].map(function (x) {
return x < 10 ? "0" + x : x;
}).join(":");
var rowClone = $('.mess_hide').clone().removeClass('mess_hide');
$('#messages').append(rowClone);
$('.time', rowClone).html(res);
$('.name', rowClone).html(data.user);
$('.message', rowClone).html(data.message);
$('.scroller').scrollTop($('#messages').height());
}
}
}
}
.scroller {
width: 490px;
height: 255px;
max-height: 255px;
overflow-y: auto;
overflow-x: hidden;
}
table#messages {
min-height: 260px;
width: 100%;
background: #fffecd;
border: none;
}
table#messages::-webkit-scrollbar {
width: 1em;
}
table#messages::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}
table#messages::-webkit-scrollbar-thumb {
background-color: darkgrey;
outline: 1px solid slategrey;
}
tr {
height: 20%;
display: block;
}
td.time,
td.name {
width: 70px;
max-width: 75px;
text-align: center;
}
td.name {
font-weight: bold;
}
form#text_submit {
display: inline-flex;
align-items: flex-start;
}
input#text {
width: 370px;
height: 30px;
margin-top: 20px;
background: #fffecd;
font-family: 'Montserrat';
font-size: 16px;
border: none;
align-self: flex-start;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="scroller">
<table id="messages">
<tr class="mess_hide">
<td class="time"></td>
<td class="name"></td>
<td class="message"></td>
</tr>
</table>
</div>
<form method="POST" id="easyForm">
<input type="text" name="text" id="text">
<input type="submit" value="Send" id="submit">
</form>
I need to download all the messages from the file and select the last of them (last hour).
In for loop want to select each "time" from all messages and count the difference with "now". So i need try to do that, but i thing my for loop is not correct.What's wrong?

How i can make Endless sliding of table content without white gap?

I want to make a sliding table that shows the rows from bottom to top contentiously without having a white gap at the beginning or end.
var my_time;
$(document).ready(function() {
setTimeout(function() {
}, 200);
pageScroll();
$("#contain").mouseover(function() {
clearTimeout(my_time);
}).mouseout(function() {
pageScroll();
});
});
function pageScroll() {
var objDiv = document.getElementById("contain");
objDiv.scrollTop = objDiv.scrollTop + 1;
$('p:nth-of-type(1)').html('scrollTop : ' + objDiv.scrollTop);
$('p:nth-of-type(2)').html('scrollHeight : ' + objDiv.scrollHeight);
if (objDiv.scrollTop == (objDiv.scrollHeight - 106)) {
objDiv.scrollTop = -50;
}
my_time = setTimeout('pageScroll()', 25);
}
body {
font-family: 'helvetica';
}
#contain {
height: 100px;
overflow-y: scroll;
}
#table_scroll {
width: 100%;
margin-top: 100px;
margin-bottom: 100px;
border-collapse: collapse;
}
#table_scroll thead th {
padding: 10px;
background-color: #ea922c;
color: #fff;
}
#table_scroll tbody td {
padding: 10px;
background-color: #ed3a86;
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="CC">
<table style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
<th>Company</th>
</tr>
</thead>
</table>
<div id="contain">
<table border="0" id="table_scroll">
<tbody>
<tr>
<td>User One</td>
<td>0123456789</td>
<td>Company1</td>
</tr>
<tr>
<td>User Two</td>
<td>000550050055</td>
<td>Company2</td>
</tr>
<tr>
<td>Another User</td>
<td>22221323123</td>
<td>Company3</td>
</tr>
<tr>
<td>Some more users.............</td>
<td>......................</td>
<td>...............</td>
</tr>
</tbody>
</table>
</div>
<p></p>
<p></p>
</div>
Currently its working fine, but there is a gap, i want it to be one after another without starting from the bottom or top, like rotating a ball.
hope my question is clear.
Thanks in advance!
I used another example for a different customer. Maybe you can use it:
/*
Ininite looping scroll.
Tested and works well in latest verions of Chrome, Safari and Firefox.
Not built/tested for mobile.
*/
'use strict';
var doc = window.document,
context = doc.getElementsByClassName('js-loop')[0],
clones = context.getElementsByClassName('is-clone'),
disableScroll,
scrollHeight,
scrollPos,
clonesHeight,
i;
function getScrollPos() {
return (context.pageYOffset || context.scrollTop) - (context.clientTop || 0);
}
function setScrollPos(pos) {
context.scrollTop = pos;
}
function getClonesHeight() {
clonesHeight = 0;
i = 0;
for (i; i < clones.length; i += 1) {
clonesHeight = clonesHeight + clones[i].offsetHeight;
}
return clonesHeight;
}
function reCalc() {
window.requestAnimationFrame(reCalc);
scrollPos = getScrollPos();
scrollHeight = context.scrollHeight;
clonesHeight = getClonesHeight();
if (scrollPos <= 0) {
setScrollPos(1); // Scroll 1 pixel to allow upwards scrolling
}
}
// Calculate variables
window.requestAnimationFrame(reCalc);
function scrollUpdate() {
if (!disableScroll) {
scrollPos = getScrollPos();
if (clonesHeight + scrollPos >= scrollHeight) {
// Scroll to the top when you’ve reached the bottom
setScrollPos(1); // Scroll down 1 pixel to allow upwards scrolling
disableScroll = true;
} else if (scrollPos <= 0) {
// Scroll to the bottom when you reach the top
setScrollPos(scrollHeight - clonesHeight);
disableScroll = true;
}
}
if (disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering
window.setTimeout(function () {
disableScroll = false;
}, 40);
}
}
context.addEventListener('scroll', function () {
window.requestAnimationFrame(scrollUpdate);
}, false);
window.addEventListener('resize', function () {
window.requestAnimationFrame(reCalc);
}, false);
// Just for the demo: Center the middle block on page load
window.onload = function () {
setScrollPos(Math.round(clones[0].getBoundingClientRect().top + getScrollPos() - (window.innerHeight - clones[0].offsetHeight) / 2));
};
html,
body {
height: 100%;
overflow: hidden;
}
.Loop {
position: relative;
height: 100%;
overflow: auto;
}
section {
position: relative;
text-align: center;
min-height: 300px;
max-height: 700px;
height: 80%;
}
.inner-wrap {
animation: autoscroll 10s linear infinite;
}
#keyframes autoscroll {
from { transform: translate3d(0,0,0); }
to { transform: translate3d(0,-75%,0); }
}
::scrollbar {
display: none;
}
body {
font-family: "Avenir Next", Helvetica, sans-serif;
font-weight: normal;
font-size: 100%;
}
.red {
background: #FF4136;
}
.green {
background: #2ECC40;
}
.blue {
background: #0074D9;
}
.orange {
background: rebeccapurple;
}
h1 {
margin: 0;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
font-size: 80px;
letter-spacing: 5px;
color: #fff;
text-transform: uppercase;
}
<main class="Loop js-loop">
<div class="inner-wrap">
<section class="green">
<h1>One</h1>
</section>
<section class="red">
<h1>For</h1>
</section>
<section class="blue">
<h1>All</h1>
</section>
<section class="orange">
<h1>And</h1>
</section>
<section class="blue">
<h1>All</h1>
</section>
<section class="red">
<h1>For</h1>
</section>
<!--
These blocks are the same as the first blocks to get that looping illusion going. You need to add clones to fill out a full viewport height.
-->
<section class="green is-clone">
<h1>One</h1>
</section>
<section class="red is-clone">
<h1>For</h1>
</section>
</div>
</main>

$.each() Adding Tab Spaces To Each Line Using repeat()

Brief
I'm working on my jQuery Syntax Highlight Script and I want to implement a beautifier which minifies the code then beautifies it by adding new lines and tab spaces where needed.
I'm working on the CSS section at the moment and I'm having trouble adding the tab spaces to each line between the { and }.
Live Examples:
> View On CodePen
> View Below
code = $(".input").html().trim();
if (code) {
code = code
// REMOVE TAB SPACES
.replace(/( )/gi,'')
// REMOVE NEW LINES
.replace(/[\n\r]/g,'')
// REMOVE SPACES BETWEEN SECTIONS
.replace(/(;|{|})(\s+)([^ \s+])/g,'$1$3')
.replace(/(;|{|})(\s+)([^ \s+])/g,'$1$3')
.replace(/(\*\/)(\s+)([^ \s+])/g,'$1$3');
}
$('.minified').text(code);
minified = $('.minified').html();
if (minified) {
minified = minified
.replace(/(\;|\}|\{)/gi,'$1\n\r')
.replace(/((\/\*)(| )([^"'\s\n]+)(| )(\*\/))/gi,'\n\r$1\n\r');
var level = 0;
var lines = minified.split("\n");
$.each(lines, function(n, elem) {
last = elem[elem.length -1]
if (last === "{") { level = level + 1; }
else if (last === "}") { level = level - 1; }
else {
var tab = " ".repeat(level);
elem = tab + elem;
}
});
}
$('.beautified').text(minified);
.wrap {
float:left;
width: 400px;
margin: 20px;
}
.wrap > h1 {
text-align: center;
}
pre {
float: left;
width:100%;;
height: 400px;
border: 1px solid #000000;
overflow: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="wrap">
<h1>INPUT</H1>
<pre class="input">
div.highlight {
background:#FFFFFF;
border:1px solid #E0E0E0;
font-family:"Courier New",Courier,monospace;
overflow: hidden;
}
div.highlight pre{
width: 100%;
overflow: auto;
padding:0;
margin:0;
font-size:13px;
clear: both;
}
/* tabs */
div.highlight ul.tabs {
overflow: hidden;
padding: 5px 0 5px 0;
margin: 0;
list-style: none;
border-bottom: 1px solid #E0E0E0;
width: 100%;
}
div.highlight ul.tabs li {
padding: 0;
margin: 0 5px;
float: left;
background: none;
border-bottom: 1px dashed #CCC;
line-height:1.0em;
color: #CCC;
cursor: pointer;
}
div.highlight ul.tabs li.active {
border-bottom: none;
cursor: default;
}
div.element {
flex-direction: row;
flex-wrap: nowrap;
flex-flow: column-reverse wrap;
}
</pre>
</div>
<div class="wrap">
<h1>MINIFIED</H1>
<pre class="minified"></pre>
</div>
<div class="wrap">
<h1>BEAUTIFIED</H1>
<pre class="beautified"></pre>
</div>
Questions
How do I correctly loop through each line and if the line doesn't end with an opening ( { ) or closing ( } ) curly bracket, indent the code to the with either a tab our four white spaces timed by the level?
UPDATE 1). A Working Version Via Regex For CSS One Indent Only
This version will only work for one indent and for CSS only, however
I'd like to get my other version working to use for other languages
and to indent further for #media queries, indenting the code further
within:
> Example on CodePen
> Example Below
>
> code = $(".input").html().trim();
>
> if (code) {
> code = code
> // REMOVE TAB SPACES
> .replace(/( )/gi,'')
> // REMOVE NEW LINES
> .replace(/[\n\r]/g,'')
> // REMOVE SPACES BETWEEN SECTIONS
> .replace(/(;|{|})(\s+)([^ \s+])/g,'$1$3')
> .replace(/(;|{|})(\s+)([^ \s+])/g,'$1$3')
> .replace(/(\*\/)(\s+)([^ \s+])/g,'$1$3');
> }
> $('.minified').text(code);
>
> minified = $('.minified').html();
>
> if (minified) {
> minified = minified
> .replace(/(\;|\}|\{)/gi,'$1\n\r')
> .replace(/((\/\*)(| )([^"'\s\n]+)(| )(\*\/))/gi,'\n\r$1\n\r')
> .replace(/(([a-zA-Z0-9 -]+)(:)([a-zA-Z0-9 -#%"-., ]+)(;))/g,' $1');
> }
> $('.beautified').text(minified);
>
>
>
> .wrap {
> float:left;
> width: 400px;
> margin: 20px;
> }
> .wrap > h1 {
> text-align: center;
> }
> pre {
> float: left;
> width:100%;;
> height: 400px;
> border: 1px solid #000000;
> overflow: auto;
> }
>
> pre.minified {
> height: 50px;
> }
>
>
>
> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
> <div class="wrap">
> <h1>INPUT</H1>
> <pre class="input">
> div.highlight {
> background:#FFFFFF;
> border:1px solid #E0E0E0;
> font-family:"Courier New",Courier,monospace;
> overflow: hidden;
> }
> div.highlight pre{
> width: 100%;
> overflow: auto;
> padding:0;
> margin:0;
> font-size:13px;
> clear: both;
> }
>
> /* tabs */
> div.highlight ul.tabs {
> overflow: hidden;
> padding: 5px 0 5px 0;
> margin: 0;
> list-style: none;
> border-bottom: 1px solid #E0E0E0;
> width: 100%;
> }
> div.highlight ul.tabs li {
> padding: 0;
> margin: 0 5px;
> float: left;
> background: none;
> border-bottom: 1px dashed #CCC;
> line-height:1.0em;
> color: #CCC;
> cursor: pointer;
> }
> div.highlight ul.tabs li.active {
> border-bottom: none;
> cursor: default;
> }
> div.element {
> flex-direction: row;
> flex-wrap: nowrap;
> flex-flow: column-reverse wrap;
> }
> </pre>
> </div>
> <div class="wrap">
> <h1>MINIFIED</H1>
> <pre class="minified"></pre>
> </div>
> <div class="wrap">
> <h1>BEAUTIFIED</H1>
> <pre class="beautified"></pre>
> </div>
>
>
You're close. In the code you have, elem is a local variable inside the function you pass to each. You modify it to be indented, but then the program forgets about it when the function returns at the end of the iteration. You'll want to assign it back to lines, probably with lines[n] = elem. Edit: $.map may be more appropriate.
Then, another variant of the same problem occurs. lines is updated, but minified is still the same. You can use minified = lines.join('\n') to glue the pieces back together and update minified.
Your indentation algorithm isn't working for various reasons.
1) You need to write in the lines array like so: lines[n] = tab + elem;
2) You mistakenly assigned minified instead of lines to $('.beautified')
3) Your code was not indenting deeper than 1 level. I modified it to indent at any depth, and added a codepen example to demonstrate this.
$.each(lines, function(n, elem) {
var last = elem[elem.length -1];
if (last === "}") {
level--;
}
var tab = " ".repeat(level);
lines[n] = tab + elem + "\n";
if (last === "{") {
level++;
}
});
lines = lines.join('');
$('.beautified').text(lines);
http://codepen.io/anon/pen/NdezxM

Format credit card number

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
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.
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;
}
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.
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(' ')
)
}
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
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";
}]);
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.
<?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;
}
?>
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
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-");
}
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);
}
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;
}
});
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>
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)}
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)
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;
});
});
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.
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
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;
}

Limit a DIV to appear within another DIV of specific size

I'm currently working on this small project that randomly displays a div (#box) of 100px width and height. I want this div to appear ONLY in another div (#boxBorder) so it appears to be limited to a specific area on the page.
Here is the content of my HTML:
<h1>Test your reactions!</h1>
<p id="directions">Click the shape as fast as you can!</p>
<p id="scoreC">Click score: <span id="cScore">0</span>s</p>
<p id="scoreT">Total score: <span id="tScore">0</span>s</p>
<div id="boxBorder"></div>
<div id="box"></div>
Here is the CSS:
#boxBorder {
height: 500px;
width: 500px;
margin: 20px auto;
left: 0;
right: 0;
background-color: white;
border: 1px black solid;
position: absolute;
z-index: 0;
}
#box {
margin: 0 auto;
height: 100px;
width: 100px;
background-color: red;
display: none;
border-radius: 50px;
position: relative;
z-index: 1;
}
h1 {
margin: 15px 0 0 0;
}
#directions {
margin: 0;
padding: 5px;
font-size: 0.8em;
}
#scoreT, #scoreC {
font-weight: bold;
margin: 10px 50px 0 0;
}
#tScore, #cScore {
font-weight: normal;
}
h1, #directions, #scoreT, #scoreC {
width: 100%;
text-align: center;
}
And lastly, the javascript function for random position:
//Get random position
function getRandomPos() {
var pos = Math.floor((Math.random() * 500) + 1);
console.log("POS: " + pos + "px");
return pos + "px";
}
Which I call within a timeout method:
setTimeout(function() {
createdTime = Date.now();
console.log("make box: " + createdTime);
document.getElementById("box").style.top=getRandomPos();
document.getElementById("box").style.left=getRandomPos();
document.getElementById("box").style.backgroundColor=getRandomColor();
document.getElementById("box").style.borderRadius=getRandomShape();
document.getElementById("box").style.display="block";
}, rTime);
I'm not very skilled in positioning and I can't seem to get these two divs to align so that the #box div can recognize the size of the #boxBorder div and stay within those limits. Any help would be appreciated!
Couple things wrong here:
You need the box div nested inside the borderBox div if you want to use the relative positioning.
<div id="boxBorder">
<div id="box"></div>
</div>
The randomPos function needs to take into account the size of the box, so only multiply by 400 instead of 500.
function getRandomPos() {
var pos = Math.floor((Math.random() * 400));
return pos + "px";
}
Set the style to inline-block, not block for the box.
Use setInterval instead of setTimeout to have it repeat.
var rTime = 1000;
function getRandomPos() {
var pos = Math.floor((Math.random() * 400));
console.log("POS: " + pos + "px");
return pos + "px";
}
function getRandomColor() {
return ['#bf616a', '#d08770', '#ebcb8b', '#a3be8c', '#96b5b4', '#8fa1b3', '#b48ead'][(Math.floor(Math.random() * 7))];
}
function randomizeBox() {
createdTime = Date.now();
console.log("make box: " + createdTime);
document.getElementById("box").style.top = getRandomPos();
document.getElementById("box").style.left = getRandomPos();
document.getElementById("box").style.backgroundColor = getRandomColor();
}
setInterval(randomizeBox, rTime);
#boxBorder {
height: 500px;
width: 500px;
margin: 20px auto;
left: 0;
right: 0;
background-color: white;
border: 1px black solid;
position: absolute;
z-index: 0;
}
#box {
margin: 0 auto;
height: 100px;
width: 100px;
border-radius: 50px;
position: relative;
z-index: 1;
display: inline-block;
}
h1 {
margin: 15px 0 0 0;
}
#directions {
margin: 0;
padding: 5px;
font-size: 0.8em;
}
#scoreT,
#scoreC {
font-weight: bold;
margin: 10px 50px 0 0;
}
#tScore,
#cScore {
font-weight: normal;
}
h1,
#directions,
#scoreT,
#scoreC {
width: 100%;
text-align: center;
}
<h1>Test your reactions!</h1>
<p id="directions">Click the shape as fast as you can!</p>
<p id="scoreC">Click score: <span id="cScore">0</span>s</p>
<p id="scoreT">Total score: <span id="tScore">0</span>s</p>
<div id="boxBorder">
<div id="box"></div>
</div>

Categories