I want to trim a text in a field when the user is writing or paste text on it, the following code works great but has only one problem, when the user selects all the text to replace it with the pasted text or put the cursor to insert the pasted text inside, the code repeats the present value + the new value at the end, How can I fix it?
$(document).on('paste', "input[type='text'], textarea", function(e) {
let val = $(this).val();
val = val.trim();
val = val.replace(/(?:^\s+|\s+?(\s))(\S+)/g, "$1$2");
val = val.replace(/\s+(\s)$/, "$1");
$(this).val( val );
} );
I believe what I need to do is to paste normally, get the final pasted text and trim it, but that is the question How to get the final pasted text? Can you help?
In short, the paste event isn't a pipeline that allows you to change the pasting text on the fly. You can access the clipboard, but getting the data from it is a bit challenging as there is questionable browser support not only for the paste event, but for the clipboardData within said event.
onChange or onBlur are your best bets, as this is a great time to update your values even after a user pastes their value:
$("input[type='text'], textarea").bind('blur', function(e) {
$(this).val($.trim($(this).val()));
} );
This will work, whether they type in the whitespaces, or paste them in. If it has to be pasted, then you may want to instead have the above, and a paste event, but the paste event merely sets a shared boolean like "valueIsPasted" to be true, and then you can do the above only when that value is true. I don't love that solution, but unfortunately, the paste support isn't great. On top of that, that still will not work in IE, which doesn't support the paste event.
Update:
Looks like W3Schools begs to differ on browser support; however, I still feel that onChange or onBlur are your best bets as it will allow you to change the value once the user is done editing the value or done with the control, and definitely work. Check out my codepen.
The issue is that your trim code is running first (causing one copy of the data to be placed in the element) and the paste is happening second (causing a second copy of the data at the end).
If we delay the trim code, the issue is resolved very simply:
$("#txtInput, textarea").on('paste', function(e) {
// Store the invocation context (the input element)
// because it will change in the setTimeout
var ctx = this;
// Wait 1/10th of a second before trimming the data
// This will allow the paste to complete normally.
setTimeout(function(){
$(ctx).val($(ctx).val().trim());
}, 100);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="txtInput" size="40" value=" there are leading and trailing spaces here ">
<textarea></textarea>
Related
Does the beforeinput event provide a convenient way to preview the result of a proposed modification so that it can be blocked if necessary for validation purposes?
I'm not looking for alternative ways to do input validation; I'm already well-aware of methods involving the keypress and input events, as well as HTML5 validation, etc.. Right now, I'm specifically looking into the beforeinput event to see what it offers.
So far, this is the best I've come up with:
document.getElementById("phone").addEventListener("beforeinput", function(e) {
if(!/^(\d{0,7}|\d{3}-\d{0,4}|)$/.test(e.target.value + (e.data ?? ""))) {
e.preventDefault();
}
return;
});
<input id="phone">
The text field in the above snippet should accept a simple 7-digit phone number with optional dash after the 3rd digit.
Notice that I'm appending the event's data property to the input's current value to create a preview of the modified value. This works fine if you only enter input sequentially. But if, for example, you type in all 7 digits and then arrow back to just after the 3rd and try to insert the dash, you can't do it, because the validation assumes you're at the end where a dash would be invalid. Other problems arise if you try to replace or delete a selection.
An accurate preview will be required to solve these problems. Is there a simple way to get one from the beforeinput event?
You'd have to get the selectionStart and selectionEnd to figure out how many characters were removed / replaced / etc, but yeah, pretty simple:
document.getElementById("phone").addEventListener("beforeinput", function(e) {
const nextVal =
e.target.value.substring(0, e.target.selectionStart) +
(e.data ?? '') +
e.target.value.substring(e.target.selectionEnd)
;
console.log(nextVal)
if(!/^(\d{0,7}|\d{3}-?\d{0,4}|)$/.test(nextVal)) {
e.preventDefault();
}
return;
});
<input id="phone">
I have one more input box - ibox2, on the same page.
Problem - After doing anything on ibox1 and leaving value of length > 5 there, if I start typing in ibox2 the focus jumps back to ibox1.
It is that if loop with ibox1.focus() that is doing it. How could I remove focus entirely from ibox1 upon clicking outside and nullify the if loop and its statements.
I tried blurbut it did not work.
var ibox1 = $("#inputbox1");
$(document).on("change", ibox1,function(e) {
var valu = ibox1.val();
if(valu.length > 5){
#do something
ibox1.focus(); #used this as input box lost focus with each charater typed.
}
});
var ibox2 = $("#inputbox2"); #This is for google places autocomplete.
PS - Please do not tag it as a duplicate one, I have tried almost everything here, and only then I posted this. I shall remove it upon getting solution.
Respected mods, I followed a nice accepted answer and made a mistake about understanding $('document'), but I now got it cleared. That's the reason I am not deleting this question, even though I said I would, as it might help others. You guys, if
you feel, could delete this. Thanks.
The focus is jumping back to ibox1 because you are instructing your document to do so each time the onChange event is fired.
e.g.: $(document).on("change", ibox1, funct... where you are calling for ibox1.focus(); `.
Possible solution: bind your change event to the element of interest itself and avoid binding an event of such local significance to the whole document in the future.
Use a simple method to attach an event to inputs. Check below code it may help you.
(function(){
var in1 = jQuery('#input1'); // first input
var in2 = jQuery('#input2'); // second input
// On change of first input
in1.change(function(){
if(this.val().length > 5){
// do something
}
});
// On change of second input
in2.change(function(){
if(this.val().length > 5){
// do something
}
});
})();
I was wondering if someone can point me into the right direction. I have a text input field, that I now need to update so a user can only "edit" through a scan gun to capture a bar code. My code so far was updated to prevent the user from adding data via keyboard but I am unsure how I can make it "editable" again via scan. During testing scanning was treated similar to key presses:
$('#barCodeNum').keydown(function(){
$('#barCodeNum').attr('readonly', true);
});
$('#barCodeNum').keyup(function(){
$('#barCodeNum').attr('readonly', true);
alert("You cannot manually edit this field. Please Scan the item.");
});
What event should I be using, if there is any form of an event to capture the "action" of scanning? Or has anyone found another solution to handle this kind of scenario?
EDIT
Another idea... From Anthony's comment about an hidden field...
But! An hidden field just can't be focussed... And you need it to use the codebar reader.
So what about to place that "other" input outside the viewport (using CSS) instead of hiding it... Doing this, no one can focus it and just type in.
So in order to focus it, you will also need an "enable scan" button. The button will also start an interval to check for a value inputed fast! say... within 100ms... Then blur the field.
Here, there's only one delay to handle: The full scan minimum time. So that is easier...
No human can enter a complete code so fast.
Then you just need to validate that the code length is correct, based on your typical codes. If just one or two character are in... It's sure invalid.
You'll have to adjust the "maxScanDelay" by testing your scanner... 100ms may be too short. But make it as short as possible. ;)
Look below:
var ScanCheck;
var maxScanDelay = 100;
$('#barCodeNum').on("keydown",function(e){
e.preventDefault();
}); // End keydown on the visible
$('#barCodeNumHidden').on("input",function(e){
$('#barCodeNum').val($(this).val());
}); // End keydown on the hidden
$("#scanEnable").on("click",function(){
// Clear the fields
$('#barCodeNum,#barCodeNumHidden').val("");
$("#barCodeNumHidden").focus();
$(this).text("Waiting for the code.");
ScanCheck = setInterval(function(){
console.log("interval");
if($('#barCodeNumHidden').val()!=""){
$("#barCodeNumHidden").blur();
$("#scanEnable").text("Enable scan.");
clearInterval(ScanCheck);
console.log("interval stopped");
}
},maxScanDelay);
});
#barCodeNumHidden{
position:fixed;
top:-100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="barCodeNum" readonly>
<input type="text" id="barCodeNumHidden">
<br>
<br>
<button id="scanEnable">Enable scan.</button>
Typing speed solution
If you make it readonly... The codebar reader won't be able to fill the input.
Since just return false; does not work...
I think I found a way to grab the characters typed FAST, like no human can, and get rid of the character typed more slowly.
I have no codebar reader to test that...
You will have two delays to adjust :
A threshold (key must be typed faster than this delay)
An "output" delay (to allow all characters to be collected before an output)
Here the code to try:
var string = "";
var timeout;
var lastEventTime = 0;
// ADJUST THOSE TWO!
var threshold = 35;
var outputDelay = 100;
$('#barCodeNum').on("keydown",function(e){
e.preventDefault();
var codebarInput = $(this);
// Get current time.
var thisEventTime = Date.now();
console.log(lastEventTime);
console.log(thisEventTime);
// Grab the character.
string += String.fromCharCode(e.keyCode)
console.log(string);
// If this event occurs sooner than the threshold delay, use the timeout to output the value when all characters are in string.
if(lastEventTime+threshold > thisEventTime){
console.log("OK");
// Output the string after a delay.
clearTimeout(timeout);
timeout = setTimeout(function(){
codebarInput.val(string);
},outputDelay);
// If this event occurs after the threshold delay, clear string and input value.
}else{
console.log("NOT OK");
codebarInput.val("");
string = "";
console.log("Key prevented.");
}
// Keep this event time.
lastEventTime = thisEventTime;
}); // End keydown
From my tests on CodePen, a HOLDED down key works with a 35ms threshold... That seems to be the lowest, since 30ms block everything. With this threshold, even if I type as fast as I can, no single keys are passing. ;)
Usually barcode scanners act as a keyboard input, which would prevent you from knowing whether its a keyboard or a scanner.
One way to get around this is to check the timing between keypresses. The barcode scanner inputs much faster than a human, and I believe you can modify this speed.
If the typing is slow enough you'll know its a keyboard. If the typing is within some threshold you will know its the scanner.
I've built a page where you can filter results by typing into an input box.
Basic mechanics are:
Start typing, input event is fired, elements without matching text begin hiding
If input becomes empty (or if you click a reset button), all elements are shown again
I have noticed a problem, though, when highlighting text. Say I type "apple" into the input. Then I highlight it, and type "orange."
If an element exists on the page containing "orange," but it was already hidden because I filtered for "apple," it does not show up. I have gathered this is because the input never truly empties; rather, I simply replace "apple" with the "o" from orange before continuing with "r-a-n-g-e." This means I get a subset of "apple" results that contain "orange," as if I had typed "apple orange."
What I really want to do is clear my input on the keypress for the "o" in "orange" before hiding nonmatching elements, so I'm effectively searching the whole page for "orange."
What I've tried so far
1: Set input value to '' on select event:
$('.myinput').on('select', function(){
$(this).val('');
});
This doesn't work because it just deletes my highlighted text, which is unexpected. I only want to reset the input on the keypress following the highlight.
2: Include an if statement in my input event that checks if there is a selection within the input:
$('.myinput').on('input', function(){
var highlightedText = window.getSelection();
if($(highlightedText).parent('.myinput')) {
//reset my input
}
});
This doesn't work because it seems to fire on every keypress, regardless of if there is any actual selection. (Are user inputs always treated as selected?)
3: Add a select event listener to the input element, and set a variable to true if there's a selection. Then, in my input event, check if the variable is true on keypress.
$(function(){
var highlightedText = false;
$('.myinput').on('input', function(){
if(highlightedText = true) {
//reset my input
}
//do stuff
highlightedText = false;
});
$('.myinput').on('select', function(){
highlightedText = true;
});
});
I really thought this one would work because a basic console log in the select function only fires when I want it to – when text in the input is highlighted, but not when other text is highlighted and not when text is entered into the input. But alas, when I change that to a variable toggle, it seems to fire on every keypress again.
So the question is: How can I fire a function on input only if text in my input is highlighted?
I have found this question that suggests binding to the mouseup event, but it seems like overkill to check every single click when I'm only worried about a pretty particular situation. Also, that solution relies on window.getSelection(), which so far isn't working for me.
I've also found another question that suggests to use window.selectionEnd instead of window.getSelection() since I'm working with a text input. I tried incorporating that into option 2 above, but it also seems to fire on every keypress, rather than on highlight.
This answer is not about text selection at all.
But still solve your problem to refilter text when highlighted text is being replaced with new input.
var input = document.getElementById('ok');
var character = document.getElementById('char');
var previousCount = 0;
var currentCount = 0;
input.addEventListener('input', function(){
currentCount = this.value.length;
if (currentCount <= previousCount){
/*
This will detect if you replace the highlighted text into new text.
You can redo the filter here.
*/
console.log('Highlighted text replaced with: ' + this.value);
}
previousCount = currentCount;
char.innerHTML = this.value;
});
<input type="text" id="ok">
<div id="char"></div>
I'll agree with others that you will save yourself some trouble if you change your filtering strategy - I'd say you should filter all content from scratch at each keypress, as opposed to filtering successively the content that remains.
Anyway, to solve your immediate problem, I think you can just get the selection and see if it is empty. You can modify your second attempt:
$('.myinput').on('input', function(){
// get the string representation of the selection
var highlightedText = window.getSelection().toString();
if(highlightedText.length) {
//reset my input
}
});
EDIT
As this solution seems to have various problems, I can suggest another, along the lines of the comment from #Bee157. You can save the old search string and check if the new one has the old as a substring (and if not, reset the display).
var oldSearch = '';
$('.myinput').on('input', function(){
var newSearch = $('.myinput').val();
if (newSearch.indexOf(oldSearch) == -1) {
// reset the display
console.log('RESET');
}
oldSearch = newSearch;
// filter the results...
});
This approach has the added benefit that old results will reappear when you backspace. I tried it in your codepen, and I was able to log 'RESET' at all the appropriate moments.
I have a <input id="inp" type="text"> that user writes in, and sometimes uses suggests from a dictionary. When a suggest is selected I do:
var input = $('#inp');
input.val(input.val()+suggestedText+' ');
input.focus(); // that is because the suggest can be selected with mouse
everything works great, but when after adding a suggest that makes the resulting input.val() too long to fit in the edit field, the cursor is at the end of the string (which is good), but only the beginning of the string is visible in the edit field, so the cursor is hidden as well.
As soon as a key is pressed (a key that changes the value) the "scroll" goes to the end of the string hiding the beginning... How to trigger this behavior automatically, without having to press a key?
I have found a solution here - but it is not good as the whole input experience is changed...
Have you tried:
var input = $('#inp');
input.val(input.val()+suggestedText+' ');
input.focus(); // that is because the suggest can be selected with mouse
var height=input.contents()[0].outerHeight()
input.animate({
scrollTop:height
},'normal');
?
thank you all for answers, meanwhile I have found sth as well...
when using mouse to click the input lost focus (clik on sth else), and then regained it (thanks to input.focus()) - "scrolling" to the end, but when choosing a suggest was done with a keyboard, focus was never lost, and that is why it was not "scrolling" itself. I just simply added input.blur(), before input.focus(), now works like a charm... have a look at working example
http://46.4.128.78/input/
To make this work you need to set the focus() BEFORE you set the value. You can fix this in many ways, for example:
input.focus(); // that is because the suggest can be selected with mouse
var input = $('#inp');
input.val(input.val() + suggestedText + ' ');
Or this one:
function changeValue(element, newValue) {
element.focus();
element.val(element.val() + newValue + ' ');
}