Как отложить обработчик .keyup (), пока пользователь не перестанет печатать?

582

У меня есть поле поиска. Сейчас он ищет каждую клавиатуру. Поэтому, если кто-то наберет "Windows", он выполнит поиск с AJAX для каждой клавиатуры: "W", "Wi", "Win", "Wind", "Windo", "Window", "Windows".

Я хочу иметь задержку, поэтому он выполняет поиск только тогда, когда пользователь перестает печатать на 200 мс.

В функции keyup нет опции, и я пробовал setTimeout, но он не работал.

Как я могу это сделать?

  • 4
    Дубликат stackoverflow.com/questions/1620602/…
  • 2
    Если бы я мог, я бы закрыл это как дубликат.
Показать ещё 7 комментариев
Теги:

25 ответов

1022
Лучший ответ

Я использую эту маленькую функцию для той же цели, выполняя функцию после того, как пользователь прекратил печатать в течение определенного периода времени или в событиях, которые запускаются с высокой скоростью, таких как resize:

function delay(callback, ms) {
  var timer = 0;
  return function() {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      callback.apply(context, args);
    }, ms || 0);
  };
}


// Example usage:

$('#input').keyup(delay(function (e) {
  console.log('Time elapsed!', this.value);
}, 500));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="input">Try it:
<input id="input" type="text" placeholder="Type something here..."/>
</label>

Как это устроено:

Функция delay будет возвращать упакованную функцию, которая внутренне обрабатывает отдельный таймер, при каждом выполнении таймер перезапускается с предоставленной временной задержкой. Если многократное выполнение происходит до того, как пройдет это время, таймер просто сбрасывается и запускается снова.

Когда таймер наконец заканчивается, выполняется функция обратного вызова, передавая исходный контекст и аргументы (в этом примере объект события jQuery и элемент DOM, как this).

Для чего-то более сложного, посмотрите на плагин jQuery Typewatch.

  • 60
    Другая альтернатива: github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js . Он работает почти так же, как вы описали, я просто использовал этот шаблон, поэтому реализовал его как плагин jQuery, чтобы упростить синтаксис. Вот демонстрационная страница: briangrinstead.com/files/bindWithDelay
  • 2
    я изначально жестко закодировал количество набранных букв, этот подход гладкий как патока (без серфера)
Показать ещё 16 комментариев
52

Если вы хотите выполнить поиск после завершения типа, используйте глобальную переменную, чтобы удерживать тайм-аут, возвращенный с вашего вызова setTimout, и отмените его с помощью clearTimeout, если он еще не появился, чтобы он не срабатывал таймаут, кроме последнего события keyup

var globalTimeout = null;  
$('#id').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
}   
function SearchFunc(){  
  globalTimeout = null;  
  //ajax code
}

Или с анонимной функцией:

var globalTimeout = null;  
$('#id').keyup(function() {
  if (globalTimeout != null) {
    clearTimeout(globalTimeout);
  }
  globalTimeout = setTimeout(function() {
    globalTimeout = null;  

    //ajax code

  }, 200);  
}   
27

Еще одно небольшое улучшение в ответе CMS. Чтобы легко разрешить отдельные задержки, вы можете использовать следующее:

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

Если вы хотите повторно использовать ту же задержку, просто сделайте

var delay = makeDelay(250);
$(selector1).on('keyup', function() {delay(someCallback);});
$(selector2).on('keyup', function() {delay(someCallback);});

Если вам нужны отдельные задержки, вы можете сделать

$(selector1).on('keyup', function() {makeDelay(250)(someCallback);});
$(selector2).on('keyup', function() {makeDelay(250)(someCallback);});
  • 1
    Интересно, что решение с более чем 600 ответами на день, когда я пишу этот комментарий, не так хорошо, как этот ответ только с 2 ответами (включая мое). Совершенно очевидно, что он поддерживает несколько виджетов, которые могут совместно использовать или не совместно использовать задержку. Отличный ответ.
  • 0
    ... Хотя плагин jQuery в комментарии под ответом также обрабатывает отдельные таймеры для отдельных элементов, а также добавляет дроссель и аргумент события: github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js. Общий подход этого плагина такой же, как и ваш код, поэтому ваш код стоит внимательно изучить, чтобы понять его простоту, прежде чем пытаться понять более сложный плагин. Тем не менее, я рекомендую этот плагин - но если вам не нужен газ или передача данных о событиях, ваш код великолепен! Два хороших варианта. Спасибо!
Показать ещё 2 комментария
27

Вы также можете посмотреть underscore.js, который предоставляет полезные методы, такие как debounce:

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
  • 0
    То же самое доступно и в Lodash - lodash.com/docs/#debounce
13

Основываясь на ответе CMS, я сделал следующее:

Поместите код ниже после включения jQuery:

/*
 * delayKeyup
 * http://code.azerti.net/javascript/jquery/delaykeyup.htm
 * Inspired by CMS in this post : http://stackoverflow.com/questions/1909441/jquery-keyup-delay
 * Written by Gaten
 * Exemple : $("#input").delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);
 */
(function ($) {
    $.fn.delayKeyup = function(callback, ms){
        var timer = 0;
        $(this).keyup(function(){                   
            clearTimeout (timer);
            timer = setTimeout(callback, ms);
        });
        return $(this);
    };
})(jQuery);

И просто используйте вот так:

$('#input').delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);

Осторожно: переменная $(this) в функции, переданной как параметр, не соответствует вводу

10

Задержка многофункциональных вызовов с использованием меток

Это решение, с которым я работаю. Это задержит выполнение ЛЮБОЙ функции, которую вы хотите. Это может быть поисковый запрос по нажатию клавиши, может быть быстрый щелчок по предыдущей или следующей кнопке (в противном случае отправка множественного запроса будет выполняться при быстром щелчке и не будет использоваться в конце концов). При этом используется глобальный объект, который сохраняет каждое время выполнения и сравнивает его с самым последним запросом.

Таким образом, в результате будет вызван только последний щелчок/действие, поскольку эти запросы хранятся в очереди, то есть после вызова X миллисекунд, если в очереди не существует другого запроса с такой же меткой!

function delay_method(label,callback,time){
    if(typeof window.delayed_methods=="undefined"){window.delayed_methods={};}  
    delayed_methods[label]=Date.now();
    var t=delayed_methods[label];
    setTimeout(function(){ if(delayed_methods[label]!=t){return;}else{  delayed_methods[label]=""; callback();}}, time||500);
  }

Вы можете установить собственное время задержки (необязательно, по умолчанию 500 мс). И отправьте аргументы вашей функции в "режиме закрытия".

Например, если вы хотите вызвать нижеуказанную функцию:

function send_ajax(id){console.log(id);}

Чтобы предотвратить несколько запросов send_ajax, вы задерживаете их, используя:

delay_method( "check date", function(){ send_ajax(2); } ,600);

Каждый запрос, использующий метку "дата проверки", будет запускаться только в том случае, если в течение 600 миллисекунд не было сделано ни одного другого запроса. Этот аргумент не является обязательным

Пометьте независимость (вызывая ту же целевую функцию), но запустите оба:

delay_method("check date parallel", function(){send_ajax(2);});
delay_method("check date", function(){send_ajax(2);});

Приводит к вызову одной и той же функции, но задерживает их независимо, потому что их метки отличаются

  • 0
    Я использую ваш код в проекте, над которым я работаю, и испытываю трудности с ним. Это на самом деле ничего не откладывает. Мысль, которую вы, возможно, захотите увидеть / оказать помощь. stackoverflow.com/questions/51393779/...
  • 0
    я проверю это подождите ...
Показать ещё 1 комментарий
6

Это сработало для меня, когда я задерживаю операцию поиска и делаю проверку, если значение такое же, как указано в текстовом поле. Если значение такое же, тогда я иду вперед и выполняю операцию для данных, относящихся к значению поиска.

$('#searchText').on('keyup',function () {
    var searchValue = $(this).val();
    setTimeout(function(){
        if(searchValue == $('#searchText').val() && searchValue != null && searchValue != "") {
           // logic to fetch data based on searchValue
        }
        else if(searchValue == ''){
           // logic to load all the data
        }
    },300);
});
  • 0
    не работает должным образом - количество выборок одинаково, просто задерживается на 300 мс - вам нужно использовать функцию clearTimeout () при каждом нажатии клавиши перед выполнением setTimeout ()
  • 0
    Ну, вам не нужно использовать clearTimeout в этом случае. Условие searchValue == $ ('# searchText'). Val () внутри setTimeout будет гарантировать, что значение будет таким же, как оно было 300 мс назад. Дайте мне знать, если это имеет смысл. Благодарю.
6

Эта функция немного расширяет функцию ответа Gaten, чтобы вернуть элемент:

$.fn.delayKeyup = function(callback, ms){
    var timer = 0;
    var el = $(this);
    $(this).keyup(function(){                   
    clearTimeout (timer);
    timer = setTimeout(function(){
        callback(el)
        }, ms);
    });
    return $(this);
};

$('#input').delayKeyup(function(el){
    //alert(el.val());
    // Here I need the input element (value for ajax call) for further process
},1000);

http://jsfiddle.net/Us9bu/2/

5

Я удивлен, что никто не упоминает проблему с несколькими входами в CMS очень красиво отрезанным.

В принципе, вам нужно будет определять переменную задержки отдельно для каждого входа. В противном случае, если sb помещает текст в первый вход и быстро переходит на другой вход и начинает печатать, обратный вызов для первого WILL NOT будет вызван!

См. код ниже. Я пришел с учетом других ответов:

(function($) {
    /**
     * KeyUp with delay event setup
     * 
     * @link http://stackoverflow.com/questions/1909441/jquery-keyup-delay#answer-12581187
     * @param function callback
     * @param int ms
     */
    $.fn.delayKeyup = function(callback, ms){
            $(this).keyup(function( event ){
                var srcEl = event.currentTarget;
                if( srcEl.delayTimer )
                    clearTimeout (srcEl.delayTimer );
                srcEl.delayTimer = setTimeout(function(){ callback( $(srcEl) ); }, ms);
            });

        return $(this);
    };
})(jQuery);

Это решение сохраняет ссылку setTimeout в переменной ввода delayTimer. Он также передает ссылку элемента на обратный вызов, как предположил fazzyx.

Протестировано в IE6, 8 (comp-7), 8 и Opera 12.11.

  • 0
    Пробовал это, но не могу заставить работать должным образом на первом keyup.
4

Если кому-то нравится откладывать одну и ту же функцию и без внешней переменной, он может использовать следующий script:

function MyFunction() {

    //Delaying the function execute
    if (this.timer) {
        window.clearTimeout(this.timer);
    }
    this.timer = window.setTimeout(function() {

        //Execute the function code here...

    }, 500);
}
4

Функция задержки для вызова каждой клавиши. jQuery 1.7.1 или требуется

jQuery.fn.keyupDelay = function( cb, delay ){
  if(delay == null){
    delay = 400;
  }
  var timer = 0;
  return $(this).on('keyup',function(){
    clearTimeout(timer);
    timer = setTimeout( cb , delay );
  });
}

Использование: $('#searchBox').keyupDelay( cb );

3

Основываясь на CMS, ответьте здесь на новый метод задержки, который сохраняет 'this' в своем использовании:

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

Использование:

$('input').keyup(function() {
    delay(function(){
      alert('Time elapsed!');
    }, 1000, this);
});
3

Супер простой подход, предназначенный для запуска функции после того, как пользователь закончил печатать в текстовом поле...

<script type="text/javascript">
$(document).ready(function(e) {
    var timeout;
    var delay = 2000;   // 2 seconds

    $('.text-input').keyup(function(e) {
        console.log("User started typing!");
        if(timeout) {
            clearTimeout(timeout);
        }
        timeout = setTimeout(function() {
            myFunction();
        }, delay);
    });

    function myFunction() {
        console.log("Executing function for user!");
    }
});
</script>

<textarea name="text-input" class="text-input"></textarea>
2

Сочетание ответа CMS с Miguel one дает надежное решение, допускающее одновременные задержки.

var delay = (function(){
    var timers = {};
    return function (callback, ms, label) {
        label = label || 'defaultTimer';
        clearTimeout(timers[label] || 0);
        timers[label] = setTimeout(callback, ms);
    };
})();

Если вам нужно отдельно отложить разные действия, используйте третий аргумент.

$('input.group1').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, 'firstAction');
});

$('input.group2').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, '2ndAction');
});
2

Основываясь на ответе CMS, он просто игнорирует ключевые события, которые не изменяют значение.

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

var duplicateFilter=(function(){
  var lastContent;
  return function(content,callback){
    content=$.trim(content);
    if(content!=lastContent){
      callback(content);
    }
    lastContent=content;
  };
})();

$("#some-input").on("keyup",function(ev){

  var self=this;
  delay(function(){
    duplicateFilter($(self).val(),function(c){
        //do sth...
        console.log(c);
    });
  }, 1000 );


})
2

Это решение по линии CMS, но решает несколько ключевых вопросов для меня:

  • Поддержка нескольких входов, задержки могут выполняться одновременно.
  • Игнорирует ключевые события, которые не изменяли значение (например, Ctrl, Alt + Tab).
  • Решает условие гонки (когда выполняется обратный вызов и значение уже изменено).
var delay = (function() {
    var timer = {}
      , values = {}
    return function(el) {
        var id = el.form.id + '.' + el.name
        return {
            enqueue: function(ms, cb) {
                if (values[id] == el.value) return
                if (!el.value) return
                var original = values[id] = el.value
                clearTimeout(timer[id])
                timer[id] = setTimeout(function() {
                    if (original != el.value) return // solves race condition
                    cb.apply(el)
                }, ms)
            }
        }
    }
}())

Использование:

signup.key.addEventListener('keyup', function() {
    delay(this).enqueue(300, function() {
        console.log(this.value)
    })
})

Код написан в стиле, который мне нравится, вам может понадобиться добавить кучу с запятой.

Что нужно помнить:

  • Уникальный идентификатор создается на основе идентификатора формы и имени ввода, поэтому они должны быть определены и уникальны, или вы можете настроить его в своей ситуации.
  • delay возвращает объект, который легко расширяется для ваших собственных нужд.
  • Исходный элемент, используемый для задержки, связан с обратным вызовом, поэтому this работает как ожидалось (например, в примере).
  • Пустое значение игнорируется во второй проверке.
  • Остерегайтесь enqueue, он сначала ожидает миллисекунд, я предпочитаю это, но вы можете переключать параметры в соответствии с setTimeout.

Решение, которое я использую, добавляет еще один уровень сложности, что позволяет отменить выполнение, например, но это хорошая основа для построения.

2

Используйте

mytimeout = setTimeout( expression, timeout );

где выражение - это script для запуска, а время ожидания - это время ожидания в миллисекундах перед его запуском - это НЕ ХАРАКТЕРИСТИРОВАТЬ script, а просто задерживает выполнение этой части до истечения таймаута.

clearTimeout(mytimeout);

будет reset/очистить таймаут, чтобы он не запускал выражение script в выражении (например, отмена), если оно еще не выполнено.

1

Вот предложение, которое я написал, который заботится о нескольких вводах в вашей форме.

Эта функция получает объект поля ввода, помещает в ваш код

function fieldKeyup(obj){
    //  what you want this to do

} // fieldKeyup

Это фактическая функция delayCall, которая обслуживает несколько полей ввода

function delayCall(obj,ms,fn){
    return $(obj).each(function(){
    if ( typeof this.timer == 'undefined' ) {
       // Define an array to keep track of all fields needed delays
       // This is in order to make this a multiple delay handling     
          function
        this.timer = new Array();
    }
    var obj = this;
    if (this.timer[obj.id]){
        clearTimeout(this.timer[obj.id]);
        delete(this.timer[obj.id]);
    }

    this.timer[obj.id] = setTimeout(function(){
        fn(obj);}, ms);
    });
}; // delayCall

Использование:

$("#username").on("keyup",function(){
    delayCall($(this),500,fieldKeyup);
});
1
var globalTimeout = null;  
$('#search').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
});
function SearchFunc(){  
  globalTimeout = null;  
  console.log('Search: '+$('#search').val());
  //ajax code
};
1

Используйте bindWithDelay плагин jQuery:

element.bindWithDelay(eventType, [ eventData ], handler(eventObject), timeout, throttle)
0

Пользовательская библиотека lodash javascript и использование функции _.debounce

changeName: _.debounce(function (val) {
  console.log(val)                
}, 1000)
0

С ES6 можно также использовать синтаксис функции стрелки.

В этом примере код задерживает keyup события для 400мса после того как пользователь закончить typeing перед вызовом searchFunc сделать запрос на запрос.

const searchbar = document.getElementById('searchBar');
const searchFunc = // any function

// wait ms (milliseconds) after user stops typing to execute func
const delayKeyUp = (() => {
    let timer = null;
    const delay = (func, ms) => {
        timer ? clearTimeout(timer): null
        timer = setTimeout(func, ms)
    }
    return delay
})();

searchbar.addEventListener('keyup', (e) => {
    const query = e.target.value;
    delayKeyUp(() => {searchFunc(query)}, 400);
})
0

Видел это сегодня немного поздно, но просто хочу поставить это здесь, если кому-то еще понадобится. просто отделите функцию, чтобы сделать ее многоразовой. код ниже будет ждать полтора секунды после остановки печати. ​​

    var timeOutVar
$(selector).on('keyup', function() {

                    clearTimeout(timeOutVar);
                    timeOutVar= setTimeout(function(){ console.log("Hello"); }, 500);
                });
0

Ну, я также сделал фрагмент кода для ограничения высокочастотного запроса ajax-запроса Keyup/Keydown. Проверьте это:

https://github.com/raincious/jQueue

Сделайте свой запрос следующим образом:

var q = new jQueue(function(type, name, callback) {
    return $.post("/api/account/user_existed/", {Method: type, Value: name}).done(callback);
}, 'Flush', 1500); // Make sure use Flush mode.

И привяжите событие следующим образом:

$('#field-username').keyup(function() {
    q.run('Username', this.val(), function() { /* calling back */ });
});
0

Взгляните на плагин autocomplete. Я знаю, что он позволяет указать задержку или минимальное количество символов. Даже если вы не используете плагин, просмотр кода даст вам некоторые идеи о том, как его реализовать самостоятельно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню