個人前端工具集(十)按鈕點擊操做鎖

個人前端工具集(十)按鈕點擊操做鎖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;
            }
        }
    }        

 

 

以上!

相關文章
相關標籤/搜索