Div as Input Web/Mobile - javascript

when I open my website (https://scampsblog.com/code/openjarvis/two/index.html) a div shows up with which the user can interact (e.g. type "help"). My problem is, that when somebody opens the website on a computer, they can type as expected, but opening the website via a smartphone will prevent them from any interaction. Using contenteditable solved the smartphone issue but resulted in some weird bugs.
Here is the code I have so far:
/*
// Made with <3 by Marcus Bizal
// github.com/marcbizal
// linkedin.com/in/marcbizal
*/
$(document).ready(function() {
"use strict";
// UTILITY
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// END UTILITY
// COMMANDS
function clear() {
terminal.text("");
}
function help() {
terminal.append("There is no help... MUAHAHAHAHA. >:D\n");
}
function echo(args) {
var str = args.join(" ");
terminal.append(str + "\n");
}
function fortune() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://cdn.rawgit.com/bmc/fortunes/master/fortunes', false);
xhr.send(null);
if (xhr.status === 200) {
var fortunes = xhr.responseText.split("%");
var fortune = fortunes[getRandomInt(0, fortunes.length)].trim();
terminal.append(fortune + "\n");
}
}
// END COMMANDS
var title = $(".title");
var terminal = $(".terminal");
var prompt = "";
var path = ">";
var commandHistory = [];
var historyIndex = 0;
var command = "";
var commands = [{
"name": "clear",
"function": clear
}, {
"name": "help",
"function": help
}, {
"name": "fortune",
"function": fortune
}, {
"name": "echo",
"function": echo
}];
function processCommand() {
var isValid = false;
// Create args list by splitting the command
// by space characters and then shift off the
// actual command.
var args = command.split(" ");
var cmd = args[0];
args.shift();
// Iterate through the available commands to find a match.
// Then call that command and pass in any arguments.
for (var i = 0; i < commands.length; i++) {
if (cmd === commands[i].name) {
commands[i].function(args);
isValid = true;
break;
}
}
// No match was found...
if (!isValid) {
terminal.append("openjarvis: command not found: " + command + "\n");
}
// Add to command history and clean up.
commandHistory.push(command);
historyIndex = commandHistory.length;
command = "";
}
function displayPrompt() {
terminal.append("<span class=\"prompt\">" + prompt + "</span> ");
terminal.append("<span class=\"path\">" + path + "</span> ");
}
// Delete n number of characters from the end of our output
function erase(n) {
command = command.slice(0, -n);
terminal.html(terminal.html().slice(0, -n));
}
function clearCommand() {
if (command.length > 0) {
erase(command.length);
}
}
function appendCommand(str) {
terminal.append(str);
command += str;
}
/*
// Keypress doesn't catch special keys,
// so we catch the backspace here and
// prevent it from navigating to the previous
// page. We also handle arrow keys for command history.
*/
$(document).keydown(function(e) {
e = e || window.event;
var keyCode = typeof e.which === "number" ? e.which : e.keyCode;
// BACKSPACE
if (keyCode === 8 && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
e.preventDefault();
if (command !== "") {
erase(1);
}
}
// UP or DOWN
if (keyCode === 38 || keyCode === 40) {
// Move up or down the history
if (keyCode === 38) {
// UP
historyIndex--;
if (historyIndex < 0) {
historyIndex++;
}
} else if (keyCode === 40) {
// DOWN
historyIndex++;
if (historyIndex > commandHistory.length - 1) {
historyIndex--;
}
}
// Get command
var cmd = commandHistory[historyIndex];
if (cmd !== undefined) {
clearCommand();
appendCommand(cmd);
}
}
});
$(document).keypress(function(e) {
// Make sure we get the right event
e = e || window.event;
var keyCode = typeof e.which === "number" ? e.which : e.keyCode;
// Which key was pressed?
switch (keyCode) {
// ENTER
case 13:
{
terminal.append("\n");
processCommand();
displayPrompt();
break;
}
default:
{
appendCommand(String.fromCharCode(keyCode));
}
}
});
// Set the window title
title.text("openjarvis setup");
// Get the date for our fake last-login
var date = ("setup");
// Display last-login and prompt
terminal.append("openjarvis " + date + " \n");
displayPrompt();
});
html,
body {
height: 100%;
overflow: hidden;
}
body {
display: flex;
justify-content: center;
align-items: center;
}
* {
box-sizing: border-box;
}
textarea,
input,
button {
outline: none;
}
.window-button,
.window .buttons .close,
.window .buttons .minimize,
.window .buttons .maximize {
padding: 0;
margin: 0;
margin-right: 4px;
width: 12px;
height: 12px;
background-color: gainsboro;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 6px;
color: rgba(0, 0, 0, 0.5);
}
.window {
animation: bounceIn 1s ease-in-out;
width: 640px;
}
.window .handle {
height: 22px;
background: linear-gradient(0deg, #d8d8d8, #ececec);
border-top: 1px solid white;
border-bottom: 1px solid #b3b3b3;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
color: rgba(0, 0, 0, 0.7);
font-family: Helvetica, sans-serif;
font-size: 13px;
line-height: 22px;
text-align: center;
}
.window .buttons {
position: absolute;
float: left;
margin: 0 8px;
}
.window .buttons .close {
background-color: #ff6159;
}
.window .buttons .minimize {
background-color: #ffbf2f;
}
.window .buttons .maximize {
background-color: #25cc3e;
}
.window .terminal {
padding: 4px;
background-color: black;
opacity: 0.7;
height: 218px;
color: white;
font-family: 'Source Code Pro', monospace;
font-weight: 200;
font-size: 14px;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
overflow-y: auto;
}
.window .terminal::after {
content: "|";
animation: blink 2s steps(1) infinite;
}
.prompt {
color: #bde371;
}
.path {
color: #5ed7ff;
}
#keyframes blink {
50% {
color: transparent;
}
}
/*#keyframes bounceIn {
0% {
transform: translateY(-1000px);
}
60% {
transform: translateY(200px);
}
100% {
transform: translateY(0px);
}
}*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>openjarvis</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="cmd.css">
</head>
<body>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:200' rel='stylesheet' type='text/css'>
<div class="container">
<div class="window">
<div class="handle">
<div class="buttons">
<button class="close">
</button>
<button class="minimize">
</button>
<button class="maximize">
</button>
</div>
<span class="title"></span>
</div>
<div class="terminal"></div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" defer></script>
<script src="cmd.js" defer></script>
</body>
</html>

Related

how to ignore tilde accent in search filter js?

When I look in the search engine for a word with an accent, for example: Débora, Lázaro, Ángela, Álvaro, Arquímedes, etc. does not find the result.
Without accents or with upper or lower case, the search engine works excellent.
Someone who can help me, I'm still a beginner in programming.
This is my complete code
const deepMerge = (...objects) => {
const isObject = (obj) => obj && typeof obj === "object";
return objects.reduce((prev, obj) => {
Object.keys(obj).forEach((key) => {
const pVal = prev[key];
const oVal = obj[key];
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
} else if (isObject(pVal) && isObject(oVal)) {
prev[key] = deepMerge(pVal, oVal);
} else {
prev[key] = oVal;
}
});
return prev;
}, {});
};
const DEFAULT_OPTIONS = {
classNames: {
wrapperEl: "cselect",
selectEl: "cselect__select",
renderedEl: "cselect__rendered",
renderedTextEl: "cselect__rendered-text",
searchEl: "cselect__search",
optionsEl: "cselect__options",
optionEl: "cselect__option",
init: "js-init-cselect",
open: "is-open",
onTop: "is-on-top",
selected: "is-selected",
hidden: "is-hidden"
},
minimumOptionsForSearch: 10,
onOpen: null,
onClose: null,
onToggle: null
};
class CSelect {
// Elements
#wrapperEl;
#renderedEl;
#renderedTextEl;
#searchEl;
#optionsEl;
#optionEls;
// Functions
#handleSearch;
#optionElClick;
#clickOutside;
#escPress;
constructor(selectEl, options = {}) {
// Handle arguments
this.selectEl = selectEl;
this.options = deepMerge(DEFAULT_OPTIONS, options);
// Bind 'this'
this.open = this.open.bind(this);
this.close = this.close.bind(this);
this.toggle = this.toggle.bind(this);
this.#handleSearch = this.#handleSearchFn.bind(this);
this.#optionElClick = this.#optionElClickFn.bind(this);
this.#clickOutside = this.#clickOutsideFn.bind(this);
this.#escPress = this.#escPressFn.bind(this);
// Functions
this.init();
}
init() {
// Check if already init
if (this.selectEl.classList.contains(this.options.classNames.init)) {
console.error(`CSelect already initialized. ID: ${this.selectEl.id}`);
return;
}
// Handle select element
this.selectEl.setAttribute("tabindex", "-1");
this.selectEl.classList.add(this.options.classNames.selectEl);
// Functions
this.#generateHTML();
this.#addEvents();
// Add initialization
this.selectEl.classList.add(this.options.classNames.init);
}
#generateHTML() {
// Generate wrapper
const wrapperHTML = /* HTML */ `
<div class="${this.options.classNames.wrapperEl}"></div>
`;
this.selectEl.insertAdjacentHTML("beforebegin", wrapperHTML);
this.#wrapperEl = this.selectEl.previousElementSibling;
this.#wrapperEl.appendChild(this.selectEl);
// Generate rendered
const selectedOption = this.selectEl.options[this.selectEl.selectedIndex];
const selectedOptionText = selectedOption.textContent;
this.#renderedEl = document.createElement("button");
this.#renderedEl.type = "button";
this.#renderedEl.className = this.options.classNames.renderedEl;
this.#wrapperEl.appendChild(this.#renderedEl);
this.#renderedTextEl = document.createElement("span");
this.#renderedTextEl.className = this.options.classNames.renderedTextEl;
this.#renderedTextEl.textContent = selectedOptionText;
this.#renderedEl.appendChild(this.#renderedTextEl);
// Generate options wrapper
this.#optionsEl = document.createElement("div");
this.#optionsEl.className = this.options.classNames.optionsEl;
this.#wrapperEl.appendChild(this.#optionsEl);
// Generate search
if (
[...this.selectEl.options].length >= this.options.minimumOptionsForSearch
) {
this.#searchEl = document.createElement("input");
this.#searchEl.type = "text";
this.#searchEl.className = this.options.classNames.searchEl;
this.#optionsEl.appendChild(this.#searchEl);
}
// Generate each option
const selectOptions = [...this.selectEl.options];
this.#optionEls = [];
for (const option of selectOptions) {
if (option.disabled) {
continue;
}
const newOption = document.createElement("button");
newOption.type = "button";
newOption.className = this.options.classNames.optionEl;
newOption.textContent = option.textContent;
newOption.setAttribute("data-value", option.value);
if (option.selected) {
newOption.classList.add(this.options.classNames.selected);
}
this.#optionsEl.appendChild(newOption);
this.#optionEls.push(newOption);
}
}
open(callback) {
this.#wrapperEl.classList.add(this.options.classNames.open);
// Handle optionsEl position
this.#handleOptionsElPosition();
// Handle search
if (this.#searchEl !== null) {
this.#resetSearch();
this.#searchEl.focus();
}
// Handle callback functions
if (typeof this.options.onOpen === "function") {
this.options.onOpen(this);
}
if (typeof callback === "function") {
callback(this);
}
}
close(callback) {
this.#wrapperEl.classList.remove(this.options.classNames.open);
// Handle callback functions
if (typeof this.options.onClose === "function") {
this.options.onClose(this);
}
if (typeof callback === "function") {
callback(this);
}
}
toggle(callback) {
if (!this.#wrapperEl.classList.contains(this.options.classNames.open)) {
this.open();
} else {
this.close();
}
// Handle callback functions
if (typeof this.options.onToggle === "function") {
this.options.onToggle(this);
}
if (typeof callback === "function") {
callback(this);
}
}
#handleOptionsElPosition() {
this.#optionsEl.classList.remove(this.options.classNames.onTop);
const boundingRect = this.#optionsEl.getBoundingClientRect();
const isOutTop = boundingRect.top < 0;
const isOutBottom =
boundingRect.bottom >
(window.innerHeight || document.documentElement.clientHeight);
if (isOutBottom) {
this.#optionsEl.classList.add(this.options.classNames.onTop);
}
if (isOutTop) {
this.#optionsEl.classList.remove(this.options.classNames.onTop);
}
}
#resetSearch() {
this.#searchEl.value = "";
for (const optionEl of this.#optionEls) {
optionEl.classList.remove(this.options.classNames.hidden);
}
}
#handleSearchFn() {
for (const optionEl of this.#optionEls) {
if (
optionEl.textContent
.toLowerCase()
.indexOf(this.#searchEl.value.toLowerCase()) > -1
) {
optionEl.classList.remove(this.options.classNames.hidden);
} else {
optionEl.classList.add(this.options.classNames.hidden);
}
}
}
#optionElClickFn(event) {
// Close the select
this.close();
// Cache the target
const target = event.target;
// Check if click selected option
if (this.selectEl.value === target.dataset.value) {
return;
}
// Handle rendered text
this.#renderedTextEl.textContent = target.textContent;
// Handle select element change
this.selectEl.value = target.dataset.value;
const triggerEvent = new Event("change");
this.selectEl.dispatchEvent(triggerEvent);
// Highlight selected
for (const optionEl of this.#optionEls) {
optionEl.classList.remove(this.options.classNames.selected);
}
target.classList.add(this.options.classNames.selected);
}
#clickOutsideFn(event) {
const isOutside =
event.target.closest(`.${this.options.classNames.wrapperEl}`) !==
this.#wrapperEl;
const isOpen = this.#wrapperEl.classList.contains(
this.options.classNames.open
);
if (isOutside && isOpen) {
this.close();
}
}
#escPressFn(event) {
const isEsc = event.keyCode === 27;
const isOpen = this.#wrapperEl.classList.contains(
this.options.classNames.open
);
if (isEsc && isOpen) {
this.close();
}
}
#addEvents() {
this.#renderedEl.addEventListener("click", this.toggle);
if (this.#searchEl !== null) {
this.#searchEl.addEventListener("input", this.#handleSearch);
}
for (const optionEl of this.#optionEls) {
optionEl.addEventListener("click", this.#optionElClick);
}
document.addEventListener("click", this.#clickOutside);
document.addEventListener("keyup", this.#escPress);
}
destroy() {
// Check if already init
if (!this.selectEl.classList.contains(this.options.classNames.init)) {
console.error(`CSelect not initialized. ID: ${this.selectEl.id}`);
return;
}
// Remove Events
document.removeEventListener("click", this.#clickOutside);
document.removeEventListener("keyup", this.#escPress);
// Unwrap
this.#wrapperEl.replaceWith(this.selectEl);
// Clear select element
this.selectEl.removeAttribute("tabindex");
this.selectEl.classList.remove(this.options.classNames.selectEl);
this.selectEl.classList.remove(this.options.classNames.init);
}
}
// ***
window.addEventListener("DOMContentLoaded", () => {
window["selectObj"] = {};
const selectEls = [...document.querySelectorAll(".js-select")];
for (const selectEl of selectEls) {
window["selectObj"][selectEl.id] = new CSelect(selectEl, {
minimumOptionsForSearch: 0
// onClose: (cselectObj) => {
// console.log(cselectObj)
// },
});
}
// window['selectObj']['select1'].open((cselectObj) => {
// console.log(cselectObj)
// })
});
body {
margin: 0;
padding: 100px 150px;
font-family: sans-serif;
}
/* *** */
.cselect {
position: relative;
}
.cselect,
.cselect *,
.cselect *:before,
.cselect *:after {
box-sizing: border-box;
}
.cselect__select {
display: block;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: -1;
pointer-events: none;
}
.cselect__rendered {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0;
padding: 6px 12px;
width: 100%;
font: inherit;
font-size: 16px;
font-weight: normal;
color: #fff;
line-height: 1.5;
text-align: left;
text-decoration: none;
background: #333;
border: 0;
border-radius: 6px;
cursor: pointer;
}
.cselect__rendered:after {
content: "▾";
display: block;
margin: 0 0 0 8px;
}
.cselect.is-open .cselect__rendered:after {
content: "▴";
}
.cselect__rendered-text {
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.cselect__options {
position: absolute;
top: 100%;
left: 0;
margin: 6px 0;
padding: 6px;
width: 100%;
max-height: 264px;
color: #fff;
background: #333;
border-radius: 6px;
transform: translateY(-6px) scale(0.98);
transform-origin: center top;
opacity: 0;
visibility: hidden;
transition-property: transform, visibility, opacity;
transition-duration: 0.3s;
transition-timing-function: ease;
overflow: auto;
z-index: 999;
}
.cselect__options::-webkit-scrollbar {
width: 4px;
}
.cselect__options::-webkit-scrollbar-track {
background: transparent;
border-radius: 4px;
}
.cselect__options::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
.cselect__options::-webkit-scrollbar-thumb:hover {
background: #777;
}
.cselect__options.is-on-top {
top: auto;
bottom: 100%;
}
.cselect.is-open .cselect__options {
transform: translateY(0) scale(1);
opacity: 1;
visibility: visible;
transition-property: transform, opacity;
}
.cselect__search {
display: block;
margin: 0 0 6px;
padding: 2px 6px;
width: 100%;
font: inherit;
font-size: 16px;
font-weight: normal;
color: #333;
line-height: 1.5;
background: #fff;
border: 0;
border-radius: 6px;
}
.cselect__option {
display: block;
padding: 6px;
width: 100%;
font: inherit;
font-size: 16px;
font-weight: normal;
color: #fff;
line-height: 1.5;
text-align: left;
text-decoration: none;
background: transparent;
border: 0;
border-radius: 6px;
cursor: pointer;
}
.cselect__option:hover {
background: #555;
}
.cselect__option.is-selected {
color: #999;
background: transparent;
cursor: default;
}
.cselect__option.is-hidden {
display: none;
}
<form action="./" method="get">
<button type="submit">GET</button>
<br><br><br><br>
<div style="max-width: 256px;">
<select name="select1" id="select1" class="js-select" required>
<option value="" selected disabled>Please select</option>
<option value="bunny">Perú</option>
<option value="kitten">Luís</option>
<option value="hamster">Hamster</option>
</select>
</div>
</form>
You need to update your function handleSearchFn to convert characters to the base ones (you can just replace yours with next one):
#handleSearchFn() {
const searchPhrase = this.#searchEl.value.normalize('NFD').replace(/[\u0300-\u036f]/g, "").toLowerCase();
for (const optionEl of this.#optionEls) {
if (optionEl.textContent.toLowerCase().indexOf(searchPhrase) > -1) {
optionEl.classList.remove(this.options.classNames.hidden);
} else {
optionEl.classList.add(this.options.classNames.hidden);
}
}
}

Add score board in ti-tac-toe game

var grid = document.getElementById('grid');
var msg = document.querySelector('.message');
var chooser = document.querySelector('form');
var mark;
var cells;
// add click listener to radio buttons
function setPlayer() {
mark = this.value;
msg.textContent = mark + ', click on a square to make your move!';
chooser.classList.add('game-on');
this.checked = false;
buildGrid();
}
// add click listener to each cell
function playerMove() {
if (this.textContent == '') {
this.textContent = mark;
checkRow();
switchMark();
computerMove();
}
}
// let the computer make the next move
function computerMove() {
var emptyCells = [];
var random;
/* for (var i = 0; i < cells.length; i++) {
if (cells[i].textContent == '') {
emptyCells.push(cells[i]);
}
}*/
cells.forEach(function(cell){
if (cell.textContent == '') {
emptyCells.push(cell);
}
});
// computer marks a random EMPTY cell
random = Math.ceil(Math.random() * emptyCells.length) - 1;
emptyCells[random].textContent = mark;
checkRow();
switchMark();
}
// switch player mark
function switchMark() {
if (mark == 'X') {
mark = 'O';
} else {
mark = 'X';
}
}
// determine a winner
function winner(a, b, c) {
if (a.textContent == mark && b.textContent == mark && c.textContent == mark) {
msg.textContent = mark + ' is the winner!';
a.classList.add('winner');
b.classList.add('winner');
c.classList.add('winner');
return true;
} else {
return false;
}
}
// check cell combinations
function checkRow() {
winner(document.getElementById('c1'), document.getElementById('c2'), document.getElementById('c3'));
winner(document.getElementById('c4'), document.getElementById('c5'), document.getElementById('c6'));
winner(document.getElementById('c7'), document.getElementById('c8'), document.getElementById('c9'));
winner(document.getElementById('c1'), document.getElementById('c4'), document.getElementById('c7'));
winner(document.getElementById('c2'), document.getElementById('c5'), document.getElementById('c8'));
winner(document.getElementById('c3'), document.getElementById('c6'), document.getElementById('c9'));
winner(document.getElementById('c1'), document.getElementById('c5'), document.getElementById('c9'));
winner(document.getElementById('c3'), document.getElementById('c5'), document.getElementById('c7'));
}
// clear the grid
function resetGrid() {
mark = 'X';
/* for (var i = 0; i < cells.length; i++) {
cells[i].textContent = '';
cells[i].classList.remove('winner');
}*/
cells.forEach(function(cell){
cell.textContent = '';
cell.classList.remove('winner');
});
msg.textContent = 'Choose your player:';
chooser.classList.remove('game-on');
grid.innerHTML = '';
}
// build the grid
function buildGrid() {
for (var i = 1; i <= 9; i++) {
var cell = document.createElement('li');
cell.id = 'c' + i;
cell.addEventListener('click', playerMove, false);
grid.appendChild(cell);
}
/* cells = document.querySelectorAll('li'); //Returns a NodeList, not an Array
See https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches */
cells = Array.prototype.slice.call(grid.getElementsByTagName('li'));
}
var players = Array.prototype.slice.call(document.querySelectorAll('input[name=player-choice]'));
players.forEach(function(choice){
choice.addEventListener('click', setPlayer, false);
});
var resetButton = chooser.querySelector('button');
resetButton.addEventListener('click', function(e) {
e.preventDefault();
resetGrid();
});
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background-color: #996E89;
font-family: "Comfortaa", sans-serif;
}
h1 {
font-family: "Lobster", serif;
}
.message {
color: #fff;
font-size: 1.5em;
padding-bottom: 1em;
}
form {
label {
font-size: 4em;
font-weight: bold;
vertical-align: middle;
}
input[type=radio] {
margin: 1em;
cursor: pointer;
}
fieldset {
display: block;
opacity: 1;
transition: all ease 1s;
}
&.game-on fieldset {
opacity: 0;
display: none;
}
&.game-on button {
opacity: 1;
display: block;
margin: 0 auto;
}
}
#grid-section {
margin: 3em 0;
}
#grid {
width: 300px;
margin: 0 auto;
}
li {
border: 1px solid #000;
width: 100px;
height: 100px;
display: block;
float: left;
font-size: 3em;
text-align: center;
padding: .5em;
}
#c1, #c2, #c3 {
border-top: none;
}
#c3, #c6, #c9 {
border-right: none;
}
#c7, #c8, #c9 {
border-bottom: none;
}
#c1, #c4, #c7 {
border-left: none;
}
.winner {
color: #9AE1E5;
}
.btn-primary,
.btn-primary:focus {
background-color: #33B7A5;
opacity: 0;
outline: 0;
transition: all ease .3s;
}
.btn-primary:hover {
background-color: #4C2F39;
}
<div class="container">
<div class="row text-center" id="intro-section">
<h1>Player vs Computer Tic-Tac-Toe</h1>
<h2 class="message">Choose your player:</h2>
<form action="#">
<fieldset>
<input type="radio" name="player-choice" id="player-choice-1" value="X" />
<label for="player-choice-1">X</label>
<input type="radio" name="player-choice" id="player-choice-2" value="O" />
<label for="player-choice-2">O</label>
</fieldset>
<button id="reset" class="btn btn-primary">Reset</button>
</form>
</div>
<div class="row" id="grid-section">
<ul id="grid"></ul>
</div>
I wish to add a scoreboard on top where wins,loses,draws would be calculated. How do I go about the same? Also an another HTML page where I could see the number of wins, loses, draws.
I have tried adding table in the html and using it in JS but not able to implement the same.
Could you suggest any methods to implement these tasks in the project.

:before with position: absolute causing line breaks and moving elements

I am making a typing program. Each letter is in its own div, which is broken into words.
For example, the word other would be written as:
<div class="word">
<div class="letter" id="l184"></div>
<div class="letter" id="l185">o</div>
<div class="letter" id="l186">t</div>
<div class="letter" id="l187">h</div>
<div class="letter" id="l188">e</div>
<div class="letter" id="l189">r</div>
</div>
The letter with the cursor before it also has the class cursor.
.cursor:before {
position: absolute;
width: 2px;
height: 30px;
background: var(--accent);
content: ' ';
}
Sometimes, when typing the first word of a line, the completed part of the word, which has the cursor after it, is bumped up to the line before it. This does not happen when the :before has no content. Please help me figure out why something with position: absolute is moving elements around. Thank you!
EDIT: Snippet
EDIT 2: The glitch only works with a certain combination of words, so if you cannot reproduce the glitch, please try again.
let dict = ['test', 'stack', 'overflow'];
let index = 0, words, wrong = 0, last;
let running;
let sec = 0, min = 0;
const text = document.querySelector('#text');
const st = document.querySelector('#sec');
const mt = document.querySelector('#min');
function genTest() {
words = "";
for (let i = 0; i < 100; i++) {
words += dict[randomRange(0, 2)];
if (i !== 99) words += " ";
}
let html = `<div class='word'>`;
let i = 0;
words.split('').forEach(l => {
if (l === ' ') html += `</div><div class="word">`;
if (i === 0) html += `<div class="letter curs-fade" id='l${i}'>${l}</div>`;
else html += `<div class="letter" id='l${i}'>${l}</div>`;
i++;
});
last = i;
html += '</div>'
text.innerHTML = html;
}
function initTest() {
running = false;
genTest();
st.classList = "";
mt.classList = "";
l(0).classList.add('cursor');
}
function start() {
running = true;
st.classList.add('txt-active');
setInterval(() => {
sec++;
if (sec >= 60) {
sec %= 60;
min++;
if (min === 1) {
mt.classList.add('txt-active');
}
}
mt.innerHTML = min.toString() + ':';
st.innerHTML = ((sec < 10) ? '0' : '') + sec.toString();
}, 1000);
}
document.addEventListener('keydown', (e) => {
let key = e.key;
const cl = l(index);
if (wrong > 0 && key === 'Backspace') {
wrong--;
w(wrong).remove();
return;
}
if (key.match(/^[a-zA-Z"'\s]+$/) && key.length === 1) {
if (index === 0 && !running) {
start();
}
if (cl.innerHTML === key.toLowerCase() && wrong === 0) {
cl.classList.add('correct');
index++;
} else if (key !== ' ') {
let w = document.createElement('DIV');
w.classList.add('letter');
w.classList.add('incorrect');
w.id = "w" + wrong;
w.innerHTML = key;
wrong++;
if (index > 0) l(index - 1).appendChild(w);
else {
let n = l(index);
n.parentNode.insertBefore(w, n);
}
}
cl.classList.remove('cursor');
l(index).classList.add('cursor');
}
});
function l(index) {
return document.getElementById('l' + index);
}
function w(index) {
return document.getElementById('w' + index);
}
function randomRange(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
initTest();
#import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
* {
font-family: 'Roboto Mono', monospace;
}
body, html {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
body {
background-color: #0f0f0f;
--accent: yellow;
--gray: #ababab;
--dark-gray: #8f8f8f;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.header {
margin: 10px 150px;
}
.title {
font-size: 50px;
font-weight: 200;
color: white;
}
#text {
color: var(--gray);
margin: 20px 150px;
text-align: left;
}
.timer {
color: var(--dark-gray);
font-size: 50px;
font-weight: 300;
text-align: center;
margin: 100px 0 0;
}
.txt-active {
color: white;
transition: color .7s ease;
}
#min, #sec {
display: inline;
}
.word {
display: inline;
}
.letter {
display: inline;
font-weight: 200;
font-size: 24px;
}
.correct {
color: white;
}
.incorrect {
color: #d26f6f;
}
.cursor:before {
position: absolute;
width: 2px;
height: 30px;
background: var(--accent);
content: ' ';
}
.curs-fade:before {
animation: cursor-fade alternate-reverse .8s infinite;
}
#keyframes cursor-fade {
80% {opacity: 1}
0% {opacity: 0}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/style.css">
<title>Typeracer++</title>
</head>
<body>
<div class="header">
<div class="title">typeracer++</div>
</div>
<div class="test">
<div class="timer"><div id="min">0:</div><div id="sec">00</div></div>
<div id="text"></div>
</div>
<script type="text/javascript" src="scripts/game.js"></script>
</body>
</html>

Trouble showing div ID

I have a basic Decision Tree. It has an Answer field and a Message field. I am having trouble displaying JavaScript inside the "Message" area using its ID.
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link href="~/Content/styles.css" type="text/css" rel="stylesheet">
<script src="http://code.jquery.com/jquery-3.1.1.js"></script>
<script src="~/Scripts/tree.js"></script>
<link href="http://www.jqueryscript.net/css/jquerysctipttop.css" rel="stylesheet" type="text/css">
<title>Test Tree</title>
<style>
a {
color: #ffffff;
}
a:hover {
color: #ffffff;
text-decoration: none;
cursor: pointer;
}
body {
font: 'Gotham Book', Gotham-Book, Arial, sans-serif;
background-color: #f2f2f2;
}
h1 {
margin: 40px auto 5px auto;
text-align: center;
}
.dc-tree {
font: 16px 'Roboto',Verdana, sans-serif;
position: relative;
text-align: center;
background-color: #f2f2f2;
height: 50%;
overflow: hidden;
}
.dc-tree * {
box-sizing: border-box;
}
.dctree-card {
text-align: center;
padding: 0px;
position: absolute;
margin: 0 auto;
background-color: #f2f2f2;
display: none;
}
.dctree-message {
padding: 10px;
margin-bottom: 20px;
font-size: 1.5em;
}
[class^="dctree-answer"] {
padding: 20px;
/*added height 10vh to auto-align boxes*/
height: 10vh;
text-align: center justify;
margin: 15px 120px;
box-shadow: 3px 3px 5px 2px #BDBDBD;
/*position: fixed;*/
/*text-shadow: 1px 1px 2px black;*/
}
.darkBlue {
background-color: #003E69;
color: white;
}
.softBlack {
background-color: #333333;
color: white;
}
.lightGrey {
background-color: #808080;
color: white;
}
.teal {
background-color: #38939b;
color: white;
}
.green {
background-color: #5E9732;
color: white;
}
.orange {
background-color: #F47B20;
color: white;
}
.footer {
font: 'Gotham Book', Gotham-Book, Arial, sans-serif;
position: fixed;
left: 0;
bottom: 80px;
width: 100%;
background-color: #f2f2f2;
color: black;
text-align: center;
font-size: 16px;
padding: 5px;
/*text-decoration: underline;*/
}
</style>
<script>
(function ($) {
var settings;
var currentCard;
var prevCard = [];
// Plugin definition.
$.fn.decisionTree = function (options) {
var elem = $(this);
settings = $.extend({}, $.fn.decisionTree.defaults, options);
elem.addClass(settings.containerClass);
renderRecursive(settings.data, elem, "dctree-first");
$('.dctree-prev').on('click', function () {
showCard(prevCard.pop(), true);
});
currentCard = $('#dctree-first');
currentCard.show();
};
$.fn.decisionTree.defaults = {
data: null,
animationSpeed: "fast",
animation: "slide-left",
containerClass: "dc-tree",
cardClass: "dctree-card",
messageClass: "dctree-message"
};
function renderRecursive(data, elem, id) {
var container = $('<div></div>')
.addClass(settings.cardClass)
.addClass('col-xs-12');
var message = $('<div></div>').addClass(settings.messageClass).append(data.message);
message.append('');
message.append('<div id="heading"></div>');
container.append(message);
if (id != null) {
container.attr('id', id)
}
if (typeof data.decisions != "undefined") {
var decisions = $('<div></div>').addClass('dctree-decisions');
for (var i = 0; data.decisions.length > i; i++) {
var decision = data.decisions[i];
var genId = guid();
var grid = $('<div></div>').addClass('col-md-6');
var answer = $('<div></div>')
.addClass("dctree-answer-" + i)
.append(decision.answer, '')
.on('click', function () {
getNextCard(this);
})
.attr('data-dctree-targetid', genId);
if (typeof decision.class != "undefined") {
answer.addClass(decision.class);
}
grid.append(answer);
decisions.append(grid);
renderRecursive(decision, elem, genId);
}
container.append(decisions);
}
if (id != 'dctree-first') {
var controls = $('<div></div>').addClass('dctree-controls col-md-12');
controls.append($('< Back'));
container.append(controls);
}
elem.append(container);
$('.dctree-message').click(function () { console.log("Hi"); })
}
function getNextCard(elem) {
var e = $(elem);
currentCard = e.parents('.' + settings.cardClass)[0];
prevCard.push(currentCard.id);
var nextCard = e.attr('data-dctree-targetid');
showCard(nextCard);
}
function showCard(id, backward) {
var nextCard = $("#" + id);
if (settings.animation == 'slide') {
$(currentCard).slideUp(settings.animationSpeed, function () {
nextCard.slideDown(settings.animationSpeed);
});
} else if (settings.animation == 'fade') {
$(currentCard).fadeOut(settings.animationSpeed, function () {
nextCard.fadeIn(settings.animationSpeed);
});
} else if (settings.animation == 'slide-left') {
var left = { left: "-100%" };
var card = $(currentCard);
if (backward) {
left = { left: "100%" };
}
card.animate(left, settings.animationSpeed, function () {
card.hide();
});
if (nextCard.css('left') == "-100%" || nextCard.css('left') == "100%") {
left.left = 0;
nextCard.show().animate(left, settings.animationSpeed);
} else {
nextCard.fadeIn(settings.animationSpeed);
}
}
currentCard = nextCard;
}
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// End of closure.
})(jQuery);
</script>
<script>
var newURL = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname;
if (a.indexOf('html') > -1) { //Check of html String in URL.
url = url.substring(0, newURL.lastIndexOf("."));
}
//Gets current date and subtracts 7 days. This is to check whether the decision is 7 days prior to today's date.
var d = new Date();
d.setDate(d.getDate() - 7);
document.getElementById('heading').innerHTML = "I bought it " + (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear() + "?"; </script>
<div class="jquery-script-center">
<div class="jquery-script-clear"></div>
</div>
<!--</div>-->
<h1>Dolls</h1>
<div class="main"></div>
<script type="text/javascript">
var data = {
message: "<div style='color:black;'>Are you buying a doll?</div>",
decisions: [{
answer: "ACH",
class: "green",
message: "<div id='heading'></div>",
decisions: [{
answer: "I love it",
class: "orange",
message: "It's my favorite"
},
{
answer: "I don't like it",
class: "green",
message: "It's not my favorite"
},
]
},
]
},
]
},
]
};
$(document).ready(function () {
$('.main').decisionTree({
data: data
});
});
My intent is, for the message that has id=heading, I'd like it to display the JavaScript for the date. However, when I try to launch it in my browser, it refuses to show. Can someone help me with I may be doing wrong? Thank you!
I think your heading div doesn't exist when your script runs initially...all the script is loaded prior to $(document).ready and that is where the "heading" div is added. As a test, I put the date/time code in a function and called it after the
$('.main').decisionTree statement, and I think it worked as expected.
function lateBinder(){
var d = new Date();
d.setDate(d.getDate() - 7);
document.getElementById('heading').innerHTML = "I bought it " + (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear() + "?";
}
$(document).ready(function () {
$('.main').decisionTree({
data: data
});
lateBinder();
});
It seems you have too many closing brackets in your variable "data" object.

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;
}

Categories