Counting words separated by comma - javascript

My counter function looks like that
function count() {
var value = ids.val();
return (value == '') ? 0 : value.replace(/\s,?|,$/g, '').split(',').length;
}
Then checking for returned value and posting data via ajax
if(count() === 10){
ajaxPost();
$(ids).val('');
}
I set interval to use this function. Noting that, tried all other function like change(),paste().. the only way that worked with my scanner device is, to set interval.
The problem is, when i type 9 numbers, like 1...9 and then want to type 10, when I press 1 to write 10 right after 9, it directly posts data. doesn't wait for ",". How can I modify it to wait for "," after last (in this case 10th) word?

You can check the key pressed and see if it is a comma, then only run your code after it is known that the last key entered was a comma:
$(ids).on('keyup', function (event) {
if (event.which == 188) {
if (count(this.value) === 10) {
ajaxPost();
ids.value = '';
}
}
});

10 words with a trailing comma give you 10 commas, consequently, 11 elements in the split result array (the last element will be empty if comma is the last character of the input). Check it like if (count() === 11)....

This should work...
var count = function(str){
var matches = str.match(/.*?,/g);
return (matches == null)? 0 : matches.length;
};
Also you can probably use the keyup or keydown methods to catch this event rather than using a setInterval.

If I were you, I would put this code into a keypress handler. That way it would only be invoked when you type new characters. To prevent it from running the check too often, use _.debounce. Finally, as Andrew mentioned, ",,,,,,,,,,".split(',').length == 11. It appears that you are sabotaging yourself with that regular expression that looks for an optional final comma.

Not sure if I've understood your question completely, but...
You want to wait for a last "," to make sure that all numbers were typed by your scanner? Or you need to get that 10 but the interval occurs just before the scanner finished writing it?
If it is the second case, I recommend you to restart the interval at every keyPressed event. Doing that, you'll give some time just after the key was pressed to wait in case any other key is pressed too.
So, you'll have something like this:
var lastInterval = null
$(ids).keyPress(function() {
if(lastInterval != null)
clearTimeout(lastInterval)
lastInterval = setInterval(function() {
// ... your code here
}, 1000)
})
Is this clear? Hope it helps :)
--- EDIT
Ok, it's weird that a barcode scanner doesn't trigger keyPress events, but taking this as a premise, you could check for changes in the string, and when the string didn't change in N cycles, you trigger your code.
In this example, you'll be sure that the string remained equal at least 1000 ms (between 1000ms and 1999ms).
var lastString = ""
setInterval(function() {
if(lastString == $(ids).val()) { // So, if the value remains the same for 2 cycles, the second one your code will be evaluated
// ... your code here
}
lastString = $(ids).val()
}, 1000)

Related

Allow only alphanumeric, special letters and symbols in input

I need to allow user to enter English and French characters (so I need to take in consideration chars like é,à,ç,è...etc). I also need to allow user to enter symbols and numbers.
In other words I need to prevent the user from entering non latin characters like arabic, chinese, japanese, russian ...etc
$("input:text,textarea").keydown(function(event) {
var allowed = /[A-Za-z0-9àâçéèêëîïôûùüÿñæœ!##\$%\^&\*\(\)_|}{~">:<\?/\]\[\\=\- ; ]/g;
var key = String.fromCharCode(event.which);
if (event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39 || allowed.test(key)) {
return true;
} else {
return event.preventDefault()
}
});
This works well except for some characters like ; ? / Ù even though I aded them
There's a couple of issues with the pattern you've showed. For a start, you don't have to escape all the metacharacters inside [] definition, but only those that might lead to its incorrect parsing. So quite a bit of those \ are redundant.
Also, be aware that patterns designed just for .test purposes should in general avoid /g flag, as this leads to some surprising results:
const patt = /[ab]/g;
console.log( patt.test('a') ); // true
console.log( patt.test('b') ); // false
Finally, as you didn't make your pattern case-insensitive, all the capital letters with diacritics will be missed by that. You can fix this by adding /i flag, which should work in your case, though, but... that's not the true reason your code fails.
See, the far bigger problem than all the previous combined is using event.which in combination with keydown event. Again, there are some minor issues with the approach overall (for example, how can you prevent user from pasting incorrect values inside the form - with or without a mouse?), but the particular thing that bites you is inconsistency of event.which results:
The which read-only property of the KeyboardEvent interface returns
the numeric keyCode of the key pressed, or the character code
(charCode) for an alphanumeric key pressed.
Simply said, String.fromCharCode() line works fine only for Latin characters and digits keys on the keyboard - but not for all the rest of the symbols you want to allow in the input:
// `0` pressed:
event.which - 48
String.fromCharCode - '0'
// '-' (neighbor of 0) pressed:
event.which - 189
String.fromCharCode - '½'
Now I suppose the real question is 'well, how to avoid this?'. One possible approach is checking event key property instead, which is now universally supported:
The KeyboardEvent interface's key read-only property returns the value
of the key pressed by the user, taking into consideration the state of
modifier keys such as Shift as well as the keyboard locale and layout.
In this case, however, you still need to handle copy-paste too, as well as some edge cases on keyboard input itself.
What I'd suggest as alternative is using input event to test the whole value of the input after the change, and not just individual letters. While input event is not cancellable, it's possible to revert the change it introduces, like this:
document.querySelector('input').oninput = (function() {
let oldValue = '';
const smallEl = document.querySelector('small');
const notAllowed = /[^a-zàâçéèêëîïôûùüÿñæœ0-9 \\?!##$%^&*":;~=|\-(){}<>[\]]/i;
return function(event) {
const val = event.target.value;
if (notAllowed.test(val)) {
event.target.value = oldValue;
smallEl.removeAttribute('hidden');
}
else {
oldValue = event.target.value;
smallEl.setAttribute('hidden', '');
}
}
})();
small {
color: red;
}
[hidden] {
display: none !important;
}
<label>Let's put some text here: <input /><br />
<small hidden>Non-Latin alpha characters are not allowed</small></label>
This works, but has two caveats. First, the validator now has to store the previous value of input (which raises its complexity a bit). Second, when incorrect chars are thrown into a mix and the value of input is rewritten, cursor always jumps into the end of the line. Not great, but definitely not terrible to me.
All of this won't be necessary when beforeinput event will be properly implemented in all the browsers. At this moment (Nov 2020) Firefox has it hidden under experimental flag (here's the ticket), and Chrome seems to not handle its cancellation correctly:
document.querySelector('input').addEventListener('beforeinput', (function() {
const smallEl = document.querySelector('small');
const notAllowed = /[^a-zàâçéèêëîïôûùüÿñæœ0-9 \\?!##$%^&*":;~=|\-(){}<>[\]]/i;
return function(event) {
const val = event.target.value;
if (notAllowed.test(val)) {
event.preventDefault();
smallEl.removeAttribute('hidden');
}
else {
smallEl.setAttribute('hidden', '');
}
}
})());
small {
color: red;
}
[hidden] {
display: none !important;
}
<label>Let's put some text here: <input /><br />
<small hidden>Non-Latin alpha characters are not allowed</small></label>
When I test it locally in Chrome 87, the input gets stuck immediately after processing the first 'incorrect' character, disallowing any further action on that input. There are other issues with beforeinput in Chrome, some of them stay opened for ~2 years already, so I wouldn't place my bets on this event's usability in the nearest future.
As a final sidenote, one should strongly consider checking this UX Experience thread to validate the whole approach from UX perspective. While Prevention is the only way in some cases, in others it might just lead to user frustration ("why doesn't this stupid app let me put my proper name in it???" is the typical reaction), so choose your options wisely.
I've seen that you're currently using jquery and the answer given was using pure Js, so here's a solution using Jquery:
$("input:text,textarea").on("input",function(event) {
const notAllowed = /[^a-zàâçéèêëîïôûùüÿñæœ0-9 \\?!##$%^&*":;~=|\-(){}<>[\]]/i;
return (function(event){
const val = event.target.value;
if(notAllowed.test(val)){
//Show error message
// Your code here ...
// Getting old value
let old = $(this).data('val')? $(this).data('val'):'';
event.target.value=old
}
else{
//Remove error message
// Your code here ...
//Setting current value in order to use in case a non permitted character is entered
$(this).data('val',event.target.value);
}
})(event)
});
you can then implement an error message
Filtering keypresses can work to eliminate the characters you don't want to allow, although you'll also need to handle copy/paste too.
Filter on Unicode character ranges is the most concise method I think. Pick the ranges / chars you want to allow from here: https://en.wikipedia.org/wiki/Latin_script_in_Unicode
// Allow backspace, left and right arrows
if ([8, 37, 39].includes(event.keyCode)) {
return true;
}
// Check for disallowed characters
const re = /[^\u0020-\u00ff\u0152\u0153]/;
var key = String.fromCharCode(event.which);
if (!re.test(stringValue)) {
return true
}

Why is there a difference in text length with backspace or delete (in TinyMCE)

In my TinyMCE.init method, I have a setup function like this one :
setup: function(ed){
ed.onKeyUp.add(function(ed, e){
var count = ed.getBody().innerText.length;
var key = e.keyCode || e.charCode;
console.log(count);
console.log(ed.getBody().innerText);
});
}
If my textarea is empty, when I press Backspace (key = 8), count equals 0.
When I press Delete (key = 46), count equals 1.
In both cases, console.log(ed.getBody().innerText); returns an empty string.
I want to use this to count (and limit) the size of my TinyMCE.
Does anyone can illuminate me about that strange difference ?
Delete is character code 127 in the ASCII-Table. The delete char is written into the textinput and therefore counts to the length of it, but is not displayed, because control characters dont get displayed.
This is indeed strange behaviour, because actually the delete character should not be written into the text field, but it seems like it does

How can I stop isNaN from returning an error for a blank field?

EDIT:
Ok so I'm updating this question, to show what I've built as I've still not been able to fix this issue. Here is an image of what I've got. So as you can see,
When the user enters a value, the calculation (they are just percentage and total calculations are done "onkeyup". As you can see because of this they return "NaN". Is there a way for me to stop the field displaying a NaN and then subsequently only showing the total values?
I have thought about this and I could just get all the fields to calculate as soon as something is input into the final field? What do you think. Apologies to all those that had perviously answered my question, I am still trying to figure out the best approach, I'm just not as good with JavaScript as I am with HTML/CSS!!
You should try writing a checkNumber function that takes the entered value as its argument (rather than referring directly to each field inside the function). Something like this:
var checkNumber = function (testval) {
if ( isNaN(testval) ) {
alert('Bad!');
// clean up field? highlight in red? etc.
} else {
// call your calculation function
}
}
Then bind that function to the keyup event of each form field. There are a number of ways to do this. Look into addEventListener(), or the binding features of a framework like jQuery (.delegate() or .keyup(), e.g.).
Note that if you do bind the function to the event, you won't have to explicitly pass in the value argument. You should be able to work with a field's value within the function via this.value. So you'd have something like this:
var checkNumber = function () {
if ( isNaN( this.value ) ) {
alert('Bad!');
// clean up field? highlight in red? etc.
} else {
// call your calculation function
}
}
And then (with a naive binding approach by ID):
document.getElementById('id_of_a_field').addEventListener('keyup', checkNumber, true);
Can't you just initialize the text box with a default value, say 0?
Why don't you use 3 different functions or an argument to identify which of the inputs the user is pressing? If each of the inputs calls checkNumber(1), checkNumber(2) and checkNumber(3) you can only validate the input that the user is using instead of validating all 3 at the same time.
Alternatively you can use input validation and instead of an alert just return false to prevent the user from inputing invalid chars
How about use short-circuit evaluation with jsFiddle example
EDIT for parseFloat:
function checkNumber()
{
var sInput = parseFloat(document.getElementById('sInput').value || 0);
var dInput = parseFloat(document.getElementById('dInput').value || 0);
var pInput = parseFloat(document.getElementById('pInput').value || 0);
if (isNaN(sInput) || isNaN(dInput) || isNaN(pInput)) {
alert("You entered an invalid character. Please press 'Reset' and enter a number.");
}
}
So if pInput is undefined just use 0, but if the input has value then use that value.
SIDE NOTE: white space is actually a number, +' '; // 0

Jquery text change event

I need to fire an event anytime the content of a textbox has changed.
I cant use keyup nor can I use keypress.
Keyup and keydown doesn't work if you hold down on the key.
Keypress triggers before the text has actually changed. It doesn't recognize backspace or delete either.
So now I'm assuming I'm going to have to build some custom logic or download a plugin. Are there any plugins out there? Or if I should build one, what constraints should I look out for?
For eg. Facebook does it with their search at the top. you can press and hold.
another example is writing a stackoverflow question. Right below the editor, the contents are copied in real time, backspace and everythng works. How do they do it?
I just took a look at SO's source. It looks like they do something a lot like this:
function updatePreview(){
$('div').text($('textarea').val());
}
$('textarea').bind('keypress', function(){
setTimeout(updatePreview, 1);
}
);​
They do some extra stuff to make HTML tags for bold and italics and links and such and they time it. They increase the delay from 1 to longer if it takes too long to generate the HTML.
I had success using jQuery (in Chrome). If you hold a key down, it counts every change, not just the first one, and it counts non-print keys like backspace.
HTML
<input id="txt" type="text" />
<span id="changeCount">0</span>
JavaScript
$('#txt').keydown(function(event) {
// Don't count the keys which don't actually change
// the text. The four below are the arrow keys, but
// there are more that I omitted for brevity.
if (event.which != 37 && event.which != 38 &&
event.which != 39 && event.which != 40) {
// Replace the two lines below with whatever you want to
// do when the text changes.
var count = parseInt($('#changeCount').text(), 10) + 1;
$('#changeCount').text(count);
}
});
Like I said above, you'll want to filter out all of the key codes that don't change the text, like ctrl, shift, alt, enter, etc. There's also the boundary condition if you press the backspace or delete key when the textbox is empty or if the textbox has a maximum length and a printable key is pressed, but it's not terribly difficult to handle those either.
Here's a working jsfiddle example.
How about a poll? Do a setInterval and call a function that checks the text say every 500ms? You don't want to detect content change on every key anyway because it gets kinda slow in some older browser/older computer and you would notice a lag between typing and the text displaying.
You need a watcher type functionality.
It resorts to setInterval polling if the other features are not available: http://james.padolsey.com/javascript/monitoring-dom-properties/
I have a simple solution that we use happily in one of our project.
you can try it # http://jsfiddle.net/zSFdp/17/
var i = 0;
$('#text').bind('check_changed', function(){
var t = $(this);
// do something after certain interval, for better performance
delayRun('my_text', function(){
var pv = t.data('prev_val');
// if previous value is undefined or not equals to the current value then blablabla
if(pv == undefined || pv != t.val()){
$('#count').html(++i);
t.data('prev_val', t.val());
}
}, 1000);
})
// if the textbox is changed via typing
.keydown(function(){$(this).trigger('check_changed')})
// if the textbox is changed via 'paste' action from mouse context menu
.bind('paste', function(){$(this).trigger('check_changed')});
// clicking the flush button can force all pending functions to be run immediately
// e.g., if you want to submit the form, all delayed functions or validations should be called before submitting.
// delayRun.flush() is the method for this purpose
$('#flush').click(function(){ delayRun.flush(); });
The delayRun() function
;(function(g){
var delayRuns = {};
var allFuncs = {};
g.delayRun = function(id, func, delay){
if(delay == undefined) delay = 200;
if(delayRuns[id] != null){
clearTimeout(delayRuns[id]);
delete delayRuns[id];
delete allFuncs[id];
}
allFuncs[id] = func;
delayRuns[id] = setTimeout(function(){
func();
delete allFuncs[id];
delete delayRuns[id];
}, delay);
};
g.delayRun.flush = function(){
for(var i in delayRuns){
if(delayRuns.hasOwnProperty(i)){
clearTimeout(delayRuns[i]);
allFuncs[i]();
delete delayRuns[i];
delete allFuncs[i];
}
}
};
})(window);
Zurb has a great plugin which might be useful for you
http://www.zurb.com/playground/jquery-text-change-custom-event

Why do I get wrong, doubled input value on keyup when typing fast?

$("#input").keyup(function(){
console.log($(this).val());
})
When one is typing slowly "a" & "b" the above code will result in "a", "b" in the console log. But the problem is when somebody does it quickly. The result then is "ab", "ab". It's easier to repeat this situation with letters which are near on a keyboard e.g. "e" & "r". How to avoid it?
Events keydown and keypress does not suffer from this problem of quick-typist, but they are also fire to early. Result: returned value of an input does not contain the last typed letter when they occur. Or maybe there is a way to get this last letter somehow?
Well, the problem is not really fast typing, but actually when the key up event happens. Consider this:
a is pressed
b is pressed
a is released
b is released
No matter how slowly this is done, you will not get the key up event until the first key is released. There is obviously no way around this, you can't cause the key up event before the key is released.
If you just want the key that is pressed in a key press event, you can get it from the event object. Example:
$('#input').keypress(function(e){
console.log(e.which);
});
I had a similar problem and found a workaround. Basically, I created an array with the timeStamp of the event (e.timeStamp) for every time the keyup fired. Then I compared the last two values in the array (the two most recent) and stopped the event if the difference in time wasn't at least 100ms.
This is my code inside the function called by the keyup listener:
timeArray.push(e.timeStamp); // add new timeStamp to array
if (timeArray.length >= 2) {
var diff = (timeArray[timeArray.length - 1] - timeArray[timeArray.length - 2]);
if (diff > 100) { // 1 second
// perform desired action
} else { return false; }
} else {
// still perform desired action if array only has one timestamp
}

Categories