3

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?

Henrik Petterson
  • 6,862
  • 20
  • 71
  • 155

3 Answers3

3

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") ...

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Just testing your code. Is your answer a better approach than the other one? – Henrik Petterson Oct 28 '17 at 12:51
  • @henrik its probably not better in the sense of performance, but i think the difference is not measurable / relevant. However it allows for more customizations... – Jonas Wilms Oct 28 '17 at 12:55
  • Can you please elaborate on `cache $("#search") ...` -- what steps do I need to do to do this? – Henrik Petterson Oct 28 '17 at 12:57
  • @henrik do `var search = $("#search")` in the above scope, then use `search.val()` ... – Jonas Wilms Oct 28 '17 at 13:00
  • I would not bother with caching `id` selectors, as they are pretty performant. That one is translated to `document.getElementById` call. – Martin Adámek Oct 28 '17 at 13:00
  • @martin for fast typing users & unperformant devices milliseconds matter... – Jonas Wilms Oct 28 '17 at 13:02
  • I do not really think that a human can see the difference here... But in general caching selectors is a good idea. – Martin Adámek Oct 28 '17 at 13:03
  • Also I think that `input` event will be more performant (again not measurable for human), as it will be triggered only when the value changes. `keyup` will be triggered also when meta keys are pressed (that is not a problem tho I think). The reason why this solution will be less performant (but again, it will be almost the same), is that you have additional code (variable assignment and if condition), which needs to be evaluated. – Martin Adámek Oct 28 '17 at 13:05
  • @martin and *That one is translated to document.getElementById call* -> but using [VanillaJS](http://vanilla-js.com) only is still 100 times faster ;) – Jonas Wilms Oct 28 '17 at 13:06
  • Not 100 times, but yes, its slower (about 10 times, according to this https://jsperf.com/jquery-selectors-with-id). End of off-topic, I guess? :] – Martin Adámek Oct 28 '17 at 13:09
0

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.

Community
  • 1
  • 1
Martin Adámek
  • 16,771
  • 5
  • 45
  • 64
0

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
        })))();
    }
})
Vasan
  • 4,810
  • 4
  • 20
  • 39