Auto Updating the Value of an Input Field - javascript

I'm not sure how simple or how complicated this question may as I am just learning javascript, but here it goes.
Say I have an input field:
<input type="text" value=""/>
I am trying to write a javascript function that checks the input value every time a space is entered, and then edit the value live while the user is typing.
For example, say I start typing: hello, how are u
After I hit space following 'u', I would like to replace 'u' with 'you'.
So, if anyone who be so kind, where do I start here? This may be ironic because I see the text I am typing right this instant being updated just to the bottom of this textarea. However, I am trying to get it to update in the field itself.
Thanks!!!

You don't necessarily need jQuery to do this, but it is a little more work with vanilla JS. First, give your input an ID:
<input id="ValueField" type="text" value="" />
Then add an event listener:
var field = document.getElementById('ValueField');
field.addEventListener('keyup',function(e){
if(e.keyCode==32){
// do your stuff
}
});
There are a million ways to detect change, but first thing that comes to mind is a function:
var field = document.getElementById('ValueField');
field.addEventListener('keyup',function(e){
if(e.keyCode==32){
field.value = replaceValues(field.value);
}
});
function replaceValues(fieldval){
var baditems = [' u ',' i '],
newitems = [' you ',' I '],
length = baditems.length;
for(var i = length; i--;){
fieldval = fieldval.replace(baditems[i],newitems[i]);
}
return fieldval;
}
Just build out the array of bad and new items to be the same length with their order matching and you should be good. This way you can replace as many items you want, and should run pretty quickly.
EDIT
Might help if I have the function return something! Here is a jsFiddle to show example. Also, I had to add spaces around the u and i because otherwise it would just match any instance of the letter.

if you are using jquery, look at this: https://stackoverflow.com/a/2249215/146602
good way to detect if a space character is being used.

Related

Directive for restricting typing by Regex in AngularJS

I coded an angular directive for inhibiting typing from inputs by specifying a regex. In that directive I indicate a regex that will be used for allow the input data. Conceptually, it works fine, but there are two bugs in this solution:
In the first Plunker example the input must allow only numbers or numbers followed by a dot [.], or numbers followed by a dot followed by numbers with no more than four digits.
If I type a value '1.1111' and after that I go to the first digit and so type another digit (in order to get a value as '11.1111') , nothing happening. The bug is in the fact I use the expression elem.val() + event.key on my regex validator. I do not know how to get the whole
current value for a input on a keypress event;
The second one is the fact that some characters (grave, acute, tilde, circumflex) are being allowed on typing (press one of them more than once), althought the regex does not allow them.
What changes do I need to make in my code in order to get an effective type restriction by regex?
<html ng-app="app">
<head>
<script data-require="angularjs#1.6.4" data-semver="1.6.4" src="https://code.angularjs.org/1.6.4/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Restrict typing by RegExp</h1>
PATTERN 1 (^\d+$|^\d+[.]$|^\d+[.]\d{1,4}$) <input type="text" allow-typing="^\d+$|^\d+[.]$|^\d+[.]\d{1,4}$"/><br>
ONLY NUMBERS <input type="text" allow-typing="^[0-9]+$"/><br>
ONLY STRINGS <input type="text" allow-typing="^[a-zA-Z]+$"/>
</body>
</html>
Directive
angular.module('app', []).directive('allowTyping', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs, ctrl) {
var regex = attrs.allowTyping;
elem.bind('keypress', function(event) {
var input = elem.val() + event.key;
var validator = new RegExp(regex);
if(!validator.test(input)) {
event.preventDefault();
return false;
}
});
}
};
});
If this were my code, I'd change tactics entirely: I would listen for input events instead of trying to micromanage the user interactions with the field.
The approach you are taking, in general, has problems. The biggest one is that keypress won't be emitted for all changes to the field. Notably,
It is not triggered by DELETE and BACKSPACE keys.
Input methods can bypass it. When you entered diacritics as diacritics, your code was not registering the change. In general, if the user is using an input method, there is no guarantee that each new character added to the field will result in a keypress event. It depends on the method the user has chosen.
keypress does not help when the user cuts from the field or pastes into the field.
You could add code to try to handle all the cases above, but it would get complex quick. You've already run into an issue with elem.val() + event.key because the keypress may not always be about a character inserted at the end of the field. The user may have moved the caret so you have to keep track of caret position. One comment suggested listening to keyup but that does not help with input methods or paste/cut events.
In contrast, the input event is generated when the value of the field changes, as the changes occur. All cases above are taken care of. This, for instance, would work:
elem.bind('input', function(event) {
var validator = new RegExp(regex);
elem.css("background-color", !validator.test(elem.val()) ? "red" : null);
});
This is a minimal illustration that you could plop into your fiddle to replace your current event handler. In a real application, I'd give the user a verbose error message rather than just change the color of the field and I'd create validator just once, outside the event handler, but this gives you the idea.
(There's also a change event but you do no want to use that. For text fields, it is generated when the focus leaves the field, which is much too late.)
See Plnkr Fixed as per your approach:
The explanation of why and the changes are explained below.
Side note: I would not implement it this way (use ngModel with $parsers and $formatters, e.g. https://stackoverflow.com/a/15090867/2103767) - implementing that is beyond the scope of your question. However I found a full implementation by regexValidate by Ben Lesh which will fit your problem domain:-
If I type a value '1.1111' and after that I go to the first digit and so type another digit (in order to get a value as '11.1111') , nothing happening.
because in your code below
var input = elem.val() + event.key;
you are assuming that the event.key is always appended at the end.
So how to get the position of the correct position and validate the the reconstructed string ? You can use an undocumented event.target.selectionStart property. Note even though you are not selecting anything you will have this populated (IE 11 and other browsers). See Plnkr Fixed
The second one is the fact that some characters (grave, acute, tilde, circumflex) are being allowed on typing (press one of them more than once), althought the regex does not allow them.
Fixed the regex - correct one below:
^[0-9]*(?:\.[0-9]{0,4})?$
So the whole thing looks as below
link: function(scope, elem, attrs, ctrl) {
var regex = attrs.allowTyping;
elem.bind('keypress', function(event) {
var pos = event.target.selectionStart;
var oldViewValue = elem.val();
var input = newViewValue(oldViewValue, pos, event.key);
console.log(input);
var validator = new RegExp(regex);
if (!validator.test(input)) {
event.preventDefault();
return false;
}
});
function newViewValue(oldViewValue, pos, key) {
if (!oldViewValue) return key;
return [oldViewValue.slice(0, pos), key, oldViewValue.slice(pos)].join('');
}
}
You specified 4 different patterns 3 different pattens in your regex separated by an alteration sign: ^\d+$|^\d+[.]$|^\d+[.]\d{1,4}$ - this will not fulfill the criteria of input must allow only numbers followed by a dot [.], followed by a number with no more than four digits. The bug "where nothing happens" occurs because the variable you are checking against is not what you think it is, check the screenshot on how you can inspect it, and what it is:
Can not reproduce.
You can change the event to keyup, so the test would run after every additional character is added.
It means you need to save the last valid input, so if the user tries to insert a character that'll turn the string invalid, the test will restore the last valid value.
Hence, the updated directive:
angular.module('app', [])
.directive('allowTyping', function() {
return {
restrict : 'A',
link : function(scope, elem, attrs, ctrl) {
var regex = attrs.allowTyping;
var lastInputValue = "";
elem.bind('keyup', function(event) {
var input = elem.val();
var validator = new RegExp(regex);
if (!validator.test(input))
// Restore last valid input
elem.val(lastInputValue).trigger('input');
else
// Update last valid input
lastInputValue = input;
});
}
};
});

get the new added characters to an input by js

I know this seems a quite easy target. I have an input[type=text], and I want to detect the new added character(s) in it. The normal way is:
$selector.keypress(function(e) {
//do sth here
var newchar = String.fromCharCode(e.which);
});
But the above method not working properly for some browsers on android devices. Typing the android virtual keyboard will not fire the keypress.
Then I found the following method is better:
$selector.on('input', function(e){
//do sth here
});
It works fine for android devices, and also, it can detect cut/paste.
Now the question is, is there a way to know the new added character(s) to the input? Do I need to do the complicated string comparison during inputing each time, i.e. compare the previous string and the new string in the input box? I said it's complicated because you may not always type in char(s) at the end, you may insert some char(s) in the middle of the previous string. Think about this, the previous string in the input box is "abc", the new string after pasting is "abcxabc", how can we know the new pasted string is "abcx", or "xabc"?
The method from keypress is quite simple:
String.fromCharCode(e.which);
So, is there similar way to do this by the on('input') method?
After reading Yeldar Kurmangaliyev's answer, I dived into this issue for a while, and find this is really more complicated than my previous expectation. The key point here is that there's a way to get the cursor position by calling: selectionEnd.
As Yeldar Kurmangaliyev mentioned, his answer can't cover the situation:
it is not working is when you select text and paste another text with
replacing the original one.
Based on his answer, I modified the getInputedString function as following:
function getInputedString(prev, curr, selEnd) {
if (selEnd === 0) {
return "";
}
//note: substr(start,length) and substring(start,end) are different
var preLen = prev.length;
var curLen = curr.length;
var index = (preLen > selEnd) ? selEnd : preLen;
var subStrPrev;
var subStrCurr;
for(i=index; i > 0; i--){
subStrPrev = prev.substr(0, i);
subStrCurr = curr.substr(0, i);
if (subStrCurr === subStrPrev) {
var subInterval = selEnd - i;
var interval = curLen - preLen;
if (interval>subInterval) {
return curr.substring(i, selEnd+(interval-subInterval));
}
else{
return curr.substring(i, selEnd);
}
}
}
return curr.substring(0, selEnd);
}
The code is quite self explanation. The core idea is, no matter what character(s) were added(type or paste), the new content MUST be ended at the cursor position.
There's also one issue for my code, e.g. when the prev is abcabc|, you select them all, and paste abc, the return value from my code will be "". Actually, I think it's reasonable, because for my scenario, I think this is just the same with delete the abc from previous abcabc|.
Also, I changed the on('input') event to on('keyup'), the reason is, for some android browsers, the this.selectionEnd will not work in a same way, e.g., the previous text is abc|, now I paste de and the current string will be abcde|, but depending on different browsers, the this.selectionEnd inside on('input') may be 3, or 5. i.e. some browsers will report the cursor position before adding the input, some will report the cursor position after adding the input.
Eventually, I found on('keyup') worked in the same way for all the browsers I tested.
The whole demo is as following:
DEMO ON JSFIDDLE
Working on the cross-browser compatibility is always difficult, especially when you need to consider the touch screen ones. Hope this can help someone, and have fun.
Important notes:
when a user types in a character, the cursor stands after it
when a user pastes the text, the cursor is also located after the pasted text
Assuming this, we can try to suggest the inputed \ pasted string.
For example, when we have a string abc and it becomes abcx|abc (| is a cursor) - we know that actually he pasted "abcx", but not "xabc".
How do this algorithmically? Lets assume that we have the previous input abc and the current input: abcx|abc (cursor is after x).
The new one is of length 7, while the previous one is of length 4. It means that a user inputed 4 characters. Just return these four characters :)
The only case when it is not working is when you select text and paste another text with replacing the original one. I am sure you will come up with a solution for it yoruself :)
Here is the working snippet:
function getInputedString(prev, curr, selEnd) {
if (prev.length > curr.length) {
console.log("User has removed \ cut character(s)");
return "";
}
var lengthOfPasted = curr.length - prev.length;
if (curr.substr(0, selEnd - lengthOfPasted) + curr.substr(selEnd) === prev)
{
return curr.substr(selEnd - lengthOfPasted, lengthOfPasted);
} else {
console.log("The user has replaced a selection :(");
return "n\\a";
}
}
var prevText = "";
$("input").on('input', function() {
var lastInput = getInputedString(prevText, this.value, this.selectionEnd);
prevText = this.value;
$("#result").text("Last input: " + lastInput);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<div id="result">Start inputing...</div>

attribute maxlength of input field is changing, but input doesn't care

I have a function to limit the number of characters that a user can type into an input field for my game. It works, except that if it goes down by 1 or more in length, the user can still enter 1 or more characters than they should be able to.
I check the inspector, and it even shows maxlength changing correctly. However, for whatever reason, it still lets the user enter in a length equal to the max number of characters that the variable was equal to during the same session. Is it a bug? Any way to get it working correctly?
my_var = 150000; //this var changes often, can go down to 0 or up to 1000000000
function limitNumberOfCharacters() {
x = my_var.toString().length;
$('.my_input_class').attr('maxlength', x);
}
limitNumberOfCharacters(); //this gets called often
EDIT: http://jsfiddle.net/mDw6f/
EDITTTT:
You are using x as a global variable and is probably getting changed from something else in your code. Use var x = my_var.toString().length; (emphasis on var)
Honestly after seeing this code I was afraid there would be many more underlying problems but all I did was add var before the xyz and it works just as you want it to.
Also fixed the issue of the previous bet amount returning to the input field. It now results to a blank field.
Cheers
Real Fiddle Example
Try using this fiddle:
Working Demo
Use the html input like I did in the code, no need to specify the maxlength attribute to it.
<input type="text" class="my_input_class"/>
and the script
my_var = 25; //this var changes often, can go down to 0 or up to 1000000000
function limitNumberOfCharacters() {
x = my_var.toString().length;
$('.my_input_class').attr('maxlength', x);
}
limitNumberOfCharacters();

String creation and comparison in Drag n' drop game

I'm trying to make a simple drag and drop game where users need to re order someone's name by dragging characters into a dropping zone.
I'm ok with the drag and drop animations but I'm not being able (mostly due to technical lack of skills) to create strings with this letters in order to make a comparison between both of them.
Check my example code here jsFiddle
I'm creating the first string with the name before I randomize all the letters dragItemsContent = [];
I'm kinda being able to create a new string for letters I'm dragging dragedItemsContent += ui.draggable.text();
But when I wan't to delete any of those letters I can't delete them, and the worst thing is I have no idea how to look for that letter's index and delete it properly from my string.
I'm using data-letra which is a unique indicator for each letter, maybe it can help.
So, to sum up, I need to add/remove letters (or some comparable data) to my strings and compare them to know if the users finish the game correctly.
Thanks
Very fun game.
I have edited it to display You Won! or Sorry. Please try again. based on the results (Spelling out Fredfigglehorn.
Clicky.
The important part is:
var letters = $('.drop-area').find('.drag-item');
if (letters.length == dragItemsLength ) {
var final = '';
for (var i=0;i<dragItemsLength;i++)
final += letters[i].innerText.substring(0, 1);
if (final == 'fredfigglehorn')
alert("You won!");
else
alert("Sorry. Please try again.")
}
Instead of dragedItemsContent += ui.draggable.text(); you should use dragedItemsContent.push(ui.draggable.text()) this way you will get a good array in proper order. Later you can use toString to make your string and to remove simply remove the element from the array
You might also want to check out sortable from jQuery UI which can handle almost everything out of the box
I have made a fiddle for u which does comparison based drag items text hope it helps u.. http://jsbin.com/oluhuk/2/edit

jquery masked input plugin to not clear field when errored

I'm looking at the http://digitalbush.com/projects/masked-input-plugin/
I'm calling it like this:
$(control).mask('999-999-9999');
And I don't want it to throw away the users input if something is wrong, e.g. they haven't finished
[407-555-____]
If you leave the field after having typed this much, it clears it. I'd like to leave it so they can finish later.
I'm new to jQuery, and I've looked through his source, but I can't find any way to do that, nor can I find any way to edit it to accomplish what I want, because the code is arcane to my eyes.
Set autoclear option to false.
$(control).mask('999-999-9999', {autoclear: false});
It looks like I should just make the whole mask optional:
mask('?999-999-9999')
That way the control thinks what the user has is "valid" and I can continue. Even though it isn't really the optional part of the mask.
You should delete statement input.val(""); in checkVal() function for a proper solution.
If you're using minified version, you should search and delete statement:
if(!a&&c+1<i)f.val(""),t(0,k);else
Try update file jquery.maskedinput.js
In function function checkVal(allow) set parameter allow on true. Its help for me.
function checkVal(allow) {
allow = true; ///add this command
//..............
}
In addition to removing the input.val("") in checkVal() you can also change the call to clearBuffer.
In the original code it is: clearBuffer(0, len); removing all user input.
if you change this to clearBuffer(lastMatch + 1, len); the user input will be displayed, followed by the mask placeholders that are still needed to complete correct input.
I have also added a user message in the .bind. This works for us, as we are using the MaskedInput for exactly one type of input. I'm checking for any input going further than position 7, because that's where the user input starts.
Here is what I did:
.bind("blur.mask", function() {
// find out at which position the checkVal took place
var pos = checkVal();
// if there was no input, ignore
if (pos <=7) {input.val(""); clearBuffer(0, len);}
// if the user started to input something, which is not complete, issue an alert
if (pos > 7 && pos < partialPosition) alert("Tell the user what he needs to do.");
if (input.val() != focusText)
input.change();
})
Adding Placeholder could solve the problem.
$(control).mask('999-999-9999');
Add an empty place holder into mask. see below
$(control).mask('999-999-9999', { placeholder: "" });
which would replace _ on the input text field by default. so there would bot be any _ left if the input length is dynamic and not fixed.
Looking for into the pluging script the unmask method.
$('#checkbox').unmask();

Categories

Resources