问题描述
当前端使用
ajax提交一个表单时,因网络缓慢等原因导致数据没有及时返回数据,导致用户没有得到正确的提示,以为未点中提交按钮,进而在此点击提交按钮,进行提交数据(多半会重复多次点击)
解决方法
- 禁用按钮,并添加
loading提示 - 移除点击事件
- 结合
vue使用
常规点击请求代码
这里自己封装一个函数来模仿 ajax 请求
// 模拟ajax请求数据
function ajax() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 随机返回错误
let res = parseInt(Math.random() * 10) % 2
res === 0 ? resolve(1) : reject(2)
}, 2000)
})
}理想状态下,点击按钮应返回数据,但因为网络缓慢,所以模拟数据回来要 2s
<button id="btn">点击</button>
<script>
// 按钮点击次数
var num = 0
// 模拟ajax请求数据
function ajax() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res = parseInt(Math.random() * 10) % 2
res === 0 ? resolve(1) : reject(2)
}, 3000)
})
}
const btn = document.getElementById('btn')
btn.addEventListener('click', function() {
ajax().then(res=> {
console.log('数据回来了');
}).catch(e=>{
console.log('数据请求有问题呀');
})
console.log(++num);
})
</script>结果,作为新用户的我看到数据没有显示出来,以为没点中按钮,于是又多点了几下按钮。

解决方案
禁用按钮,添加提示 loading
代码思路:将当前点击按钮禁用,并将按钮展示文本信息进行修改
<button id="btn">点击</button>
<p>0次</p>
<script>
// 按钮点击次数
var num = 0
// 模拟ajax请求数据
function ajax() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res = parseInt(Math.random() * 10) % 2
res === 0 ? resolve(1) : reject(2)
}, 3000)
})
}
const btn = document.getElementById('btn')
var handleClick = function () {
// 禁用按钮
btn.setAttribute('disabled', true)
// 文字提示
btn.innerText = 'loading...'
// 点击次数,渲染到p标签上
num++
document.querySelector('p').innerText = `${num} 次`
// 模拟请求数据,并作出响应
ajax()
.then((res) => {
console.log('点击成功: ' + res)
})
.catch((e) => {
console.log('点击失败: ' + e)
})
.finally(() => {
// 数据请求完毕,恢复按钮
btn.removeAttribute('disabled')
btn.innerText = '点击'
})
}
btn.addEventListener('click', handleClick)
</script>移除按钮点击事件
换个思路,不妨可以直接将点击事件移除,这样用户也同样不会触发多次点击事件
<button id="btn">点击</button>
<p>0次</p>
<script>
// 按钮点击次数
var num = 0
// 模拟ajax请求数据
function ajax() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res = parseInt(Math.random() * 10) % 2
res === 0 ? resolve(1) : reject(2)
}, 3000)
})
}
const btn = document.getElementById('btn')
var handleClick = function () {
// 移除按钮点击事件
btn.removeEventListener('click', handleClick)
btn.innerText = '加载中...'
// 点击次数
num++
document.querySelector('p').innerText = `${num} 次`
// 模拟请求数据,并作出响应
ajax()
.then((res) => {
console.log('点击成功: ' + res)
})
.catch((e) => {
console.log('点击失败: ' + e)
})
.finally(() => {
// 数据请求完毕,恢复按钮
btn.addEventListener('click', handleClick)
btn.innerText = '点击'
})
}
btn.addEventListener('click', handleClick)
</script>以上两种方法结合使用
- 禁用按钮
- 添加提示
- 移除点击事件
<button id="btn">点击</button>
<p></p>
<script>
// 按钮点击次数
var num = 0
// 模拟ajax请求数据
function ajax() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res = parseInt(Math.random() * 10) % 2
res === 0 ? resolve(1) : reject(2)
}, 3000)
})
}
const btn = document.getElementById('btn')
var handleClick = function () {
// 使按钮无效处理
btn.removeEventListener('click', handleClick)
btn.setAttribute('disabled', true)
btn.innerText = '加载中...'
// 点击次数
num++
document.querySelector('p').innerText = `${num} 次`
// 模拟请求数据,并作出响应
ajax()
.then((res) => {
console.log('点击成功: ' + res)
})
.catch((e) => {
console.log('点击失败: ' + e)
})
.finally(() => {
// 数据请求完毕,恢复按钮
btn.addEventListener('click', handleClick)
btn.removeAttribute('disabled')
btn.innerText = '点击'
})
}
btn.addEventListener('click', handleClick)
</script>结合vue来使用
这里只是做了一个小的demo来解释下思路,多数情况下会用axios,可选择在封装axios请求时,进行封装这个操作。网上还有一些案例使用了指令来封装,思路有很多。
这里没有使用移除点击事件,可自行选择添加。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>防止用户同一时间内点击多次按钮,发送多次无效数据</title>
</head>
<body>
<div id="root">
<button @click="handleClick" ref="btn">点击</button>
<p></p>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script>
new Vue({
el: '#root',
methods: {
// 对ajax请求封装
ajax(button) {
let text = ''
if (button && button instanceof HTMLElement) {
text = button.innerText || ''
button.setAttribute('disabled', true)
button.innerText = 'loading...'
}
return new Promise((resolve, reject) => {
setTimeout(() => {
let res = parseInt(Math.random() * 10) % 2
res === 0 ? resolve(1) : reject(2)
}, 1000)
})
.then((res) => {
// 无论是 resolve
if (button) {
button.removeAttribute('disabled')
button.innerText = text
}
return res
})
.catch((e) => {
// 还是 catch,都能重置按钮
if (button) {
button.removeAttribute('disabled')
button.innerText = text
}
throw e
})
},
handleClick() {
// 只需要在调用的时候传入按钮
this.ajax(this.$refs.btn)
.then((res) => {
console.log('点击成功: ' + res)
})
.catch((e) => {
console.log('点击失败: ' + e)
})
},
},
})
</script>
</body>
</html>