優雅解決按鈕」重複點擊「問題

1、對思否的一點吐槽

思否上的許多按鈕都沒有作重複點擊檢測的問題,每每致使點擊沒反應,屢次點擊後忽然發表多條相同內容,好比和一位同窗的私信:
image.pngjavascript

又如一個問題下的評論:
image.pngjava

若是你在這篇文章下發表評論,能夠不當心多點幾下"提交評論"按鈕,會發現也存在相同的問題~~~ios


這個問題怎麼解決呢?
簡單點,使用一個lock標記,在請求發出時上鎖,上鎖後就不能夠再發請求,能夠在請求結束後解鎖:axios

let clickButton = (function () {
  let lock = false
  return function (postParams) {
    if (lock) return
    lock = true
    // 假設使用axios發送請求
    axios.post('urlxxx', postParams).then(
      // 表單提交成功
    ).catch(error => {
      // 表單提交出錯
      console.log(error)
    }).finally(() => {
      // 無論成功失敗 都解鎖
      lock = false
    })
  }
})()

button.addEventListener('click', clickButton)

固然對於button按鈕,可使用setAttribute('disabled', xxx)和removeAttribute('disabled')來代替lock標記。promise

這個方案問題在於,對於每一次按鈕點擊,咱們都要寫個lock標記,至關於重複的邏輯會出如今代碼的各個地方——是否是能夠封裝一下呢?函數

2、封裝按鈕鎖定、解鎖邏輯

寫一個裝飾器將邏輯封裝起來:post

function ignoreMultiClick(func, manual = false) {
  let lock = false
  return function (...args) {
    if (lock) return
    lock = true
    let done = () => (lock = false)
    if (manual) return func.call(this, ...args, done)
    let promise = func.call(this, ...args)
    Promise.resolve(promise).finally(done)
    return promise
  }
}

將想監聽點擊回調函數func做爲傳遞給ignoreMultiClick進行裝飾,會返回一個新的函數,使用該函數做爲點擊的回調事件便可。
這裏一樣用了一個標記lock來上鎖,有兩種方法解鎖:this

  1. 手動解鎖:能夠給ignoreMultiClick傳遞一個參數manual,意思是主動調用解鎖。若該參數爲truthy,則點擊事件觸發時會給原始的點擊回調func傳遞一個參數done,done是一個函數,調用它能夠解鎖。
  2. 自動解鎖:可使原監聽函數func返回一個promise,在該promise決議後自動執行解鎖操做。由於Promise管理回調函數很是方便,而且像axios這樣很是經常使用的請求庫返回值自己也是一個promise,因此默認狀況使用這種方式。固然返回promise並非必須的,有時候咱們在發請求前會進行一些驗證,驗證沒經過則直接return,此時裝飾器函數也能正常處理,由於使用Promise.resolve包裹了一下promise: Promise.resolve(promise).finally(done)

3、使用實例

自動解鎖使用例子:url

let clickButton = ignoreMultiClick(function (postParams) {
  if (!checkForm()) return // 假設有一些檢測表單的操做,檢查不經過則直接返回
  // 返回promise
  return axios.post('urlxxx', postParams).then(
    // 表單提交成功
  ).catch(error => {
    // 表單提交出錯
    console.log(error)
  })
})
button.addEventListener('click', clickButton)

手動解鎖:spa

let clickButton = ignoreMultiClick(function (postParams, done) {
  if (!checkForm()) return done() // 表單驗證不經過解鎖
  axios.post('urlxxx', postParams).then(
    // 表單提交成功
  ).catch(error => {
    // 表單提交出錯
    console.log(error)
  }).finally(() => done()) // 請求結束解鎖
})
button.addEventListener('click', clickButton)

普通場景下仍是自動解鎖比較簡單,由於可能有多個條件分支,手動解鎖須要在每個返回的地方都調用done。

相關文章
相關標籤/搜索