Запустить функцию javascript, когда пользователь закончит ввод, а не при нажатии клавиши?

377

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

Есть ли способ, чтобы я мог обнаружить, когда пользователь закончил ввод, а затем выполнил запрос ajax?

Использование jQuery здесь! Dave

  • 16
    Я думаю, что вам нужно определить «закончить печатать» для нас.
  • 7
    Хотя ответ @Surreal Dreams удовлетворяет большинству ваших требований, если пользователь начнет вводить снова ПОСЛЕ указанного времени ожидания, на сервер будет отправлено несколько запросов. Посмотрите мой ответ ниже, который хранит каждый запрос XHR в переменной и отменяет его перед запуском нового. Это именно то, что Google делает в своем Мгновенном поиске.
Показать ещё 4 комментария
Теги:
keyboard

23 ответа

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

Итак, я собираюсь догадаться, что завершение ввода означает, что вы просто остановитесь на некоторое время, скажем, 5 секунд. Поэтому, имея в виду, давайте начнем таймер, когда пользователь выпустит ключ и очистит его, когда он нажмет один. Я решил, что входной вопрос будет #myInput.

Сделаем несколько предположений...

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 second for example
var $input = $('#myInput');

//on keyup, start the countdown
$input.on('keyup', function () {
  clearTimeout(typingTimer);
  typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

//on keydown, clear the countdown 
$input.on('keydown', function () {
  clearTimeout(typingTimer);
});

//user is "finished typing," do something
function doneTyping () {
  //do something
}
  • 4
    Спасибо :) Я начал печатать свой комментарий к вопросу и понял, что у меня есть хорошая идея.
  • 0
    Извините за боль в @ss, но у вас есть два нажатия клавиш вместо нажатия двух клавиш на втором событии, и текстовое поле не является допустимым селектором, так как нет элемента textbox :)
Показать ещё 18 комментариев
316

Выбранный ответ выше не работает.

Поскольку typingTimer иногда устанавливается несколько раз (нажатие клавиши дважды до нажатия клавиши для быстрого набора текста и т.д.), Тогда оно не очищается должным образом.

Приведенное ниже решение решает эту проблему и вызывает X секунд после завершения, как было запрошено OP. Также больше не требуется избыточная функция нажатия клавиш. Я также добавил проверку, чтобы ваш вызов функции не происходил, если ваш ввод пуст.

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms (5 seconds)

//on keyup, start the countdown
$('#myInput').keyup(function(){
    clearTimeout(typingTimer);
    if ($('#myInput').val()) {
        typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

И тот же код в ванильном решении JavaScript:

//setup before functions
let typingTimer;                //timer identifier
let doneTypingInterval = 5000;  //time in ms (5 seconds)
let myInput = document.getElementById('myInput');

//on keyup, start the countdown
myInput.addEventListener('keyup', () => {
    clearTimeout(typingTimer);
    if (myInput.value) {
        typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

Это решение использует ES6, но это не обязательно здесь. Просто замените let на var а функцию arrow на обычную функцию.

  • 0
    Я считаю, что проблема, с которой вы столкнулись, заключается в том, что вы печатаете достаточно быстро, чтобы нажать на вторую клавишу, прежде чем отпустить первую.
  • 1
    Это не проблема, с которой он сталкивается, это проблема, которую любой пользователь может воспроизвести, как я только что сделал. Представьте, что в конце тайм-аута вы отправляете ajax на сервер и что-то делаете с ответом. Принятый ответ отправил бы мне повторный ответ. Поставьте оповещение и наберите очень быстро. Он будет срабатывать не только в конце тайм-аута, для меня он срабатывал неоднократно. Это самый полный ответ, и он использует меньше кода, слава.
Показать ещё 9 комментариев
67

Это просто одна строка с функцией underscore.js debounce:

$('#my-input-box').keyup(_.debounce(doSomething , 500));

В основном это говорит doSomething 500 миллисекунд после того, как я перестаю печатать.

Для получения дополнительной информации: http://underscorejs.org/#debounce

  • 5
    Кажется, также доступны для jQuery: code.google.com/p/jquery-debounce и github.com/diaspora/jquery-debounce
  • 37
    Меня удивляет, как люди считают «ОДНУ ЛИНЕЙ КОДЕКСА» критически важной. Отлично. ОДНА СТРОКА кода, для загрузки которого требуется файл JS 1.1k. Я не опровергаю это, потому что это решение. Но смелая вещь в одну строку раздражает меня, а также приводит к взлому кода.
Показать ещё 5 комментариев
39

Да, вы можете установить тайм-аут, скажем, 2 секунды на каждом событии, на котором будет срабатывать ajax-запрос. Вы также можете сохранить XHR-метод и прервать его при последующих нажатиях клавиш, чтобы вы сохранили полосу пропускания еще больше. Вот что я написал для автозаполнения script.

var timer;
var x;

$(".some-input").keyup(function () {
    if (x) { x.abort() } // If there is an existing XHR, abort it.
    clearTimeout(timer); // Clear the timer so we don't end up with dupes.
    timer = setTimeout(function() { // assign timer a new timeout 
        x = $.getJSON(...); // run ajax request and store in x variable (so we can cancel)
    }, 2000); // 2000ms delay, tweak for faster/slower
});

Надеюсь, что это поможет,

Марко

  • 0
    Я бы не рекомендовал это. Код запускает запрос API каждый раз при нажатии клавиши. Затем он отменяет запрос, если нажата другая клавиша. Несмотря на то, что это решение предотвращает запуск обратного вызова AJAX при вводе пользователем, оно все равно будет забивать запросы серверами.
  • 0
    Черт, нет, этого не будет. Используемая им функция clearTimeout очищает предыдущие события Timeout, поэтому вызывает вызов API.
Показать ещё 1 комментарий
16
var timer;
var timeout = 1000;

$('#in').keyup(function(){
    clearTimeout(timer);
    if ($('#in').val) {
        timer = setTimeout(function(){
            //do stuff here e.g ajax call etc....
             var v = $("#in").val();
             $("#out").html(v);
        }, timeout);
    }
});

полный пример здесь: http://jsfiddle.net/ZYXp4/8/

  • 0
    Это сработало для меня, хотя имейте в виду, что когда пользователь выходит из текстового поля, вы не получите событие keyup, так как оно потеряло фокус к этому моменту. Вместо этого его установка на keydown решает проблему.
10

Мне нравится ответ Surreal Dream, но я обнаружил, что моя функция "doneTyping" будет срабатывать для каждого нажатия клавиши, т.е. если вы быстро наберете "Hello"; вместо того, чтобы стрелять только один раз, когда вы перестали печатать, функция будет срабатывать 5 раз.

Проблема заключалась в том, что функция javascript setTimeout не переписывает или не убивает какие-либо старые тайм-ауты, которые были установлены, но если вы сделаете это самостоятельно, это сработает! Поэтому я просто добавил вызов clearTimeout непосредственно перед setTimeout, если установлен typingTimer. См. Ниже:

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 second for example

//on keyup, start the countdown
$('#myInput').on("keyup", function(){
    if (typingTimer) clearTimeout(typingTimer);                 // Clear if already set     
    typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

//on keydown, clear the countdown 
$('#myInput').on("keydown", function(){
    clearTimeout(typingTimer);
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

N.B. Мне бы хотелось только добавить это как комментарий к сюжету Surreal Dream, но я новый пользователь и не имею достаточной репутации. Извините!

8

Изменение принятого ответа для обработки дополнительных случаев, таких как paste:

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 2000;  //time in ms, 2 second for example
var $input = $('#myInput');

// updated events 
$input.on('input propertychange paste', function () {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(doneTyping, doneTypingInterval);      
});

//user is "finished typing," do something
function doneTyping () {
  //do something
}
5

Оба топ-2 ответа не работают для меня. Итак, вот мое решение:

var timeout = null;

$('#myInput').keyup(function() {
    clearTimeout(timeout);

    timeout = setTimeout(function() {
        //do stuff here
    }, 500);
});
4

Я не думаю, что в этом случае необходимо событие keyDown (скажите, пожалуйста, если я ошибаюсь). В моем (не jquery) script подобное решение выглядит так:

var _timer, _timeOut = 2000; 



function _onKeyUp(e) {
    clearTimeout(_timer);
    if (e.keyCode == 13) {      // close on ENTER key
        _onCloseClick();
    } else {                    // send xhr requests
        _timer = window.setTimeout(function() {
            _onInputChange();
        }, _timeOut)
    }

}

Это мой первый ответ на Stack Overflow, поэтому я надеюсь, что это кому-то поможет когда-нибудь:)

4

Ну, строго говоря, нет, поскольку компьютер не может догадаться, когда пользователь закончил печатать. Конечно, вы могли бы запустить таймер на клавиатуре и reset на каждом последующем ключе. Если таймер истекает, пользователь не набрал для длительности таймера - вы могли бы назвать это "завершенной типизацией".

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

(Если, конечно, вы не можете сказать по данным, когда они сделаны)

2

Мне кажется, что решение несколько проще с событием input:

var typingTimer;
var doneTypingInterval = 500;

$("#myInput").on("input", function () {
    window.clearTimeout(typingTimer);
    typingTimer = window.setTimeout(doneTyping, doneTypingInterval);
});

function doneTyping () {
    // code here
}
2

РЕШЕНИЕ:

Я выполнял поиск в своем листинге и нуждался в его создании на основе ajax. Это означает, что при каждом ключевом изменении результаты поиска должны обновляться и отображаться. Эта работа приводит к тому, что столько вызовов ajax отправляется на сервер, что не очень хорошо. После некоторой работы я сделал подход к серверу ping, когда клиент перестает печатать.

Решение для меня:

$(document).ready(function() {

$('#yourtextfield').keyup(function() {
s = $('#yourtextfield').val();
setTimeout(function() { 
        if($('#yourtextfield').val() == s){ // Check the value searched is the latest one or not. This will help in making the ajax call work when client stops writing.
            $.ajax({
                type: "POST",
                url: "yoururl",
                data: 'search=' + s,
                cache: false,
                beforeSend: function() {
                   // loading image
                },
                success: function(data) {
                    // Your response will come here
                }
            })
        }
    }, 1000); // 1 sec delay to check.

    }); // End of  keyup function

    }); // End of document.ready

Вы заметили, что при использовании этого таймера нет необходимости использовать какой-либо таймер.

Я уверен, это поможет другим.

Ata​​p >

1

Это простой JS-код, который я написал:

<!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>
1

Вы можете использовать событие onblur, чтобы обнаружить, когда текстовое поле теряет фокус: https://developer.mozilla.org/en/DOM/element.onblur

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

Для этого я бы предложил привязать setTimeout к событию onclick и предположить, что после x времени без нажатий клавиш пользователь перестает печатать.

0

Объявите следующую функцию delay:

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

и затем используйте это:

let $filter = $('#item-filter');
$filter.on('keydown', function () {
    delay(function () {            
        console.log('this will hit, once user has not typed for 1 second');            
    }, 1000);
});    
0

Я только что понял простой код, чтобы дождаться, пока пользователь закончит печатать:

шаг 1. установите время ожидания на ноль, а затем очистите текущее время ожидания, когда пользователь печатает.

шаг 2.trigger очистить тайм-аут для определения переменной до того, как будет запущено событие keyup.

шаг 3. определить время ожидания для переменной, объявленной выше;

<input type="text" id="input" placeholder="please type" style="padding-left:20px;"/>
<div class="data"></div>

код JavaScript

var textInput = document.getElementById('input');
var textdata = document.querySelector('.data');
// Init a timeout variable to be used below
var timefired = null;

// Listen for keystroke events
// Init a timeout variable to be used below
var timefired = null;// Listen for keystroke events
textInput.onkeyup = function (event) {
clearTimeout(timefired);
timefired = setTimeout(function () {
    textdata.innerHTML = 'Input Value:'+ textInput.value;
  }, 600);
};
  • 0
    на вопрос уже ответили ... и это очень похоже на это
0

Почему бы просто не использовать onfocusout?

https://www.w3schools.com/jsreF/event_onfocusout.asp

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

  • 0
    Когда задали вопрос, onfocusout оказал слабую поддержку (почти 7 лет назад), и эффект от onfocusout был бы несколько иным. Вам придется подождать, пока пользователь не оставит фокус на элементе, в то время как с решением по тайм-ауту / отладке он «срабатывает», как только пользователь перестает печатать, и пользователю не требуется переключать фокус. Примером использования может быть одна из тех регистрационных форм, где, когда пользователь прекращает вводить потенциальное имя пользователя, справа от поля формы появляется галочка или «X», указывающие, что имя доступно.
0

согласен с ответом @going. Другое подобное решение, которое сработало для меня, - это следующее. Единственное отличие состоит в том, что я использую .on( "input"...) вместо keyup. Это фиксирует изменения во входном сигнале. другие клавиши, такие как Ctrl, Shift и т.д., игнорируются.

var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms (5 seconds)

//on input change, start the countdown

$('#myInput').on("input", function() {    
    clearTimeout(typingTimer);
    typingTimer = setTimeout(function(){
        // doSomething...
    }, doneTypingInterval);
});
0

Если вам нужно подождать, пока пользователь завершит ввод, просто используйте это:

$(document).on('change','#PageSize', function () {
    //Do something after new value in #PageSize       
});

Полный пример с вызовом ajax - это работает для моего пейджера - количество элементов в списке:

$(document).ready(function () {
    $(document).on('change','#PageSize', function (e) {
        e.preventDefault();
        var page = 1;
        var pagesize = $("#PageSize").val();
        var q = $("#q").val();
        $.ajax({
            url: '@Url.Action("IndexAjax", "Materials", new { Area = "TenantManage" })',
            data: { q: q, pagesize: pagesize, page: page },
            type: 'post',
            datatype: "json",
            success: function (data) {
                $('#tablecontainer').html(data);
               // toastr.success('Pager has been changed', "Success!");
            },
            error: function (jqXHR, exception) {
                ShowErrorMessage(jqXHR, exception);
            }
        });  
    });
});    
0

Не уверен, что мои потребности просто странны, но мне нужно что-то похожее на это, и именно это я и использовал:

$('input.update').bind('sync', function() {
    clearTimeout($(this).data('timer'));            
    $.post($(this).attr('data-url'), {value: $(this).val()}, function(x) {
        if(x.success != true) {
            triggerError(x.message);    
        }
    }, 'json');
}).keyup(function() {
    clearTimeout($(this).data('timer'));
    var val = $.trim($(this).val());
    if(val) {
        var $this = $(this);
        var timer = setTimeout(function() {
            $this.trigger('sync');
        }, 2000);
        $(this).data('timer', timer);
    }
}).blur(function() {
    clearTimeout($(this).data('timer'));     
    $(this).trigger('sync');
});

Что позволяет мне иметь такие элементы в моем приложении:

<input type="text" data-url="/controller/action/" class="update">

Которые обновляются, когда пользователь "набирает" (без действий в течение 2 секунд) или переходит в другое поле (размывается из элемента)

0

Если вы ищете определенную длину (например, поле zipcode):

$("input").live("keyup", function( event ){
if(this.value.length == this.getAttribute('maxlength')) {
        //make ajax request here after.
    }
  });
  • 0
    Неплохая идея сделать ограниченную проверку перед отправкой запроса ajax.
0

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

Когда тайм-аут завершен, выполните ваш запрос ajax.

-1

Ничего себе, даже 3 комментария довольно правильные!

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

  • .on ('input', function() { ... }); следует использовать для запуска событий keyup, paste и change

  • Определенно .val() или .value должны использоваться

  • Вы можете использовать $(this) внутри функции событий вместо #id для работы с несколькими входами

  • (мое решение) Я использую анонимную функцию вместо doneTyping в setTimeout, чтобы легко получить доступ к $(this) из n.4, но вам сначала нужно сохранить его, как var $currentInput = $(this);

  • 0
    Как это отвечает на вопрос?
  • 0
    @ThomasOrlita этот ответ дополняет 3 наиболее рейтинговых ответа. Взгляните на принятый: 1. он не поддерживает paste , он не использует this и т. Д. (Я думаю, что это важно, но я не хочу потеряться в тоннах комментариев)
Показать ещё 1 комментарий

Ещё вопросы

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