Debounce search only if search term changes [duplicate] - javascript

This question already has answers here:
Throttle event calls in jQuery
(7 answers)
Closed 5 years ago.
I'm working on a live ajax search field. So we have:
<input type="search" id="search">
And I want to trigger the search once the user has stopped typing for 500ms.
I got it working like this:
function throttle(f, delay){
var timer = null;
return function(){
var context = this, args = arguments;
clearTimeout(timer);
timer = window.setTimeout(function(){
f.apply(context, args);
},
delay || 500);
};
}
$('#search').keyup(throttle(function(){
// do ajax
}));
The problem is: if user holds shift key and releases it, the search will trigger regardless. So no new keys are added and the same search term was submitted.
How can I make it so that it triggers only if a new character has been added/removed?

Simply keep track of the state:
var currentSearch = "";
Then whenever a keyup happens, check if the input changed, if not stop updating:
if($("#search").val() === currentSearch) return;
//updated so:
currentSearch = $("#search").val();
Sidenote: as DOM lookup is time intensive you may cache $("#search") ...

You could also use the keyup event, and check if the keycode matches the keys on which you want to act on. Please refer this answer
Adapting that answer to your case:
$('#search').keyup(
function(event) {
var isWordCharacter = event.key.length === 1;
var isBackspaceOrDelete = (event.keyCode == 8 || event.keyCode == 46);
if (isWordCharacter || isBackspaceOrDelete) {
(throttle(function(){
// do ajax
})))();
}
})

You should use input event instead of keyup, which will be triggered only when the value will change.
$('#search').on('input', throttle(function(){
// do ajax
}));
The DOM input event is fired synchronously when the value of an <input>, <select>, or <textarea> element is changed. (For input elements with type=checkbox or type=radio, the input event does not fire when a user clicks the control, because the value attribute does not change.)
But be aware that this event has some problems in IE9/10/11 (and is not supported in previous IE versions at all):
[2] IE 9 does not fire an input event when the user deletes characters from an input (e.g. by pressing Backspace or Delete, or using the "Cut" operation).
[3] IE 10 and 11 has a bug, input event fires on placeholder attribute changes.
But the IE10/11 problem is kind of ok, so it depends on if you need to support IE9.
https://developer.mozilla.org/en-US/docs/Web/Events/input
EDIT: unfortunately change is not a suitable solution as it occurres only after blur (loosing focus of the input). Correct solution is to use input event.

Related

Allow use special commands on input validating

I have a little problem with validating an input field.
Here is my validation code:
_validateInput: function(e) {
var value = e.currentTarget.value;
var key = e.which || e.keyCode;
var re = /[^0-9\.]/gi;
if (re.test(value + String.fromCharCode(key))) {
return value;
} else {
return value + String.fromCharCode(key);
}
},
The logic is the next. If user input non-digital characters, the function return current value of the input, without the last symbol.
If user type digit or dot, function return current input value + entered number.
What is my problem:
1) It not allows user to enter dot.
2) String.fromCharCode for dot character returns "¾" symbol.
3) Special symbols like backspace, tab, etc... doesn`t work.
4) Commands like Ctrl+V, Ctrl+A also does not work
Could someone help me to solve this problems? What is wrong with my RegEx?
Thanks!
P.S. Function fired on the keydown event
Unless you need to support older browsers listen for oninput instead of onkeydown and a lot of the processing will have been done for you.
http://www.w3schools.com/jsref/event_oninput.asp
This event is similar to the onchange event. The difference is that the oninput event occurs immediately after the value of an element has changed, while onchange occurs when the element loses focus, after the content has been changed. The other difference is that the onchange event also works on <keygen> and <select> elements.
Whenever oninput is triggered then check the value in the text area is valid and if it isn't then correct it. This will also let you check for multiple dots being entered (if you need to do that). For example 231.21.23 is not a valid number.

Onkeypress update input is 1 step behind

I am trying to update an input with 2 other inputs. For example
Amount : (user inputs value)
Fee: (value is already set -- disabled value: 0.0002)
Total: (amount - fee) worked out via javascript below
$('#amount').keypress(function() {
var total = $('#amount').val() - $('#fee').val();
if(typeof total != 'undefined'){ $('#total').val(total) }});
When I 'keypress' on my input with a number, it is one step behind updating the input with total.
Say for an example if I type 1 in the input amount, the total will be -0.0002(the fee) when I press 1 again, it will be 0.9998 (even though the amount input is now 11 so on so forth.)
It's cause of you're using keypress which consists of 2 events keydown and keyup, so your function launches on the first one - keydown, so you get your values, as you said "one step behind", cause when the keydown event is fired your input field didn't recieve the pressed key value yet. Use keyup and you will get your result:
$('#amount').keyup(function() {
var total = $('#amount').val() - $('#fee').val();
if(typeof total != 'undefined') $('#total').val(total)
});
Fiddle
The posted answers are good answers, but the best event to use here is the oninput event, because it's faster.
$('#amount').on('input', function() {
var total = $('#amount').val() - $('#fee').val();
if(typeof total != 'undefined'){
$('#total').val(total);
}
});
Supported by all browsers: browser support. It's actually better supported than onchange and other events like onkeyup. You can compare that on https://caniuse.com/.
You can even use this event for other input elements as well. Like checkboxes, radio buttons and the select element.
It's fast. So you can combine this with search queries to make your search results return faster.
Please note: since this is not a keyboard event, there won't be a keyCode or any other keyboard related value in the event.
Read more
Event Reference
Global Event Handler
True, because when function is called the field is not updated with the value
The event is called in this steps
Keydown
Keypress
updateview
Keypress
updateview
keyup
So if you can change this event to keyup then you will get the latest value.

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

Mootools Shift-Tab Event Problem

Im using the most recent moo release and trying to write a function that evaluates the user given expression on event "keyup". As soon as my test's are passing I put the focus on the next input element automatically to improve the user experience and speed, since he uses the form many times.
So I came up with something like that:
var getNextInputElement = function(element){
returns the next input element
}
var checkDay = function(event){
var input = $('booking_day').get('value');
if (input.length > 1 && input < 32) {
$('booking_day').erase('class');
if (!(event.key == "tab")) {
getNextInputElement($('booking_day')).focus();
}
else {
$('booking_day').focus();
}
}
else if(input.length > 1) {
$('booking_day').set('class','error');
}
else {
$('booking_day').erase('class');
}
};
window.addEvent('domready', function() {
$('new_booking').reset();
$('booking_day').addEvent('keyup', checkDay);
$('booking_day').focus();
});
Works fine so far. But if I try to "shift-tab" back to my input field
getNextInputElement($('booking_day')).focus();
is evaluated and focus reset to the next input field. So the user cannot roll back to previosly entered data. I do not find any possibility to catch that shift-tab event. Since two keys are pressed, there are two events fired. One for "tab" and a second one, but not for "shift". The event.key.code of this event seems to be outside the scope that mootools realizes.
Anyone out there who can help on this problem?
Thanks.
Jason
You can add event for both "Shift+Tab" keys
Link : http://mootools.net/docs/more/Interface/Keyboard

How to change characters typed in Firefox

I need to change in a text input the character '.' to ',' while typing.
In IE I change the keyCode event property in the keypress event, like this
document.getElementById('mytext').onkeypress =
function (evt) {
var e = evt || window.event;
if (e.keyCode && e.keyCode==46)
e.keyCode = 44;
else if (e.which && e.which==46) {
e.which = 44;
}
};
but it seemes that in Firefox it's impossible to change characters typed in key events.
Any suggestions?
Try this. It works on all browsers:
window.onload = function () {
var input = document.getElementById("mytext");
input.onkeypress = function () {
var evt = arguments[0] || event;
var char = String.fromCharCode(evt.which || evt.keyCode);
// Is it a period?
if (char == ".") {
// Replace it with a comma
input.value += ",";
// Cancel the original event
evt.cancelBubble = true;
return false;
}
}
};
Update: Pier Luigi pointed out a problem with the above. It doesn't take care of the caret position not being at the end of the text. It will append the command to the end even if you're inserting some text to the value.
The solution would be, instead of appending a comma, to simulate a keypress event for the comma key. Unfortunately the way dispatching of synthetic events work in different browsers seems to show a lot of variety and isn't an easy feat. I'll see if I can find a nice and generic method for it.
Assume that all properties in an Event object are immutable. The DOM spec doesn't address what happens when you change those values manually.
Here's the logic you need: listen for all key events. If it's a period, suppress the event, and manually add the comma at the cursor position. (Here's a code snippet for inserting arbitrary text at the cursor position.)
You'd suppress the event in Firefox by calling event.preventDefault(); this tells the browser not to go ahead with the default action associated with this event (in this case, typing the character). You'd suppress the event in IE by setting event.returnValue to false.
If it's not a period, return early from your handler.
Technically you just want to replace all dots with commas.
document.getElementById('mytext').onkeyup = function(){
this.value = this.value.replace('.', ',');
}
If I look at the official Document Object Model Events document, mouse events fields are defined as read-only. Keyboard events are not defined there, I suppose Mozilla followed this policy for them.
So basically, unless there is some smart trick, you cannot alter an event the way you want. You probably have to intercept the key and insert the char (raw or translated) where the caret is, the way JS HTML editors do.
Does this really need to be done on the fly? If you are collecting the information to be posted to a form or submitted to a database, would it not be better to modify the data once it was submitted? That way the user never sees the confusing change.
This is possible now by intercepting and cancelling the default keydown event and using HTMLInputElement.setRangeText to insert your desired character. This would look something like this:
document.addEventListener('keydown', $event => {
if($event.code === 'Period'){
$event.preventDefault();
let inputEl = document.querySelector("#my-input");
inputEl.setRangeText(
',',
inputEl.selectionStart,
inputEl.selectionEnd,
"end"
);
}
})
setRangeText will insert text at the cursor position in a given input. The "end" string as the last argument sets the cursor to the end of the inserted content.
More info here: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setRangeText

Categories