Отказаться на 500 мс, если True, иначе выполнить

Спойлер; Я совершенно новичок в jQuery/Javascript.

У меня есть логическое поле CheckMe и поле ввода textField.

Если textField пусто, CheckMe не должно отображаться, иначе оно должно быть (это означает, что при переходе от непустого к пустому CheckMe следует сразу же снова скрыть). Я хочу разобрать задержку, скажем, 500 мс, т.е. CheckMe отображается, если text не пусто и после 500 мс последнего нажатия клавиши

Я попытался использовать функцию debounce из этот ТАК ответ (см. мою реализацию ниже), но проблема в том, что CheckMe также сначала скрывается после 500 мс textField пустым

<script type="text/javascript">
  function debounce(fn, duration) {
  var timer;
  return function(){
    clearTimeout(timer);
    timer = setTimeout(fn, duration);
  }
}

$(document).ready(function () {
    const textField= $("#textField");
    const CheckMe= $("#CheckMe");
    CheckMe.hide();

    textField.on("input", debounce(()=> {
      if (textField.val()) {
        CheckMe.show();
      } else {
        CheckMe.hide();
      }
    },500));
});
</script>

но это удаляет checkMe через 500 мс, когда я очищаю textField.

Я попытался переместить debounce в утверждение True, т.е.

...
   textField.on("input", function() {
      if (textField.val()) {
        debounce(function(){CheckMe.show();},500)
      
      } else {

        CheckMe.hide();
      }
}

но это не показывает CheckMe в любой момент


person CutePoison    schedule 06.04.2021    source источник
comment
Пожалуйста, уточните: вы хотите, чтобы он появился через 500 мс после того, как кто-то перестанет печатать. Но вы хотите, чтобы он исчез сразу (как только ввод стал четким, а не через 500 мс)?   -  person freedomn-m    schedule 06.04.2021
comment
Правильно - появляется через 500 мс, когда пользователь перестает печатать, но исчезает, когда второй textField пуст, т. е. вызов CheckMe.hide() никогда не должен откладываться, а CheckMe.show() должен откладываться на 500 мс после того, как пользователь перестает печатать   -  person CutePoison    schedule 06.04.2021
comment
Возможно, вам потребуется включить некоторый HTML/фрагмент. Какая связь между priceThreshold и textField - они одинаковы, просто опечатка в вопросе?   -  person freedomn-m    schedule 06.04.2021
comment
Просто опечатка - я поправил   -  person CutePoison    schedule 06.04.2021


Ответы (1)


Причина, по которой попытка с if () { debouce(()={}); } else { immediate(); } не работает, связана с тем, как обработчики событий хранят функцию и как debounce хранит свой таймер.

Когда вы запускаете .on("input", function() { }), это определение функции сохраняется в обработчике событий и готово к запуску. Однако с debounce это не то, что делается, вместо этого:

.on("input", function())

нет функции definition — она вызывает саму функцию, которая возвращает другую функцию, которая будет вызываться при выполнении события.

Вот почему так много вопросов о SO, говорящих, что что-то вроде моего кода запускается немедленно, когда я делаю .on("input", myfunction()), когда это должно быть .on("input", myfunction)

Таким образом, одна функция (отмена дребезга) запускается один раз при настройке события — не один раз при input срабатывании события, а только один раз при настройке события. Итак, есть только один экземпляр var timer, и все они содержатся в функции debounce. Затем событие fire вызывает код внутри return function(), который уже имеет var timer, определенный ранее во внешней области (предыдущий вызов debounce).

Если вы снова вызовете debounce со вторым входом $("#secondInp").on("input", debounce(() => ..., вы получите второй экземпляр функции со своей собственной переменной (чтобы они не конфликтовали между inp1 и inp2).

Таким образом, вы можете видеть, что если вы поместите это в обработчик событий (в if), вы будете вызывать новый debounce каждый раз (не один и тот же).

Ваша попытка ничего не дала, потому что ваш код debounce(function(){CheckMe.show();},500) просто возвращает функцию, поэтому вам нужно будет сделать

debounce(function(){CheckMe.show();},500)();`

но даже это не сработает, так как каждый раз вызывается (каждое событие), вы получаете новый экземпляр функции debounce и новый экземпляр var timer.


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

debounced запустится через 500 мс, а незаблокированный запустится сразу.

function debounce(fn, duration) {
  var timer;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(fn, duration);
  }
}


$(document).ready(function() {

  const textField = $("#textField");
  const checkMe = $("#checkMe");
  checkMe.hide();

  textField.on("input", debounce(() => {
    if (textField.val()) {
      checkMe.show();
    }
  }, 500));

  textField.on("input", () => {
    if (!textField.val()) {
      checkMe.hide();
    }
  });
});
#checkMe { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="textField" type="text">
<div id='checkMe'>^-- check this</div>


можно ли сохранить его в одном if-else

Учитывая приведенное выше объяснение, невозможно использовать вашу функцию debounce как есть; из-за return function() { } и единственного экземпляра timer. Ключ представляет собой единственный экземпляр timer.

Без устранения дребезга это можно реализовать в одной функции, используя внешнюю переменную для таймера, но потребуется каждый раз повторять код устранения дребезга (например, для 2-го входа) — только clearTimeout и setTimeout код - так что не так много - это глобальная/внешняя переменная области, которая становится проблемой.

$(document).ready(function() {

  var textFieldTimer = null;
  
  const textField = $("#textField");
  const checkMe = $("#checkMe");
  checkMe.hide();

  textField.on("input", () => {
    if (textField.val()) {
      clearTimeout(textFieldTimer);
      textFieldTimer = setTimeout(() => checkMe.show(), 500);
    }
    else {
      checkMe.hide();
    }
  });
});
#checkMe { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="textField" type="text">
<div id='checkMe'>^-- check this</div>


Итак, как мы можем использовать и то, и другое: повторно используемую функцию и внутри if внутри обработчика событий?

Сохраняя единственный экземпляр timer в самом элементе — используя .data() в приведенном ниже коде, но любой метод сохранения одного экземпляра для каждого элемента также будет работать.

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

function debounce2(fn, duration)
{
    var timer = $(this).data("timer");
    clearTimeout(timer);
    $(this).data("timer", setTimeout(fn, duration));
}

$(document).ready(function() {

  $("#checkMe, #checkMe2").hide();

  $("#textField, #textField2").on("input", function() {
    if ($(this).val()) {
      debounce2.call(textField, () => $("#checkMe").show(), 500);
    }
    else {
      $("#checkMe").hide();
    }
  });



  // second input confirming `timer` is per element.
  
  $("#textField2").on("input", function() {
    if ($(this).val()) {
      debounce2.call(textField, () => $("#checkMe2").show(), 500);
    }
    else {
      $("#checkMe2").hide();
    }
  });  
});
#checkMe, #checkMe2 { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="textField" type="text">
<div id='checkMe'>^-- check this</div>
<hr/>
<input id="textField2" type="text">
<div id='checkMe2'>^-- check this</div>

person freedomn-m    schedule 06.04.2021
comment
Аааа да - это работает! Но разве нельзя сохранить его в if-else? Просто, чтобы сделать его более читабельным - person CutePoison; 06.04.2021
comment
return function() и обработчики событий работают иначе. Добавлю деталей.. - person freedomn-m; 06.04.2021