#DailyOneNote# Предотвращение многократного нажатия кнопки в течение короткого периода времени

Node.js внешний интерфейс Promise Angular.js
#DailyOneNote# Предотвращение многократного нажатия кнопки в течение короткого периода времени

每日一记 - 但并不日更

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

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

## Существующая проблема

<div class="button">
  提交
</div>
var button = document.querySelector('.button');

button.onclick = submit;

function submit (e) {
  // 模拟异步
  var promiseCb = new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('提交成功');
    }, 1000);
  })
  
  return promiseCb.then(
    function (res) {
      // 处理回调
      console.log(res);
    }
  )
}

没有提交中的保护

Решение

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

button.onclick = submit;
↓
button.onclick = actionDelegate(submit);

function actionDelegate (action) {
  // do something
}

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

function actionDelegate (action) {
  // 获取函数返回值
  var returnValue = action(e);

  // 判断返回值是 promise
  if (returnValue && returnValue.constructor && 
    returnValue.constructor.name === 'Promise') {
      // 保护按钮不被狂点
  }
  else {
    // let it go
  }
}

Поэтому важно в конечном итоге вернуть обещание в функции отправки.

function submit (e) {
  // 模拟异步
  var promiseCb = new Promise(...)
  
  return promiseCb
}

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

function actionDelegate (action) {
  return function (e) {
    if (e.target.getAttribute('progress-status') === 'processing') {
      // 如果按钮上有处理中的状态则跳过后续逻辑
      return false;
    }
    
    // 获取函数返回值
    var returnValue = action(e);

    // 判断返回值是 promise
    if (returnValue && returnValue.constructor && 
        returnValue.constructor.name === 'Promise') {
      
      // 关键点 把按钮状态保存在 node 的属性上 
      e.target.setAttribute('progress-status', 'processing')
    
      return returnValue.then(
        function () {
          // promise 结束后重置状态
          e.target.setAttribute('progress-status', 'initial');
        }
      )
    }
  }
}

Окончательный код собирается следующим образом

var button = document.querySelector('.button');

button.onclick = actionDelegate(submit);

function submit (e) {
  // 模拟异步
  var promiseCb = new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('提交成功');
    }, 1000);
  })
  
  return promiseCb.then(
    function (res) {
      // 处理回调
      console.log(res);
    }
  )
}

function actionDelegate (action) {
  return function (e) {
    if (e.target.getAttribute('progress-status') === 'processing') {
      // 如果按钮上有处理中的状态则跳过后续逻辑
      return false;
    }
    
    // 获取函数返回值
    var returnValue = action(e);

    // 判断返回值是 promise
    if (returnValue && returnValue.constructor && 
        returnValue.constructor.name === 'Promise') {

      var originInnerHTML = e.target.innerHTML;
      
      // 关键点 把按钮状态保存在 node 的属性上 
      e.target.setAttribute('progress-status', 'processing')
      e.target.innerHTML = '提交中...';
    
      return returnValue.then(
        function () {
          // promise 结束后重置状态
          e.target.setAttribute('progress-status', 'initial');
          e.target.innerHTML = originInnerHTML;
        }
      )
    }
  }
}

执行中则忽略点击

постскриптум

Изначально это была инструкция, реализованная angularjs для замены ng-click, но позже выяснилось, что она использовалась и в других проектах, поэтому логика была переделана на нативном коде. Возможно лучше реализовать в angularjs, потому что сама директива будет иметь отдельный скоуп и нет необходимости повторять объявление переменных. Когда я писал учебник сегодня, я вдруг обнаружил, что может быть удобнее использовать attr для его реализации.Если у вас есть лучший метод реализации, вы можете общаться.

Спасибо

Если вам понравилась эта статья, вы можете подписаться на колонку Пожалуйста, лайкните и поделитесь

##JSbin

исходный код демо