Listener keydown : backspace and field length - javascript

I'm wondering why my Backspace touch increment my countLetters variable.
I ask him to return the number of characters, without ever manually incrementing the variable. Why does the backspace key increment my variable once, before working properly?
const $textareas = document.querySelectorAll('.js-textarea');
$textareas.forEach(function($textarea) {
$textarea.addEventListener('keydown', function(event) {
let max = 100;
let countLetters = $textarea.value.length;
let $meta = this.nextSibling.nextSibling; // = meta
$meta.innerHTML = countLetters + ' / ' + max;
if (countLetters >= max) {
$textarea.value.toString().substring(0, max);
}
if (event.which != 46) {
return;
}
// Disabled <textarea>
if (countLetters >= max) {
event.preventDefault();
}
});
});
Demo is available on Here's Codepen!
Thanks

Keydown fires before letter was added or removed in case of backspace. Use keyup instead of keydown.

The value of the text area is changed after your keydown handler runs. You could use the input event instead.
Here is an example:
const max = 100;
$textarea.addEventListener('input', function() {
const countLetters = $textarea.value.length;
const $meta = this.nextSibling.nextSibling;
$meta.innerHTML = countLetters + ' / ' + max;
});
$textarea.addEventListener('keydown', function(event) {
if ($textarea.value.length >= max && event.key === 'Backspace') {
event.preventDefault();
}
});

Related

Increment and decrement button with 'keydown' (ArrowUp and Down)

I need some help with a school assignment. I have two buttons, one for increment and one for decrement. I have four functions, where two of them is for clicking and two for pressing arrow keys. Clicking works fine, but to use arrowUp and ArrowDown i need to click and select the button with the mouse once for it to work. Can i add something to my two lower arrow key functions so that i dont need to click them once before i can start using arrow keys?
let element = document.querySelector('#value')
let buttonIncrease = document.querySelector('#increase')
element.textContent = '1'
let buttonDecrease = document.querySelector('#decrease')
buttonDecrease.disabled = true;
//Increment number by 1 on click
buttonIncrease.addEventListener('click', () => {
element.textContent = Number(element.textContent) + 1
if (element.textContent > 1) {
buttonDecrease.disabled = false;
}
})
//Decrement number by 1 on click
buttonDecrease.addEventListener('click', () => {
element.textContent = Number(element.textContent) - 1
if (element.textContent < 2) {
buttonDecrease.disabled = true;
}
})
//Increment number by 1 on keydown
buttonIncrease.addEventListener('keydown', (up) => {
if (up.key === 'ArrowUp' ) {
element.textContent = Number(element.textContent) + 1
}
if (element.textContent > 1) {
buttonDecrease.disabled = false;
}
})
//Decrement number by 1 on keydown
buttonDecrease.addEventListener('keydown', (down) => {
document.getElementById('decrease')
if (down.key === 'ArrowDown') {
element.textContent = Number(element.textContent) - 1
}
if (element.textContent < 2) {
buttonDecrease.disabled = true;
}
})
you dont need to define muse click on increment and decrement buttons
but to use arrowUp and ArrowDown i need to click and select the button
with the mouse once for it to work.
you can detect whene arrowUp and arrowDown clicked by add event lisner directly to keyboard:
addEventListener("keydown", function (e) { // Do something }
To do what you require you can attach the keydown event handler to the window, so that no matter what element in the DOM has focus, so long as the event can bubble to the window element, the value will be changed.
Also note that you can combine the keydown event handlers in to one, and you can also make the code more succinct and DRY by extracting the repeated logic in to functions:
let buttonIncrease = document.querySelector('#increase');
let buttonDecrease = document.querySelector('#decrease');
let element = document.querySelector('#value');
buttonDecrease.disabled = true;
element.textContent = '1';
const setButtonState = () => buttonDecrease.disabled = parseInt(element.textContent, 10) <= 1;
const updateValue = increment => {
value.textContent = Math.max(1, Number(element.textContent) + increment);
setButtonState();
}
buttonIncrease.addEventListener('click', () => updateValue(1))
buttonDecrease.addEventListener('click', () => updateValue(-1))
window.document.addEventListener('keydown', e => {
e.preventDefault();
let value = Number(element.textContent);
let increment = e.key === 'ArrowUp' ? 1 : e.key === 'ArrowDown' ? -1 : 0;
updateValue(increment);
})
#value {
padding: 3px;
}
<button id="decrease">-</button>
<span id="value">1</span>
<button id="increase">+</button>

How to make html numeric input wrap back around

So my question is, if you have an HTML numeric input with max={100} and min={50} how can you make it so that if you hit the down arrow when it is set to 50, to wrap back around to 100. I tried doing this manually by manipulating the onChange handler method, but this messes with the ability to type in the input box, making it so that if I begin to type 100, the first key pressed (1) is below 50 and sets it to 50.
Something like this:
const input = document.querySelector('input');
input.addEventListener('keydown', e => {
const min = parseInt(input.min);
const max = parseInt(input.max);
if(e.keyCode === 38 || e.code === 'ArrowUp') {
e.preventDefault();
input.value++;
if(input.value > max) input.value = min;
}
if(e.keyCode === 40 || e.code === 'ArrowDown') {
e.preventDefault();
input.value--;
if(input.value < min) input.value = max;
}
})
/* hide spin button */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
display: none;
}
input[type=number] {
font-size: 3em;
}
<input type="number" min="50" max="100" value="50" autofocus/>
change handler triggers every time the input value changes. You need to use keyup handler as John mentioned in the comment.
$('#input').on('keyup', function(e) { // Trigger when a key is pressed and then released
if (e.which === 38) { // Check if the pressed key is Up Arrow
var val = $('#input').val();
if (!isNaN(val)) { // Check if the value is a valid number
val++;
if (val > 100) val = 50; // If it exceeds 100, go back to 50
if (val < 50) val = 100; // Vice versa
$('#input').val(val);
}
else $('#input').val(50); // If the value is not a number, set it to 50
}
if (e.which === 40) { // Check if the pressed key is Down Arrow
var val = $('#input').val();
if (!isNaN(val)) {
val--;
if (val > 100) val = 50;
if (val < 50) val = 100;
$('#input').val(val);
}
else $('#input').val(50);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='text' id='input' value="50">
You can use closures to keep track of the value in the input and then check the updated values against the elements own min and max attributes. Something like below:
function ticker(initVal, el, max, min) {
var currentValue = initVal;
return function(n) {
if (currentValue == n) {
if (n == max) {
el.value = min;
} else if (n == min) {
el.value = max;
}
}
currentValue = n;
}
}
var target = document.querySelector('#myNumber');
var updateEl = ticker(target.value, target, target.max, target.min);
document.querySelector('#myNumber').addEventListener('click', (e) => updateEl(e.target.value));
<input type="number" id="myNumber" value="1" max="5" min="1">

Any way to restrict JQuery input to only unique characters that haven't been input previously

I have a simple JQuery/JS Hangman game and I've spent alot of time making it work and I've run into one issue that messes up my logic and running of the game - when the player enters repeated chars (either right or wrong).
The way I've made the game work, starting with empty arrays I'm pushing into, I thought that I could create a function to only push unique chars into the array function
unique(array) {
var result = [];
$.each(array, function(i, e) {
if ($.inArray(e, result) == -1) result.push(e);
});
return result;
}
var uniqueRightGuesses = unique(rightGuesses);
var uniqueWrongGuesses = unique(wrongGuesses);
But this doesn't work because w/the inner workings of my game, the repeated input chars are still getting displayed & messing up the way winning & loosing is input (even though I'm calculating winning w/the sum of an additional array I've created to take care of a letter that repeats multiple times in a word). I've tried alot of various things at various parts of the game/in various functions and I've figured out that the easiest way to take care of this issue would be to somehow prevent the player from inputing a char if they've already input in the course of the game, in this function:
$(".form-control").keypress(function(event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == 13) {
var space = $(this).val().toLowerCase();
play(space);
$(this).val('');
endGame();
return false;
}
});
I've searched online for a way to do this, I've found jQuery.unique() but I don't think that it'd work here as it's only on DOM objects in an array (& I just want the input to not register/not be allowed if the player has already entered that letter, if it's right or wrong guess- if I take care of this problem at this spot in the game, I won't have to mess w/my arrays or the variables I'm displaying but I don't know how to simply do this.
If anyone has any suggestions or knows if this is even possible, I'd really appreciate it- I've found alot online about restricting special chars & numbers in this way but nothing about ones that have already been entered & I don't know if this is even possible (this is the first time I've ever even used .keypress() so I'm sort of new to it. Any suggestions would be much appreciated. Thanks!
Here's my entire game code:
var wordBank = ["modernism", "situationalist", "sartre", "camus", "hegel", "lacan", "barthes", "baudrillard", "foucault", "debord", "baudrillard"];
var word = [];
var answer = [];
var wrongGuesses = [];
var rightGuesses = [];
var right = [];
var images = [gallows, head, body, armL, handL, armR, handR, legL, footL, legR, footR];
var y = 0;
var i = 1;
$(document).ready(function() {
function randomWord() {
var random = Math.floor(Math.random() * wordBank.length);
var toString = wordBank[random];
console.log(toString);
word = toString.split("");
console.log(word);
}
randomWord();
function wordSpaces() {
for (var i = 0; i < word.length; i++) {
$(".word-spaces > tbody > tr").append('<td data-idx=i>' + word[i] + '</td>')
}
}
wordSpaces();
function play(space) {
//indexOf()==inArray()
var rightCount = 0;
var lIndex = jQuery.inArray(space, word);
console.log(lIndex);
if (lIndex == -1) {
wrongGuesses.push(space);
var wrong = wrongGuesses.length;
console.log('wrong ' + wrong);
$('.wrongLetters tbody tr td:nth-of-type(' + wrong + ')').text(space);
// $(this).css("background-color", "#ff4500").fadeIn(300).delay(800).fadeOut(300);
$(images[i - 1]).hide();
$(images[i]).show();
i++;
$("html").css("background-color", "#ff4500").fadeIn(300).delay(300).fadeOut(300).fadeIn(100);
console.log(word);
} else {
var totalRight = 0;
console.log(word + "word");
console.log(space + "space");
function getInstances(word, space) {
var indexes = [],
w;
for (w = 0; w < word.length; w++)
if (word[w] === space)
indexes.push(w);
return indexes;
}
console.log(word + "word");
console.log(space + "space");
var indexes = getInstances(word, space);
console.log("indexes", indexes);
indexes.forEach(function(index) {
// answer[index] = space;
rightCount++
});
console.log(rightCount + "rightcount");
console.log("answer", answer);
// rightGuesses.push(space);
console.log(rightGuesses);
// var right = rightGuesses.length;
indexes.forEach(function(index) {
$(".word-spaces tbody tr td:nth-of-type(" + (index + 1) + ")").css('color', 'black');
});
rightGuesses.push(space);
right.push(rightCount);
console.log(right + "right");
// rightGuesses.push(space);
// totalRight = totalRight + rightCount;
// totalRight++;
// console.log(totalRight + 'totalRight');
}
}
console.log(right + "right");
$(".form-control").keypress(function(event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == 13) {
var space = $(this).val().toLowerCase();
play(space);
$(this).val('');
endGame();
return false;
}
});
function endGame() {
var sumRight = right.reduce(add, 0);
function add(a, b) {
return a + b;
}
if (sumRight == word.length) {
$(images[i]).hide();
$("#victory").show();
$("body").css("background-color", "#8AFBFF");
$(".form-control").prop('disabled', true);
$("body").animate({
backgroundColor: "#0C0D86"
}, 2000);
$("body").animate({
backgroundColor: "transparent"
}, 2000);
} else if (wrongGuesses.length >= 10) {
$("body").css("background-color", "#ff4500");
$(".form-control").prop('disabled', true);
$("body").animate({
backgroundColor: "#000000"
}, 2000);
$("body").animate({
backgroundColor: "transparent"
}, 2000);
}
}
});
Use Array.indexOf(). No need for jQuery.
Do a check to see if the key pressed is contained in the wrongGuess or rightGuess array and if it is alert the user.
$(".form-control").keypress(function(event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == 13) {
var space = $(this).val().toLowerCase();
if (!(wrongGuess.indexOf(space) > -1 || rightGuess.indexOf(space) > -1)) {
play(space);
$(this).val('');
endGame();
return false;
}
else
window.alert("You already guessed this letter.");
}
});

How to real-time detect keyboard input and do NOT waste too much CPU resources on Javascript

I want to detect the barcodein real-time ,
I the USB barcode scanner to scan barcode to get a price or ISBN ,
I will detect the text field 's string length .
It will trigger some functions if match the condition
But I ran the following code and run on Firexfox for a while
then my CPU uasge is more than 100% (Intel i5 3570K 3.xGHZ) and also consume much memory,
Is there any better solution can let me achieve the task?
Thank you all.
<head>
<style type="text/css">
.close_ime{ime-mode:disabled;}
</style>
<script src="http://codeorigin.jquery.com/jquery-1.10.2.js" ></script>
<script>
$(document).ready(function () {
var pressed = false;
var chars = [];
$(window).keypress(function (e) {
if (e.which >= 48 && e.which <= 57) {
chars.push(String.fromCharCode(e.which));
}
console.log(e.which + ":" + chars.join("|"));
if (pressed == false) {
setTimeout(function () {
if (chars.length >= 10) {
var barcode = chars.join("");
console.log("Barcode Scanned: " + barcode);
// assign value to some input (or do whatever you want)
$("#barcode").val(barcode);
}
chars = [];
pressed = false;
}, 500);
}
pressed = true;
});
});
$("#barcode").keypress(function (e) {
if (e.which === 13) {
console.log("Prevent form submit.");
e.preventDefault();
}
});
</script>
</head>
<body>
<input type="text" class="close_ime" id="barcode" placeholder="Waiting for barcode scan..." size="40">
</body>
You can keep a timer variable which captures the setTimeout id. And clear it whenever there is a keypress event.
The only thing I can thing of causing performance problems is setTimeout , as you seem to unnecessarily creating an extra scope for each key press. Also once you clear the timeouts you would not be needing the pressed attribute as well.
$(document).ready(function () {
var chars = [],
timer;
$(window).keypress(function (e) {
// Clear the timer here
clearTimeout(timer);
console.log(e.which + ":" + chars.join("|"));
// You don't need the next statement if the
// keycode does not match in the first place
if (e.which < 48 && e.which > 57) return;
chars.push(String.fromCharCode(e.which));
// checking the length here
// if length less than 10 do nothing
if (chars.length < 10) return;
// Assign the id to the timer
// which will be cleared on next key press
timer = setTimeout(function () {
var barcode = chars.join("");
console.log("Barcode Scanned: " + barcode);
// assign value to some input (or do whatever you want)
$("#barcode").val(barcode);
chars = [];
}, 500);
});
});
$("#barcode").keypress(function (e) {
if (e.which === 13) {
console.log("Prevent form submit.");
e.preventDefault();
}
});

Jquery keydown() with number format not work correctly on android webview

I have encountered a strange behavior on android browser / webview. I was testing an input that will automatically format to phone number format "(xxx) xxx-xxxx". But then what happened was when I tapped or press any number on androids keyboard, the first input was like this "(x" but then the cursor was in between "(" and "x". Is there a way to put the cursor after "x" value?
I tested this on iPhone and windows web browsers and it works fine. Please let me know if there are mistakes on my jquery or javascripts.
Thanks
HTML CODE:
<html>
<head>
<title>Sample Phone Number Format</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
</head>
<body>
<input type="text" id="phone" />
<script type="text/javascript">
$('#phone').on('keydown', function (event) {
objval = $(this).val();
if (event.keyCode == 46 || event.keyCode == 8) {} else {
if (!((event.keyCode > 47 && event.keyCode < 58) || (event.keyCode > 95 && event.keyCode < 106) || (objval.length > 13))) {
event.preventDefault();
} else {
if (objval.length == 0) {
$(this).val(objval + '(');
alert(objval + '(');
} else if (objval.length == 4) {
$(this).val(objval + ') ');
alert(objval + ') ');
} else if (objval.length == 9) {
$(this).val(objval + '-');
alert(objval + '-');
} else if (objval.length >= 14) {
if (event.keyCode == 9) {
return;
} else {
event.preventDefault();
}
}
}
}
});
$('#phone').on('keydown', function (event) {
var objVal = $(this).val();
if(objVal.length < 14)
{
validateCallerForm(objVal + String.fromCharCode((96 <= event.keyCode && event.keyCode <= 105)? event.keyCode-48 : event.keyCode));
}
});
//Validates proper phone format, true if valid phone number, false otherwise
function isValidPhoneNumber(elementValue) {
var numberPattern = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
return numberPattern.test(elementValue);
}
//validates entire caller form, also updates css classes for proper response
function validateCallerForm(PhoneNumber) {
if (isValidPhoneNumber(PhoneNumber)) {
alert("true");
} else {
alert("false");
}
}
</script>
</body>
</html>
Giving +50 Bounty to who'm will answer this correctly
you have to first define listener for typing and copy-paste like below (not required) :
$("#phone").keyup( function() {
maskLine(this);
});
$("#phone").change( function() {
maskLine(this);
});
Then, to manage cursor placement, you have to cache previous phone number and then, you could compare difference and update cursor position if needed.
So declare, you have to declare a global array like this :
var _cacheElementValues = new Array();
At last, you can check the function below, it applies your mask to phone number field and manage cursor placement :
function maskLine( element ) {
element = $(element);
var maskedLine = '';
var line = element.attr('value');
// check the cache of the input and abord if no change since last treatment
if (_cacheElementValues[element.attr('id')] != undefined && _cacheElementValues[element.attr('id')] == line) {
return;
}
line = line.replace(/\D/g, ''); // remove all characters != digits
line = line.substring(0, 10);
if (line != '') {
// apply mask
if (line.length <= 2 ) {
maskedLine = "(" + line;
} else if (line.length < 6) {
maskedLine = line.replace(/^([0-9]{3})([0-9]{0,3})/g, '($1) $2');
} else {
// mask : '(XXX) XXX-XXXX'
maskedLine = line.replace(/^([0-9]{3})([0-9]{3})([0-9]{0,4})/g, '($1) $2-$3');
}
}
// define cursor position at the end of the input by default
var pos = maskedLine.length;
// Change cursor placement if necessary
if (typeof element[0].selectionStart != 'undefined') {
var start = element[0].selectionStart;
var end = element[0].selectionEnd;
var insText = element[0].value.substring(start, end);
// get current cursor placement
if (insText.length == 0) {
pos = start;
} else {
pos = start + insText.length;
}
// find how many digits typing since last mask application
var previousLength = 0;
if (_cacheElementValues[element.attr('id')] != undefined) {
previousLength = _cacheElementValues[element.attr('id')].replace(/\s/g, '').length;
}
var diff = maskedLine.replace(/\s/g, '').length - previousLength;
// if sum of new typing digit is > 0 : we change cursor placement
if (diff > 0) {
pos += (diff - 1) + Math.round((diff-1)/3);
if (pos%6 == 0 && maskedLine.length >= pos+1) pos++;
}
}
// update input data & cache
element.val(maskedLine);
_cacheElementValues[element.attr('id')] = maskedLine;
// update cursor placement
element[0].selectionStart = element[0].selectionEnd = pos;
}
You can find this example on jsFiddle : http://jsfiddle.net/UE9LB/5/
I hope this little explantion can solve your problem ;)
Enjoy !
ps: i apologize for my poor english :s
I'd recommend at least starting with an existing plugin rather than going through your own isolated rounds of solving issues.
http://digitalbush.com/projects/masked-input-plugin/
https://github.com/igorescobar/jQuery-Mask-Plugin
The short answer is to set the selectionStart and selectionEnd properties of the input element. After you format the value, set these properties to this.value.length.
this.selectionStart = this.value.length;
this.selectionEnd = this.value.length;
But, where you are going to run into trouble is when the cursor is not at the end of the text. Eg, the user has manually positioned the cursor to a position within the text. To prevent the cursor from jumping to the end, you will need to detect the cursor position before you format the input, then put the cursor back in the appropriate position after formatting.
Edit: This jsFiddle may get you started, but isn't perfect yet.
I rewrite the code on my #phone keydown event and this will work on iPhone, Android, webkit browsers.
$('#phone').on('keydown', function (event) {
if (event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39) {
// ignore if BKSPCE, left arrow, or right arrow
} else {
// validate if anything else
inputval = $(this).val();
var string = inputval.replace(/[^0-9]/g, "");
var first3 = string.substring(0,3);
var next3 = string.substring(3,6);
var next4 = string.substring(6,10);
var string = ("(" + first3 + ") " + next3 + "-" + next4);
$(this).val(string);
}
});

Categories

Resources