个人前端工具集(十)按钮点击操做锁css
liuyuhang原创,未经容许禁止转载html
目录前端
个人前端工具集ajax
一、需求后端
不少时候,在用户操做的时候是不按照正常的思路来的,作一个程序的goodcase比较容易,作badcase就比较困难。跨域
在前端的诸多内容中,封装一个统一的操做锁就比较重要,防止重复提交,防止用户在同一个按钮上点个没完。数组
二、思路浏览器
在点击这个问题上,无论点击的是什么,只要提供一个遮罩,好比进度条,好比弹出框,均可以阻止用户继续点击。网络
可是这个方式比较恶,至关于将整个app都完全拒绝,本质上将整个app都作成了一个单例的应用,在不少程度上,app
这固然是不容许的。
因此,提供一个极小的,起眼的或不起眼的小遮罩,只遮住这个刚刚被点击过的操做按钮或元素,该遮罩提供必定
时间的效果,同时能够以某个条件触发去除该遮罩。
不少时候,这种操做锁都是发生在提交表单的等待时间,可能等待前端校验(一般瞬时),也可能等待后端回执,来
决定下一步要作什么。常见的作法是在发ajax以前指定一个flag,在该flag的值为A的时候,该操做没法重复,在操做
结束之后,该flag值为B,以此来防止重复提交。
我不肯定操做与操做之间的关系,也许点击某一个操做的时候,容许操做A执行,可是不容许操做B执行。
因此,各类点击操做之间,应该有必定的关联关系,并且,一个页面一般会有多个点击来进行,所以须要对每个
点击进行注册,分别管理上锁,而且能够分别关闭之。
获取当前点击元素相对于父元素的位置,偏移等信息,建立一个新的div,该div容许浮动,而且插入到该点击元素的父元素内。
使其造成一个遮罩,同时给一个简单的表示,表示该操做正在执行,你能够替换成文字,动画,图片,anything...
本身改吧,固然也能够作成一个进度条,一个燃烧特效,一个计时器,均可以......
三、代码
代码以下,都在注释里,手累不想多写了
/** * 按钮添加遮罩操做锁,该锁为浏览器操做锁,可以使用js绕过,ajax中不建议再加额外操做锁,若用户使用js绕过,应在后端再加操做锁 * 本API包括如下内容: * 1.定义全局变量缓冲区window.clickTimerMap = {}; * 2.注册按钮或click操做的内容到缓冲区,点击后使用矩形半透明遮罩遮挡该按钮防止重复点击 * 3.手动关闭计时器与遮罩的函数removeBtnClick(btnId),该操做锁完毕后应关闭遮罩,调用该函数 * 注: * 注册按钮的函数为registClickCtrl(ids); * 该功能主要针对网络延迟可能较高的操做,以及不想让用户进行频繁使用的操做 * * @author Liuyuhang at 2018 in tit-group */ /** * 按钮遮罩计时器的全局变量缓冲区 */ window.clickTimerMap = {}; /** * 注册按钮遮罩的函数 * @param:ids,按钮的id的数组,保证该页面加载成功后使用该函数,不要跨域使用 * @see:window.clickTimerMap * @see:removeBtnClick(btnId) * @ex:registClickCtrl([ "test1", "test2" ]) */ function registClickCtrl(ids) { if (ids.length > 0) { for (var i = 0; i < ids.length; i++) { if ($("#" + ids[i]).length > 0) { $("#" + ids[i]).unbind("click." + ids[i]); //unbind addClick(ids[i]); //bind console.log("regist bind :#" + ids[i]) } else { console.error("# " + ids[i] + " 的元素并不存在") } } } //=== /** * 内部函数 * 按钮遮罩函数,默认30秒关闭遮罩,无论成功与否 * 手动关闭遮罩,@see:removeBtnClick(btnId) */ function addClick(id) { $("#" + id).bind("click." + id, function() { //获取位置和大小 var targetTop = $(this).position().top; var targetLeft = $(this).position().left; var marginLeft = $(this).css("margin-left"); var marginRight = $(this).css("margin-right"); var marginTop = $(this).css("margin-top"); var marginBottom = $(this).css("margin-bottom"); var targetWidth = $(this).css("width"); var targetHeight = $(this).css("height"); var targetId = $(this).attr("id"); //建立遮罩 var div = "<div id='" + targetId + "-div' style='opacity:0.5;background-color:white;z-index:300;position:absolute;top:" + targetTop + "px;left:" + targetLeft + "px;width:+" + targetWidth + ";height:" + targetHeight + ";font-size:20px;" + "margin:" + marginTop + " " + marginRight + " " + marginBottom + " " + marginLeft + ";' " + "class='text-center'></div>"; //加载遮罩 $(this).parent().append(div); var count = targetId + "-count"; clickTimerMap[count] = 0 //加载遮罩进程内容 clickTimerMap[targetId] = setInterval(function() { if (clickTimerMap[count] % 6 == 0) { $("#" + targetId + "-div").html(".") } else if (clickTimerMap[count] % 6 == 1) { $("#" + targetId + "-div").html("..") } else if (clickTimerMap[count] % 6 == 2) { $("#" + targetId + "-div").html("...") } else if (clickTimerMap[count] % 6 == 3) { $("#" + targetId + "-div").html("....") } else if (clickTimerMap[count] % 6 == 4) { $("#" + targetId + "-div").html(".....") } else if (clickTimerMap[count] % 6 == 5) { $("#" + targetId + "-div").html("......") } if (clickTimerMap[count] > 100) { //30秒就应该中止了,没响应也不该该继续等 clearInterval(clickTimerMap[targetId]) $("#" + targetId + "-div").remove(); } clickTimerMap[count]++; }, 300); }) } } /** * 手动关闭计时器与遮罩的函数 * @param:btnId,要关闭的启用遮罩的按钮的id * @see:window.clickTimerMap * @see:registClickCtrl(ids) */ function removeBtnClick(btnId) { setTimeout(function() { clearInterval(clickTimerMap[btnId]) $("#" + btnId + "-div").remove(); }, 100) }
四、使用
这里贴一个实际工做中的部分代码实例
但愿你只关注操做锁的注册和移除......
//init $(function() { ...... registClickCtrl([ "createAssetSubmitBtn", "modifyAssetSubmitBtn", "removeAssetSubmitBtn" ]); //注册按钮操做锁 }) ...... /** * 修改已有资产,提交表单的函数 */ function modifyAssetSubmit(id) { var id = window.assetId; if (null != id && '' != id && 'undefinded' != id) { var data = { id : id, assetName : $("#assetName").val(), assetDesc : $("#assetDesc").val(), assetCode : $("#assetCode").val(), assetModalIds : $("#assetModalIds").val().toString(), assetTypeId : $("#assetTypeId").val(), inspecteStatus : $("#inspecteStatus").val(), fixStatus : $("#fixStatus").val(), otherPropertyGroupId : $("#propertyGroupIdOther").val(), } if (checkData(data) == false) { removeBtnClick("modifyAssetSubmitBtn"); //解除操做锁 topTipModal("操做提示:", "<span class='text-danger'>资产名称,资产描述,资产编码不能为空,或长度不符合要求,请更改!</span>", 3000); return null; } else { $.ajax({ type : 'POST', url : local + "modifyAssetById.do", data : data, async : true, success : function(resultMap) { removeBtnClick("modifyAssetSubmitBtn"); //解除操做锁 if (resultMap.message == "success") { topTipModal("操做提示:", "<span class='text-success'>修改为功,正在刷新列表!</span>", 3000); getAssetAll(); //点击肯定后隐藏表单 $("#addAsset").collapse("hide"); } else { topTipModal("操做提示:", "<span class='text-warning'>修改失败,请刷新后尝试从新操做!<br>" + resultMap.message + "!</span>", 3000); return null; } }, error : function(resultMap) { removeBtnClick("modifyAssetSubmitBtn"); //解除操做锁 topTipModal("操做提示:", "<span class='text-danger'>修改失败,错误码:" + resultMap + "</span>", 3000); console.error(resultMap); } }); } } else { removeBtnClick("modifyAssetSubmitBtn"); //解除操做锁 topTipModal("操做提示:", "<span class='text-warning'>请选择一个资产,再尝试修改操做!</span>", 3000); } //========== /** * 内部函数,检查data */ function checkData(data) { var name = data.assetName; var desc = data.assetDesc; var code = data.assetCode; var ids = data.assetModalIds; if (!isEmpty(name) || !isEmpty(desc) || !isEmpty(code) || ids.length > 511 || code.length < 2 || code.length > 8 || name.length > 32 || desc.length > 128) { return false; } else { return true; } } }
以上!