如何在停止输入/写入后触发输入文本中的事件?

我希望在停止在输入文本框中键入(而不是在键入时)字符后触发一个事件。

我试过:

$('input#username').keypress(function() {
var _this = $(this); // copy of this object for further usage


setTimeout(function() {
$.post('/ajax/fetch', {
type: 'username',
value: _this.val()
}, function(data) {
if(!data.success) {
// continue working
} else {
// throw an error
}
}, 'json');
}, 3000);
});

但是这个示例为每个输入的字符产生一个超时,如果我输入20个字符,就会收到大约20个 AJAX 请求。

在这个小提琴 中,我用一个简单的警报代替 AJAX 演示了同样的问题。

有没有解决办法,或者我只是用了一个糟糕的方法来解决这个问题?

151996 次浏览

You'll have to use a setTimeout (like you are) but also store the reference so you can keep resetting the limit. Something like:

//
// $('#element').donetyping(callback[, timeout=1000])
// Fires callback when a user has finished typing. This is determined by the time elapsed
// since the last keystroke and timeout parameter or the blur event--whichever comes first.
//   @callback: function to be called when even triggers
//   @timeout:  (default=1000) timeout, in ms, to to wait before triggering event if not
//              caused by blur.
// Requires jQuery 1.7+
//
;(function($){
$.fn.extend({
donetyping: function(callback,timeout){
timeout = timeout || 1e3; // 1 second default timeout
var timeoutReference,
doneTyping = function(el){
if (!timeoutReference) return;
timeoutReference = null;
callback.call(el);
};
return this.each(function(i,el){
var $el = $(el);
// Chrome Fix (Use keyup over keypress to detect backspace)
// thank you @palerdot
$el.is(':input') && $el.on('keyup keypress paste',function(e){
// This catches the backspace button in chrome, but also prevents
// the event from triggering too preemptively. Without this line,
// using tab/shift+tab will make the focused element fire the callback.
if (e.type=='keyup' && e.keyCode!=8) return;
                    

// Check if timeout has been set. If it has, "reset" the clock and
// start over again.
if (timeoutReference) clearTimeout(timeoutReference);
timeoutReference = setTimeout(function(){
// if we made it here, our timeout has elapsed. Fire the
// callback
doneTyping(el);
}, timeout);
}).on('blur',function(){
// If we can, fire the event since we're leaving the field
doneTyping(el);
});
});
}
});
})(jQuery);


$('#example').donetyping(function(){
$('#example-output').text('Event last fired @ ' + (new Date().toUTCString()));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>


<input type="text" id="example" />
<p id="example-output">Nothing yet</p>

That will execute when:

  1. The timeout has elapsed, or
  2. The user switched fields (blur event)

(Whichever comes first)

In my thinking a user stops writing when he doesn't keep focus on that input. For this you have a function called "blur" which does stuff like

SOLUTION:

Here is the solution. Executing a function after the user has stopped typing for a specified amount of time:

var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();

Usage

$('input').keyup(function() {
delay(function(){
alert('Hi, func called');
}, 1000 );
});

You need debounce!

Here is a jQuery plugin, and here is all you need to know about debounce. If you are coming here from Google and Underscore has found its way into the JSoup of your app, it has debounce baked right in!

You can use underscore.js "debounce"

$('input#username').keypress( _.debounce( function(){<your ajax call here>}, 500 ) );

This means that your function call will execute after 500ms of pressing a key. But if you press another key (another keypress event is fired) before the 500ms, the previous function execution will be ignored (debounced) and the new one will execute after a fresh 500ms timer.

For extra info, using _.debounce(func,timer,true) would mean that the first function will execute and all other keypress events withing subsequent 500ms timers would be ignored.

I've been searching for a simple HTML/JS code and I did not found any. Then, I wrote the code below using onkeyup="DelayedSubmission()".

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pt-br" lang="pt-br">
<head><title>Submit after typing finished</title>
<script language="javascript" type="text/javascript">
function DelayedSubmission() {
var date = new Date();
initial_time = date.getTime();
if (typeof setInverval_Variable == 'undefined') {
setInverval_Variable = setInterval(DelayedSubmission_Check, 50);
}
}
function DelayedSubmission_Check() {
var date = new Date();
check_time = date.getTime();
var limit_ms=check_time-initial_time;
if (limit_ms > 800) { //Change value in milliseconds
alert("insert your function"); //Insert your function
clearInterval(setInverval_Variable);
delete setInverval_Variable;
}
}


</script>
</head>
<body>


<input type="search" onkeyup="DelayedSubmission()" id="field_id" style="WIDTH: 100px; HEIGHT: 25px;" />


</body>
</html>

cleaned solution :

$.fn.donetyping = function(callback, delay){
delay || (delay = 1000);
var timeoutReference;
var doneTyping = function(elt){
if (!timeoutReference) return;
timeoutReference = null;
callback(elt);
};


this.each(function(){
var self = $(this);
self.on('keyup',function(){
if(timeoutReference) clearTimeout(timeoutReference);
timeoutReference = setTimeout(function(){
doneTyping(self);
}, delay);
}).on('blur',function(){
doneTyping(self);
});
});


return this;
};

There is some simple plugin I've made that does exacly that. It requires much less code than proposed solutions and it's very light (~0,6kb)

First you create Bid object than can be bumped anytime. Every bump will delay firing Bid callback for next given ammount of time.

var searchBid = new Bid(function(inputValue){
//your action when user will stop writing for 200ms.
yourSpecialAction(inputValue);
}, 200); //we set delay time of every bump to 200ms

When Bid object is ready, we need to bump it somehow. Let's attach bumping to keyup event.

$("input").keyup(function(){
searchBid.bump( $(this).val() ); //parameters passed to bump will be accessable in Bid callback
});

What happens here is:

Everytime user presses key, bid is 'delayed' (bumped) for next 200ms. If 200ms will pass without beeing 'bumped' again, callback will be fired.

Also, you've got 2 additional functions for stopping bid (if user pressed esc or clicked outside input for example) and for finishing and firing callback immediately (for example when user press enter key):

searchBid.stop();
searchBid.finish(valueToPass);

why do that much when you just want to reset a clock ?

var clockResetIndex = 0 ;
// this is the input we are tracking
var tarGetInput = $('input#username');


tarGetInput.on( 'keyup keypress paste' , ()=>{
// reset any privious clock:
if (clockResetIndex !== 0) clearTimeout(clockResetIndex);


// set a new clock ( timeout )
clockResetIndex = setTimeout(() => {
// your code goes here :
console.log( new Date() , tarGetInput.val())
}, 1000);
});

if you are working on wordpress , then you need to wrap all this code inside an jQuery block :

jQuery(document).ready(($) => {
/**
* @name 'navSearch'
* @version 1.0
* Created on: 2018-08-28 17:59:31
* GMT+0530 (India Standard Time)
* @author : ...
* @description ....
*/
var clockResetIndex = 0 ;
// this is the input we are tracking
var tarGetInput = $('input#username');


tarGetInput.on( 'keyup keypress paste' , ()=>{
// reset any privious clock:
if (clockResetIndex !== 0) clearTimeout(clockResetIndex);


// set a new clock ( timeout )
clockResetIndex = setTimeout(() => {
// your code goes here :
console.log( new Date() , tarGetInput.val())
}, 1000);
});
});

You should assign setTimeout to a variable and use clearTimeout to clear it on keypress.

var timer = '';


$('input#username').keypress(function() {
clearTimeout(timer);
timer = setTimeout(function() {
//Your code here
}, 3000); //Waits for 3 seconds after last keypress to execute the above lines of code
});

Fiddle

Hope this helps.

Use the attribute onkeyup="myFunction()" in the <input> of your html.

We can use useDebouncedCallback to perform this task in react.

import { useDebouncedCallback } from 'use-debounce'; - install npm packge for same if not installed

const [searchText, setSearchText] = useState('');


const onSearchTextChange = value => {
setSearchText(value);
};


//call search api
const [debouncedOnSearch] = useDebouncedCallback(searchIssues, 500);
useEffect(() => {
debouncedOnSearch(searchText);
}, [searchText, debouncedOnSearch]);

This is what I am using with a formControl. It works for me.

this.form.controls[`text`].valueChanges
.pipe(debounceTime(500), distinctUntilChanged())
.subscribe((finalText) => {
yourMethod(finalText);
});