設計網頁時,有時需要從Client-Side禁止使用者點選、編輯某些網頁元素(如TextBox、Select、Radio、Checkbox...)。偏偏設成唯讀的做法Input, TextArea, Select不盡相同,還要小心設定disabled屬性會影響值是否傳回Server-Side,過去我還特別寫了很複雜的JS萬用函數負責將各元件設成唯讀。

AJAX開始流行起來之後,我注意到其中一項有意思的特效--Modal Dialog。當使用者叫出某個檔案上傳或選取器對話框時,為了要求使用者完成選項才將焦點交回原網頁,會用一段Javascript將整個網頁刷灰,禁止使用者點選操作,唯一能操作的區塊只有聚焦的對話框。這樣就做出了IE showModalDialog的效果,而重要的是這種Javascript式的ModalDialog特效可跨瀏覽器,Firefox也適用。

仔細研究了一下它的原理,原來是它建了一塊跟網頁一樣大小的半透明灰底Mask DIV(使用style.filter特效),以絕對座標方式放在網頁的正上方,由於DIV的z-Index設得很大,所以會"浮"在網頁其他元素的上方,形成灰色刷暗效果,提示使用者網頁已被停用;而實體上也隔絕使用者接觸到下方的網頁元素。至於要開放操作的對話框,則將z-Index設得比Mask DIV大1,等同於疊在DIV之上,不受其影響。

搞懂了它的原理,我想到這招可以用來禁止使用者對Input, Button, Link等的操作(很遺憾Select在IE6中z-Index似乎是無限大,沒有東西可以蓋得住它,但在IE7 & Firefox中則可以適用。只是如果要用它做出停用欄位的效果,必須額外考量IE6的Select),甚至可以一口氣停用整個DIV, TABLE中的全部元素,十分直覺簡便。我寫出了以下的afa_PutMask(elementId)及afa_Remove(elementId)可用來加上及移除"遮片",加上遮片後被蓋住元素就不能被點選操作,在IE6, IE7, Firefox上測試都OK,有興趣的朋友可以拿去玩看看。

【注意】由Client-Side所加諸的唯讀、防寫效果並不具任何資安保全效果,當Javascript出錯或被停用時就玩完了,而一些高階玩家也能輕易找到工具穿越防線。所以Client-Side的唯讀保護,只適合用在被破解也無傷大雅的資料欄位上,或者要在Server-Side加上第二層防護。

<body>
<div>
<input id ="myInp" type="text" value="Darkthread" />
</div>
<hr />
<div id="myDiv">
This is test line 1.<br />
This is test line 2.<br />
<button>Click Me!</button>
<table><tr>
<td>Example Link:</td>
<td><a href="http://www.google.com" id="myLink">Google</a></td>
</tr></table>
</div>
<script type="text/javascript">
//JS Utility: Put operation mask on HTML element 
//Ver 1.0 @ 2007-09-22
//by Jeffrey Lee, http://www.darkthread.net
function afa_PutMask(targetId) {
    var tgt = document.getElementById(targetId);
    var mskId= targetId + "$$Mask";
    var testMsk = document.getElementById(mskId);
    if (testMsk) testMsk.parentElement.removeChild(testMsk);
    mask = document.createElement("DIV");
    mask.id = mskId;
    var maskStyle = mask.style;
    var tgtStyle = tgt.style;
    maskStyle.display="block";
    maskStyle.position="absolute";
    maskStyle.zIndex="100";
    maskStyle.filter="alpha(opacity=40)";
    maskStyle.MozOpacity="0.4";
    maskStyle.opacity="0.4";
    maskStyle.backgroundColor="#333333";
    var pos = afa_FindPos(tgt);
    maskStyle.top=pos[1]+"px";
    maskStyle.left=pos[0]+"px";
    maskStyle.width=tgt.offsetWidth+"px";
    maskStyle.height=tgt.offsetHeight+"px";
    document.body.appendChild(mask);
}
function afa_RemoveMask(targetId) {
    var tgtMask = document.getElementById(targetId + "$$Mask");
    if (tgtMask) document.body.removeChild(tgtMask);
}
function afa_FindPos(obj) {
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        curleft = obj.offsetLeft;
        curtop = obj.offsetTop;
        while (obj = obj.offsetParent) {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        }
    }
    return [curleft,curtop];
}
</script>
<script type="text/javascript">
afa_PutMask("myInp");
afa_PutMask("myDiv");
afa_PutMask("myLink");
window.setTimeout("afa_RemoveMask('myDiv');", 3000);
</script>
</body>

[Tech Summary] afa_PutMask() helps you to put a "operation mask" on any HTML element, the mask make elements (ex: input, textarea, button, link...)  look gray, non-clickable and non-editable.  It can be used as a tool to make element readonly, but there are two considerations:
1. It works fine on IE7 & Firefox, but <select> in IE6 is impossible to be masked.  If you want to use it to make every element readonly, you have to write some extra code for IE6 <select>.
2. Client side readonly doesn't provide any *REAL* security.  Javascript could be disabled or cracked, so never use it on impartant data integrity protection, or add server-side validation is a good compromise.


Comments

# by Julian

現在在製作的系統,因為輸入欄有一百多個 也有利用到這個技巧,作到唯讀的效果 當然也就碰到ie6 select永遠最上層問題 後來有上網查到iframe可以蓋掉select 但是iframe有白底,設定為透明,又沒辦法蓋掉select 最後只好放棄這個方法了…

# by Jeffrey

http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/09/25/tips-hide-select-on-ie6.aspx 你說的是這個方法嗎? 有個尷尬的解決方案,是蓋掉了,但做不出半透明,很難說它算不算克服了問題。但至少,它用來處理動態Menu會被<select>破壞,應該綽綽有餘了。

Post a comment