点击约束
# 点击约束
// 注入全局方法
(function() {
if (window.hadResetAjaxForWaiting) { // 如果已经重置过,则不再进入。解决开发时局部刷新导致重新加载问题
return
}
window.hadResetAjaxForWaiting = true
window.waittingAjaxMap = {} // 接口映射 'get::http://yapi.luckly-mjw.cn/mock/50/test/users?pageIndex=1': dom
let OriginXHR = window.XMLHttpRequest
let originOpen = OriginXHR.prototype.open
let isSameSpace = false // 是否在同一个宏任务中,避免频繁触发
// 检测使用到的 dom 对象
function checkDom() {
if (!isSameSpace) { // 节流
isSameSpace = true
window.waittingAjaxMap = {} // 重置为空,重新寻找匹配的 dom
const domList = document.querySelectorAll('[data-loading]')
Array.prototype.forEach.call(domList, targetDom => {
targetDom.dataset.loading.split(',').forEach(targetUrl => {
targetUrl = targetUrl.replace(/['"[\]]/ig, '').trim()
window.waittingAjaxMap[targetUrl] = [targetDom, ...(window.waittingAjaxMap[targetUrl] || [])]
})
})
setTimeout(() => isSameSpace = false) // 下一个宏任务中,重新开放该方法
}
}
// 重置 XMLHttpRequest
window.XMLHttpRequest = function() {
let targetDomList = [] // 存储本 ajax 请求,影响到的 dom 元素
let realXHR = new OriginXHR() // 重置操作函数,获取请求数据
realXHR.open = function(method, url) {
checkDom()
Object.keys(window.waittingAjaxMap).forEach(key => {
let [targetMethod, type, targetUrl] = key.split('::')
if (!targetUrl) { // 设置默认类型
targetUrl = type
type = 'v-waiting-waiting'
} else { // 指定类型
type = `v-waiting-${type}`
}
if (targetMethod.toLocaleLowerCase() === method.toLocaleLowerCase() && (url.indexOf(targetUrl) > -1 || new RegExp(targetUrl).test(url))) {
targetDomList = [...window.waittingAjaxMap[key], ...targetDomList]
window.waittingAjaxMap[key].forEach(dom => {
if (!dom.classList.contains(type)) {
dom.classList.add('v-waiting', type)
if (window.getComputedStyle(dom).position === 'static') { // 如果是 static 定位,则修改为 relative,为伪类的绝对定位做准备
dom.style.position = 'relative'
}
}
dom.waitingAjaxNum = dom.waitingAjaxNum || 0 // 不使用 dataset,是应为 dataset 并不实时,在同一个时间内,上一次存储的值不能被保存
dom.waitingAjaxNum++
})
}
})
originOpen.call(realXHR, method, url)
}
// 监听加载完成,清除 waiting
realXHR.addEventListener('loadend', () => {
targetDomList.forEach(dom => {
dom.waitingAjaxNum--
dom.waitingAjaxNum === 0 && dom.classList.remove(
'v-waiting',
'v-waiting-loading',
'v-waiting-waiting',
'v-waiting-disable',
)
})
}, false)
return realXHR
}
})();
// 注入全局 css
(() => {
if (!document.getElementById('v-waiting')) {
let code = `
.v-waiting {
pointer-events: none;
/*cursor: not-allowed; 与 pointer-events: none 互斥,设置 pointer-events: none 后,设置鼠标样式无效 */
}
.v-waiting::before {
position: absolute;
content: '';
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.7 !important;
z-index: 9999 !important;
background-color: #ffffff !important;
}
.v-waiting-waiting::after {
position: absolute;
content: '数据加载中';
top: 50%;
left: 0;
width: 100%;
max-width: 100vw;
color: #666666;
font-size: 20px;
text-align: center;
transform: translateY(-50%);
z-index: 9999;
animation: v-waiting-v-waiting-keyframes 1.8s infinite linear;
}
@-webkit-keyframes v-waiting-v-waiting-keyframes {
20% {
content: '数据加载中.';
}
40% {
content: '数据加载中..';
}
60% {
content: '数据加载中...';
}
80% {
content: '数据加载中...';
}
}
.v-waiting-loading::after {
position: absolute;
content: '';
left: 50% !important;
top: 50% !important;
width: 30px;
height: 30px;
z-index: 9999 !important;
cursor: not-allowed;
animation: v-waiting-v-loading-keyframes 1.1s infinite linear;
background-position: center;
background-size: 30px 30px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAMAAABgZ9sFAAAAS1BMVEUAAAAypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV0ypV1HXd+eAAAAGHRSTlMAsCqA4guKPxXUY0mbH/TszbzGpTZ0VW0vSgG0AAABLklEQVRIx9WUWbaDIBBEQSYVnE3S+1/p88XCOIGYv9SXwj1tU1TLTmU0d4KlKqN/yURaFfSWTsNfBOVJOPf4GIHyscKT8HgWL9nO+xa0VEG6QLfrbsog7b/P59dSTo922WzbnUkDQcpbv2qkJqq3JvHI6arjugZdBFzody4Cf7AzjfYQk7c1T5Yq9eI6Y78ndafpihMVj3uTRuUVZ4VdTRrZCIoQCiTiejJz5Nt4vIvRwMynevywqpmQZkp1P9N1xEyEqsg+c2cuL8coHLpvhl9M17eyruFlMi0iN1s6tys0UjiXT5o0bJYccHkarsNPoiPIBMJF4qx6E6xuN6cB7jCPWnZLOY0uT7oBjRfPS6kPbrX1ssh9u2mSM87v4UMCer8ZWN0qlsr3teQ5O+oPB7wgmvuSTDAAAAAASUVORK5CYII=);
}
@-webkit-keyframes v-waiting-v-loading-keyframes {
from {
transform: translate(-50%, -50%) rotate(0deg);
}
to {
transform: translate(-50%, -50%) rotate(360deg);
}
} `
let style = document.createElement('style')
style.id = 'v-waiting'
style.type = 'text/css'
style.rel = 'stylesheet'
style.appendChild(document.createTextNode(code))
let head = document.getElementsByTagName('head')[0]
head.appendChild(style)
}
})()
export default {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
把上面一段代码,引入页面即可
# 使用方式
<div data-loading='get::loading::/your/api'>点击</div>
<div data-loading='post::loading::/your/api'>点击post</div>
<table data-loading='get::loading::/your/api/list'>表格</table>
<div data-loading='post::loading::/your/api$'>点击post, 正则用法,后面加$</div>
1
2
3
4
2
3
4
以上点击约束代码由大佬发明,一劳永逸的点击约束解决方案 (opens new window)
编辑 (opens new window)