Wang's blog Wang's blog
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Wang Mings

跟随大神,成为大神!
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • CSS

  • Npm

  • Vue

  • HTML

  • Node

  • Yaml

  • React

  • 框架

  • 规范

  • Electron

  • JS演示

  • VuePress

  • JavaScript

    • DOM
    • 事件
    • 实用API
    • 作用域
    • 基础篇
    • 定时器
    • 内存管理
    • 异步操作
    • 日期处理
    • 点击约束
      • 使用方式
    • 防抖节流
    • this的学习
    • ES面向对象
    • JS事件详解
    • new命令原理
    • 内置对象篇
    • 函数柯里化
    • 正则表达式
    • 浏览器模型
    • 事件订阅发布
    • 获取页面高度
    • 面向对象编程
    • JS随机打乱数组
    • js各种开发技巧
    • 函数的深入浅出
    • 防抖与节流函数
    • 模块化的使用总结
    • JS获取和修改url参数
    • JS设计模式总结笔记
    • fetch拦截和封装使用
    • 浏览器渲染原理流程
    • JavaScript对象从浅入深
    • JavaScript高级程序设计
    • Promise使用和实现方法
    • JS的种打断点调试方式
    • js深浅拷贝详解与实现
    • 九种跨域方式实现原理
    • 多种数组去重性能对比
    • 闭包原理以及使用场景
    • 判断是否为移动端浏览器
    • 比typeof运算符更准确的类型判断
    • 搞清事件循环、宏任务、微任务
    • 将一维数组按指定长度转为二维数组
  • TypeScript

  • 微信小程序

  • TypeScript-axios

  • 前端
  • JavaScript
wangmings
2022-07-19
目录

点击约束

# 点击约束

// 注入全局方法
(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

把上面一段代码,引入页面即可

# 使用方式

<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

以上点击约束代码由大佬发明,一劳永逸的点击约束解决方案 (opens new window)

编辑 (opens new window)
日期处理
防抖节流

← 日期处理 防抖节流→

最近更新
01
theme-vdoing-blog博客静态编译问题
09-16
02
搜索引擎
07-19
03
友情链接
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式