Most of the time, we click the button to submit data, but in the case of poor network conditions or unclear interactive prompts, users will click the button for several times in a period of time, if the button is not protected, it will cause repeated data submission, resulting in data abnormalities. Today, I will share a relatively general solution.

The idea is to add a variable to maintain the state of the existing buttons, but declaring the same number of variables on a page with many buttons is not maintension-friendly.

## Existing problems

<div class="button">submit</div>
Copy the code
var button = document.querySelector('.button');

button.onclick = submit;

function submit (e) {
  // Simulate asynchrony
  var promiseCb = new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('Submitted successfully');
    }, 1000);
  })
  
  return promiseCb.then(
    function (res) {
      // Handle the callback
      console.log(res); })}Copy the code

The solution

To better encapsulate the old code, you need to avoid adding extra variables, so you write an actionDelegate function that encapsulates the original action, and the original code only needs to be modified

button.onclick = submit; Left button down. The onclick = actionDelegate (submit);function actionDelegate (action) {
  // do something
}
Copy the code

Then we need to type the action. If the action returns a normal object, we consider it to be a synchronous function. If the action returns a PROMsie, then we think we need to wait for the promise state to end before we can execute the action again

function actionDelegate (action) {
  // Get the return value of the function
  var returnValue = action(e);

  // Determine the return value is PROMISE
  if (returnValue && returnValue.constructor && 
    returnValue.constructor.name === 'Promise') {
      // Protect the button from being pressed
  }
  else {
    // let it go}}Copy the code

So it’s important that you eventually return a promise in the Submit function

function submit (e) {
  // Simulate asynchrony
  var promiseCb = new Promise(...).return promiseCb
}
Copy the code

Then we will deal with the most important part, which is to save the state of the button. In this case, I obtained the node of the button through the event and stored the state on the node attR. There are other ways to store the state

function actionDelegate (action) {
  return function (e) {
    if (e.target.getAttribute('progress-status') = = ='processing') {
      // Skip subsequent logic if there is a state in progress on the button
      return false;
    }
    
    // Get the return value of the function
    var returnValue = action(e);

    // Determine the return value is PROMISE
    if (returnValue && returnValue.constructor && 
        returnValue.constructor.name === 'Promise') {
      
      // Keypoints store the button state on node properties
      e.target.setAttribute('progress-status'.'processing')
    
      return returnValue.then(
        function () {
          // The state is reset after the promise ends
          e.target.setAttribute('progress-status'.'initial'); })}}}Copy the code

The final code assembled looks like this

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

button.onclick = actionDelegate(submit);

function submit (e) {
  // Simulate asynchrony
  var promiseCb = new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('Submitted successfully');
    }, 1000);
  })
  
  return promiseCb.then(
    function (res) {
      // Handle the callback
      console.log(res); })}function actionDelegate (action) {
  return function (e) {
    if (e.target.getAttribute('progress-status') = = ='processing') {
      // Skip subsequent logic if there is a state in progress on the button
      return false;
    }
    
    // Get the return value of the function
    var returnValue = action(e);

    // Determine the return value is PROMISE
    if (returnValue && returnValue.constructor && 
        returnValue.constructor.name === 'Promise') {

      var originInnerHTML = e.target.innerHTML;
      
      // Keypoints store the button state on node properties
      e.target.setAttribute('progress-status'.'processing')
      e.target.innerHTML = 'Submitting... ';
    
      return returnValue.then(
        function () {
          // The state is reset after the promise ends
          e.target.setAttribute('progress-status'.'initial'); e.target.innerHTML = originInnerHTML; })}}}Copy the code

Afterword.

It was originally implemented as an AngularJS directive instead of ng-click, but later realized that it would be used in other projects, so I reimplemented the logic in native code. This is probably better implemented in AngularJS because directives themselves have separate scopes and don’t need to declare variables repeatedly. While writing the tutorial today, I realized that using attR might be more convenient. If you have a better way to implement it, you can communicate.

thank you

If you like this article, please follow the column and like it

##JSbin

The demo source code