个人前端工具集(十)按钮点击操做锁

个人前端工具集(十)按钮点击操做锁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;
            }
        }
    }        

 

 

以上!