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