Jquery text change event - javascript

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

Related

Debounce search only if search term changes [duplicate]

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.

Cross-browser way to get automatically repeating keydown events when key is held down

Using Javascript / jQuery, how can I get automatically repeating keydown events, or equivalent, when someone holds down a key?
What I actually want is to be able to check whether a key is down, but from other questions here it looks like that's not possible. The suggested workaround seems to be recording keydown and keyup events and then assuming the key is down if a keydown event has been recorded and no subsequent keyup.
That solution runs into a problem in my case. I am designing an online experiment. The user is supposed to hold down the "T" key for the entire experiment, never letting it up. The experiment consists of multiple trials and each trial has no access to information recorded by the previous trials. So, trial 1 could record keydown for T, but trial 2 wouldn't have access to that record and thus wouldn't know whether T was down or not.
Now, if holding down the T key would produce automatically repeating keydown events for T, I would have no problem because trial 2 would just catch the next keydown event for T to come along. But it looks like I don't get automatically repeating keydown events from holding the key down, at least in Firefox. From what I can see it seems there is variation in the way different browsers handle holding a key down. What is a good cross-browser way to solve my problem?
By the way, if it matters, I also need to be able to detect keyup and keydown events for other keys while all this is going on.
EDIT: after reading some of the comments I went back and verified that I do indeed get repeating keydown events under ordinary circumstances. But I really don't get them in the specific situation in which I need them. I've got some simple code which I think isolates the issue:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
</head>
<body>
<div id="target"></div>
</body>
<script type="text/javascript">
var i;
function foo() {
i++;
$('#target').html(i);
}
function doTrial() { // do trial
i=0;
$(document).keydown(foo);
$(document).keyup(endTrial);
}
function endTrial() { // end trial
$('#target').html('');
$(document).unbind('keydown',foo);
$(document).unbind('keyup',endTrial);
doTrial();
}
doTrial();
</script>
</html>
If you press a key and hold it down, then release, then press again, the behavior is as expected, i.e. there is a counter which increments while the key is held down, disappears when it's released, and then starts incrementing again when it's pressed again.
But if you press TWO keys down, then release ONE, I would have thought that the other (not released) key would continue sending keydown events so that the counter would (after resetting) continue incrementing. In fact, that doesn't happen. Any idea why and how to make it happen?
In the browsers I tried this in, I got repeated keydown events when holding a typeable key down. I don't know if this is a problem you actually need to solve.
But, if you did think you need to solve it OR if you want to control the repeat-rate yourself, you could do it like this:
Capture the events for keydown and keyup.
On keydown, set an interval timer that fires however often you want to know that the key is still down.
On keyup for that key, stop the interval timer.
You will get repeated notification in a cross browser way as long as the key is held down.
Working demo here: http://jsfiddle.net/jfriend00/XbZYs/
var downTimer;
var lastKey;
$(document.body).keydown(function(e) {
// if not still the same key, stop the timer
if (e.which !== lastKey) {
if (downTimer) {
clearInterval(downTimer);
downTimer = null;
}
}
// remember previous key
lastKey = e.which;
if (!downTimer) {
// start timer
downTimer = setInterval(function() {
$("#result").append("+");
}, 125);
}
}).keyup(function(e) {
// stop timer
if (downTimer) {
clearInterval(downTimer);
downTimer = null;
lastKey = 0;
}
});
If you want a key to auto-repeat forever until it is raised, even if other keys are pressed and released in the meantime and you want those other keys to do their own auto-repeating, then the OS does not ipmlement that behavior so you would have to implement it yourself. You can do something like this which calls a callback function for every key repeat event:
Working demo: http://jsfiddle.net/jfriend00/aD3Eg/
// this is called for every manufactured repeat event
// the frequency of the repeat event is determined by the time value set
// on setInterval() below
function repeatCallback(key) {
$("#result").append(key + " ");
}
var repeatState = {};
$(document.body).keydown(function(e) {
var key = e.which;
// if no time yet for this key, then start one
if (!repeatState[key]) {
// make copy of key code because `e` gets reused
// by other events in IE so it won't be preserved
repeatState[key] = setInterval(function() {
repeatCallback(key);
}, 125);
} else {
// nothing really to do here
// The key was pressed, but there is already a timer
// firing for it
}
}).keyup(function(e) {
// if we have a timer for this key, then stop it
// and delete it from the repeatState object
var key = e.which;
var timer = repeatState[key];
if (timer) {
clearInterval(timer);
delete repeatState[key];
}
});
​
The repeatCallback function is called for all of these manufactured auto-repeat events and passed the key that is auto-repeating. ​
Take a look at this jQuery plugin: fastKeys I think, that it is what you want/need...

ExtJS listening for global keystrokes with Ext.KeyMap

To polish up an application im developing I am adding keyboard shortcuts for common tasks. I can sue Ext.KeyMap to do this like so...
var map = new Ext.KeyMap("my-element", {
key: 13, // or Ext.EventObject.ENTER
fn: myHandler,
scope: myObject
});
But I want to detect "ss" or "qq" i.e. specific double key strokes of letters. Im not sure how to do this....
My idea is to detect the singular keystroke, add a listener to detect a following key stroke. And to handle the gap between them, set a delayed event that deletes the listener after x amount of time.
Any improvements/suggestions/warnings??
I'm not sure why you would need an additional listener here. Why not store the previous keystroke in a variable (with the timestamp when the keystroke occurred). Then you could just compare the latest keystroke to the previous keystroke. If they are the same, and the timestamp stored is not too far in the past, that's the double key you're after. If the key codes are not the same, or stored timestamp is too old, just update the stored keycode and timestamp with new values.
You don't have to remember all the keys pressed, just the last one. When you think at the double click event, it's similar, but since clicking two times is more popular, it's already implemented for us.
Something like this:
function defineDoubleKeyMap(element, cfg) {
var repeated, timer;
return new Ext.KeyMap(element, {
key: cfg.key,
fn: function() {
if (repeated) {
// when clicked the second time
// stop timer and call our callback
repeated = false;
clearTimeout(timer);
cfg.fn.call(cfg.scope);
}
else {
// remember we clicked once, forget after delay
repeated = true;
// adjust the delay as needed.
// current 1 sec is probably too long
timer = (function(){
repeated = false;
}).defer(1000);
}
}
});
}
defineDoubleKeyMap("my-element", {
key: Ext.EventObject.ENTER,
fn: myHandle,
scope: myObject
});
The probem with this code is that pressing quickly ENTER-DELETE-ENTER will also fire the event as if ENTER-ENTER was pressed. If that's not acceptable, then you have to keep track of all the keypresses - I think you'll have to use Ext.get("my-element").on("keypress") for that.

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