事件相关的优化教程
发布时间:2023-03-10 09:12:45 所属栏目:教程 来源:
导读:大部分的事件触发依赖于用户与浏览器的交互,但用户的行为是不可控的,许多交互设计上的缺陷与无法考虑到的因素会导致事件的频繁触发。
当事件处理器内部包含大量的操作,又不需要如此快速的响应事件时,就需要采
当事件处理器内部包含大量的操作,又不需要如此快速的响应事件时,就需要采
大部分的事件触发依赖于用户与浏览器的交互,但用户的行为是不可控的,许多交互设计上的缺陷与无法考虑到的因素会导致事件的频繁触发。 当事件处理器内部包含大量的操作,又不需要如此快速的响应事件时,就需要采用一些手段来限制事件处理器的执行。 事件的优化主要有两个目的: 减少不必要的 HTTP 请求 减少本机性能的消耗 1. 交互设计 通过交互的设计来优化事件是最常用到的方式。 如用户点击删除后将按钮禁止。 <style> .list .item {display: flex;justify-content: space-between;border-bottom: px dashed #4caf50;padding: px ;} .list .item .caption {font-weight: ;} .list .item .operates .delete {border: px solid rgb(, , );color: rgb(, , );outline: none;cursor: pointer;} </style> <div class="list"> <div class="item"> <div class="content caption"> 今天要做的事情 </div> <div class="operates caption"> 操作 </div> </div> <div class="item"> <div class="content"> 吃火锅 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> <div class="item"> <div class="content"> 和小姐姐聊天 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> </div> <script> var listEle = document.querySelector('.list'); var deleteEle = document.querySelectorAll('.delete'); deleteEle.forEach(function(el) { el.addEventListener('click', function() { console.log('开始删除...'); setTimeout(function() { var itemEl = el.parentNode.parentNode; listEle.removeChild(itemEl); console.log('删除成功'); }, ); }); }); </script> 上述例子没有在用户第一次点击后,对按钮做一个禁止,或者采用一些上锁操作。 这种情况下用户可能会点击多次,删除操作通常会发送请求到服务端做处理,不对交互做优化可能会增加服务端的压力。 通过给予按钮状态就可以改善这个情况。 <style> .list .item {display: flex;justify-content: space-between;border-bottom: px dashed #4caf50;padding: px ;} .list .item .caption {font-weight: ;} .list .item .operates .delete {border: px solid rgb(, , );color: rgb(, , );outline: none;cursor: pointer;} </style> <div class="list"> <div class="item"> <div class="content caption"> 今天要做的事情 </div> <div class="operates caption"> 操作 </div> </div> <div class="item"> <div class="content"> 吃火锅 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> <div class="item"> <div class="content"> 和小姐姐聊天 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> </div> <script> var listEle = document.querySelector('.list'); var deleteEle = document.querySelectorAll('.delete'); deleteEle.forEach(function(el) { el.addEventListener('click', function() { console.log('开始删除...'); el.setAttribute('disabled', 'disabled'); el.style.color = 'rgb(226, 174, 174)'; el.style.borderColor = 'rgb(226, 174, 174)'; el.style.cursor = 'wait'; el.innerHTML = '处理中...'; setTimeout(function() { var itemEl = el.parentNode.parentNode; listEle.removeChild(itemEl); console.log('删除成功'); }, ); }); }); </script> 在用户第一次点击按钮后,就给予按钮禁止点击的状态,同时通过样式区分给予用户一个反馈,在提高用户体验的同时,优化了整个事件。 2. 事件委托(代理) 事件委托是利用事件冒泡的特性实现的,事件委托也被称为事件代理。 通过字面意思就可以理解,子节点的事件交给父节点来执行,一旦父节点发现子节点触发了对应的事件,就执行对应的事件处理器。 如:当点击按钮的时候,删除列表上的项 <style> .list .item {display: flex;justify-content: space-between;border-bottom: px dashed #4caf50;padding: px ;} .list .item .caption {font-weight: ;} .list .item .operates .delete {border: px solid rgb(, , );color: rgb(, , );outline: none;cursor: pointer;} </style> <div class="list"> <div class="item"> <div class="content caption"> 今天要做的事情 </div> <div class="operates caption"> 操作 </div> </div> <div class="item"> <div class="content"> 吃火锅 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> <div class="item"> <div class="content"> 和小姐姐聊天 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> </div> <script> var listEle = document.querySelector('.list'); listEle.addEventListener('click', function(e) { var el = e.target; if (el.className === 'delete') { console.log('开始删除...'); el.setAttribute('disabled', 'disabled'); el.style.color = 'rgb(226, 174, 174)'; el.style.borderColor = 'rgb(226, 174, 174)'; el.style.cursor = 'wait'; el.innerHTML = '处理中...'; setTimeout(function() { var itemEl = el.parentNode.parentNode; listEle.removeChild(itemEl); console.log('删除成功'); }, ); } }); </script> 和上个小节对比效果,其实是一样的,但是这份示例代码中只在 .list 节点上绑定了事件,而上个小节则给每个按钮绑定了一个事件。 其关键的就是事件对象下的 target 属性,该属性表示当前事件流最终捕获到的元素。 很明显,根据 HTML 结构,删除按钮就是那一分支中能捕获到的最终节点。 当事件流到达捕获阶段后,则开始向上冒泡,进入冒泡阶段,在冒泡阶段会执行绑定在 .list 上的点击事件,在事件中对事件对象的 target 进行判定,如何条件就会执行真正想做的事情,这就是一个事件委托的流程。 事件委托非常适合列表相关的事件处理,假设有成千上万条的列表,这个时候每个列表的操作按钮都要绑定事件,这个消耗是非常巨大的,当列表增减还需要考虑给新列表绑定事件,给删除的列表注销事件,这个时候使用事件委托,只需要在列表之外的一个节点上绑定一个事件,其好处不言而喻。 <style> .list .item {display: flex;justify-content: space-between;border-bottom: px dashed #4caf50;padding: px ;} .list .item .caption {font-weight: ;} .list .item .operates .delete {border: px solid rgb(, , );color: rgb(, , );outline: none;cursor: pointer;} .add { border: px dashed #4caf50; font-size: px; padding: px px; margin-top: px; outline: none; cursor: pointer; } .add:active { color: white; background: #4caf50; } </style> <div class="list"> <div class="item"> <div class="content caption"> 今天要做的事情 </div> <div class="operates caption"> 操作 </div> </div> <div class="item"> <div class="content"> 吃火锅 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> <div class="item"> <div class="content"> 和小姐姐聊天 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> </div> <button class="add">增加一项</button> <script> var listEle = document.querySelector('.list'); var deleteEle = document.querySelectorAll('.delete'); deleteEle.forEach(function(el) { el.addEventListener('click', function() { console.log('开始删除...'); el.setAttribute('disabled', 'disabled'); el.style.color = 'rgb(226, 174, 174)'; el.style.borderColor = 'rgb(226, 174, 174)'; el.style.cursor = 'wait'; el.innerHTML = '处理中...'; setTimeout(function() { var itemEl = el.parentNode.parentNode; listEle.removeChild(itemEl); console.log('删除成功'); }, ); }); }); document.querySelector('.add').addEventListener('click', function() { var el = document.createElement('div'); el.className = 'item'; el.innerHTML = [ '<div class="content">', '学习', '</div>', '<div class="operates">', '<button class="delete">删除</button>', '</div>', ].join(''); listEle.appendChild(el); }); </script> 稍微改写一下之前的例子,不采用事件委托的方式,这个列表中新增的项点击删除按钮是无用的,将这个例子改成事件委托的方式: <style> .list .item {display: flex;justify-content: space-between;border-bottom: px dashed #4caf50;padding: px ;} .list .item .caption {font-weight: ;} .list .item .operates .delete {border: px solid rgb(, , );color: rgb(, , );outline: none;cursor: pointer;} .add { border: px dashed #4caf50; font-size: px; padding: px px; margin-top: px; outline: none; cursor: pointer; } .add:active { color: white; background: #4caf50; } </style> <div class="list"> <div class="item"> <div class="content caption"> 今天要做的事情 </div> <div class="operates caption"> 操作 </div> </div> <div class="item"> <div class="content"> 吃火锅 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> <div class="item"> <div class="content"> 和小姐姐聊天 </div> <div class="operates"> <button class="delete">删除</button> </div> </div> </div> <button class="add">增加一项</button> <script> var listEle = document.querySelector('.list'); listEle.addEventListener('click', function(e) { if (e.target.className === 'delete') { var el = e.target; console.log('开始删除...'); el.setAttribute('disabled', 'disabled'); el.style.color = 'rgb(226, 174, 174)'; el.style.borderColor = 'rgb(226, 174, 174)'; el.style.cursor = 'wait'; el.innerHTML = '处理中...'; setTimeout(function() { var itemEl = el.parentNode.parentNode; listEle.removeChild(itemEl); console.log('删除成功'); }, ); } }); document.querySelector('.add').addEventListener('click', function() { var el = document.createElement('div'); el.className = 'item'; el.innerHTML = [ '<div class="content">', '学习', '</div>', '<div class="operates">', '<button class="delete">删除</button>', '</div>', ].join(''); listEle.appendChild(el); }); </script> 新增的项目是不需要再重新绑定事件的。 3. 事件节流 事件节流用于控制事件触发的最小间隔。 如一个事件 100 毫秒内只能触发一次。 如窗口缩放过程中对页面的元素大小重新调整,因为 resize 事件的触发是非常快的,用户虽然在频繁的变更窗口尺寸,但用户单位时间内能感知到的事情是有限的,也许一秒内执行100次尺寸计算和一秒钟内执行10次尺寸计算,感知上是没有太大区别的,而且事件内有太多的操作,又在频繁触发事件,这样很容易造成浏览器的卡顿。 <style> .outer { position: fixed; top: ; left: ; right: ; bottom: ; background:rgb(, , ); } .outer .text { position: absolute; top: ; left: ; transform: translate(-, -); color: white; font-size: px; text-shadow: px #fff, px #fff, px #fff, px #FF1177, px #FF1177, px #FF1177, px #FF1177, px #FF1177; } </style> <div class="outer"> <div class="text">100x200</div> </div> <script> var text = document.querySelector('.text'); var resize = function() { var height = window.innerHeight; var width = window.innerWidth; text.innerText = width + 'x' + height; }; window.addEventListener('resize', resize); resize(); </script> 可以看到,resize 事件的响应是非常快的,与之类似的还有 scroll 事件,即滚动条滚动时触发的事件。 这种情况下就可以使用节流的方式来优化事件。 <style> .outer { position: fixed; top: ; left: ; right: ; bottom: ; background:rgb(, , ); } .outer .text { position: absolute; top: ; left: ; transform: translate(-, -); color: white; font-size: px; text-shadow: px #fff, px #fff, px #fff, px #FF1177, px #FF1177, px #FF1177, px #FF1177, px #FF1177; } </style> <div class="outer"> <div class="text"></div> </div> <script> var text = document.querySelector('.text'); var timer = false; var resize = function() { if (timer) return; // 判断是不是上一次事件执行完300毫秒内 var height = window.innerHeight; var width = window.innerWidth; text.innerText = width + 'x' + height; timer = setTimeout(function () { timer = null; }, ); }; window.addEventListener('resize', resize); resize(); </script> 对例子做了一个简单的修改,增加了 timer 变量,用于存放定时器的标志值(定时器的返回值),每当事件触发时,给 timer 赋值,这个时候事件就会处于一个锁住的状态,直到 300 毫秒后,timer 再次被设置为 null,表示可以触发事件。 根据需求,业务逻辑执行的时机是在定时器内还是定时器外可以自由调配。 (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |