zoukankan      html  css  js  c++  java
  • 我的前端工具集(十)按钮点击操作锁

    我的前端工具集(十)按钮点击操作锁

     

    liuyuhang原创,未经允许禁止转载

    目录

    我的前端工具集

    1、需求

      很多时候,在用户操作的时候是不按照正常的思路来的,做一个程序的goodcase比较容易,做badcase就比较困难。

      在前端的诸多内容中,封装一个统一的操作锁就比较重要,防止重复提交,防止用户在同一个按钮上点个没完。

    2、思路

      在点击这个问题上,不管点击的是什么,只要提供一个遮罩,比如进度条,比如弹出框,都可以阻止用户继续点击。

      但是这个方式比较恶,相当于将整个app都彻底拒绝,本质上将整个app都做成了一个单例的应用,在很多程度上,

      这当然是不允许的。

      所以,提供一个极小的,起眼的或不起眼的小遮罩,只遮住这个刚刚被点击过的操作按钮或元素,该遮罩提供一定

      时间的效果,同时可以以某个条件触发去除该遮罩。

      很多时候,这种操作锁都是发生在提交表单的等待时间,可能等待前端校验(通常瞬时),也可能等待后端回执,来

      决定下一步要做什么。常见的做法是在发ajax之前指定一个flag,在该flag的值为A的时候,该操作无法重复,在操作

      结束以后,该flag值为B,以此来防止重复提交。

      我不确定操作与操作之间的关系,也许点击某一个操作的时候,允许操作A执行,但是不允许操作B执行。

      所以,各种点击操作之间,应该有一定的关联关系,而且,一个页面通常会有多个点击来进行,因此需要对每一个

      点击进行注册,分别管理上锁,并且可以分别关闭之。

      获取当前点击元素相对于父元素的位置,偏移等信息,创建一个新的div,该div允许浮动,并且插入到该点击元素的父元素内。

      使其形成一个遮罩,同时给一个简单的表示,表示该操作正在执行,你可以替换成文字,动画,图片,anything...

      

      自己改吧,当然也可以做成一个进度条,一个燃烧特效,一个计时器,都可以......

    3、代码

      代码如下,都在注释里,手累不想多写了

    /**
     * 按钮添加遮罩操作锁,该锁为浏览器操作锁,可使用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;+" + 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)
    }

    4、使用

    这里贴一个实际工作中的部分代码实例

    希望你只关注操作锁的注册和移除......

        //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;
                }
            }
        }        

    以上!

  • 相关阅读:
    markdown
    线段树模板
    Trie模板 UVALive 3942 Remember the Word
    使用swift写sprite Kit的模仿微信打飞机游戏
    Graffiti support page
    使用代码控制ScrollView的contentSize
    资料整理
    pd.to_sql()用法
    如何将表格的数据导入到mysql
    安装启动MySQL8.0,报错:1053
  • 原文地址:https://www.cnblogs.com/liuyuhangCastle/p/9926193.html
Copyright © 2011-2022 走看看