2012年4月20日 星期五

hyperlink 可以 disabled 嗎?

記得以前在網頁上,如果想要把一個按鈕變成不可點擊,只需要將該按鈕的屬性設為  disabled  就可以了。只是現在我是有很多個超連結(hyperlink),也希望透過設定 disabled 就能把該超連結變成無效且不可點擊。

於是我試著寫出以下的測試程式:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>    
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <a href="#" onclick="alert('A1');" >A1</a> <br />
    <a href="#" onclick="alert('A2');" >A2</a> <br />   
    <a href="#" onclick="alert('A3');" disabled="disabled" >A3</a>
    </div>

    <div>
    <input type="button" value="B1" onclick="alert('B1');" /> <br />
    <input type="button" value="B2" onclick="alert('B2');" /> <br />
    <input type="button" value="B3" onclick="alert('B3');" disabled="disabled" />
   
    </div>
    </form>
</body>
</html>

這似乎沒甚麼難的,只是... 當我開啟 firefox、chrome 等瀏覽器時,原先被設為 disabled 的超連結竟然還可以被點選,且觸發 onclick 事件。這種現象在 Button 身上並不會因瀏覽器不同而有所差異,目前只有超連結才會。

為了避免這不一致的運作行為,我參考了 Disable anchors in Chrome/WebKit/Safari 的作法,去判斷每一個超連結,如果屬性有設 disabled,就避免觸發 click 事件。

所以加上一段 jQuery Code 去處理所有 disabled 的超連結後,就可以適用於各瀏覽器了。


<script>
    $(function () {
        //避免 disabled 的 hyperlink 被觸發, chrome,firefox 需要這段處理
        $("a").click(function () {
            if ($(this).attr("disabled") == "disabled") {

                event.preventDefault();
            }

        });
    });
</script>
 
參考:
01: Disable anchors in Chrome/WebKit/Safari
02: jQuery .attr(“disabled”, “disabled”) not working in Chrome
03:.prop() vs .attr()

2012年3月13日 星期二

雖然你已經沒了,但我還是會難過

走在大賣場的餅乾區,隨手一拿,幾乎大部分的零食所標示的反式脂肪酸都寫 0,就連泡麵也都以顯著的標語寫著:『本產品絕不含防腐劑』,人家都已經做得這麼有誠意,只差沒跪在你面前發誓,難道你不給台灣廠商一個機會嗎?

我還特別挑了一盒孔雀餅乾。它不僅反式脂肪酸是 0 ,而且還是台灣製造,重點是它還增重 45 公克,就這樣,它上了我的推車,入了我家冰箱,也進了我的肚子。



在偶然的機會裡,說真的,機會不高。我拿起塵封已久的康健雜誌,翻到第 158 期的一篇:『零反式脂肪酸,真的嗎?』它裡面有提到:

自政府於 2008 年規定,如果產品原料未使用氫化加工工序,且含量在 0.3 克/ 100 克以下,反式脂肪酸一欄即可標示為「」。

0  +  0  +  0  ≠   0


這數學式還真的走入了你我的日常生活當中,你會對這完美的數據感到高興,還是更顯不安呢?往好處想,世界衛生組織建議每人每天的攝取量以不超過 2 公克為限,0 與 2 之間,似乎還有著一些些的距離...

但是切記,別買「樂天小熊餅乾」給你們家寶貝吃,因為在康健雜誌於 2011年11月8日 ~11月11日 所抽查的報告中,它的反式脂肪酸是 4.09 。



參考:
01:康健雜誌 158 期
02:零反式脂肪,真的嗎?
03:小心隱形的油脂殺手-反式脂肪!

不會吧!又掛了

[InvalidOperationException: 由於該物件目前的狀態,導致作業無效。]
   System.Web.HttpValueCollection.ThrowIfMaxHttpCollectionKeysExceeded() +2692482
   System.Web.HttpValueCollection.FillFromEncodedBytes(Byte[] bytes, Encoding encoding) +61
   System.Web.HttpRequest.FillInFormCollection() +148

[HttpException (0x80004005): URL 編碼型式資料無效。]
   System.Web.HttpRequest.FillInFormCollection() +206
   System.Web.HttpRequest.get_Form() +68
   System.Web.HttpRequest.get_HasForm() +8743911
   System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull) +97
   System.Web.UI.Page.DeterminePostBackMode() +63
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +133


出現上面錯誤訊息時,正想要設個中斷點來看看到底哪邊發生錯誤,沒想到,連 Page_Load 事件都還沒進去,且遺言也還沒交待清楚,畫面就已經死掉了。

原來,在 2011 年12月,於德國的 Chaos Communication Congress 會議中,有人展示了透過少量的 Http Request 就可以讓網頁站台變得很忙碌,沒多久就可以讓網站忙到無法再回應新的 Http Reguest 請求而達到阻斷服務攻擊(Denial of Service)的效果。

隨後,在保哥的文章中找到一篇相關文章,於其技術摘要說明裡指出:


我們在開發 ASP.NET 網站時通常都會使用 Request.Form 物件來取得透過瀏覽器 POST 過來的資料,而此物件型別為 NameValueCollection 類別,該類別儲存 Key 的方式是透過雜湊運算的方式計算出來的 (hash-table data-structures),主要目的就是為了用極快的速度找到該 Key 的對應資料,不過此類別自行實做了有別於 .NET Framework 內建的雜湊演算法,它會將所有傳入的 Key 都先轉換為大寫,並使用名為 DJBX33X (Dan Bernstein's times 33, XOR) 的雜湊演算法進行計算,這將導致此演算法有機會遭到 Meet-in-the-middle 攻擊。

駭客能利用這個弱點實做出輕易可達成的 雜湊碰撞攻擊 (Hash collision attacks),此類攻擊可透過傳入大量的 POST 資料並且夾帶許多特別計算過的 Key 值,讓這些 Key 值在這些 Hash-table 中產生出相同的雜湊值,如此一來在這個 Hash-table 中就會出現許多擁有相同雜湊值的 Key,這便是「雜湊碰撞」的由來。而這類雜湊碰撞的情況正是 DJBX33X 演算法的致命傷,當一個 NameValueCollection 中存在著過多的「雜湊碰撞」就會導致電腦花上許多時間對該 Hash-table 進行運算,所以會讓 ASP.NET 執行緒花上數十分鐘到數小時的時間來操作相對應的 Key 值,如此一來便會耗盡網站伺服器的 CPU 資源,進而達到阻斷服務的目的。


就在 Chaos Communication Congress 會議之後,微軟很迅速的將這弱點進行了補強,也提供了 windows update 的服務,基本上,只要你的電腦有乖乖接受每個微軟的安全更新,理論上都已經可以避免如上所說的攻擊手法了,只是,微軟做了甚麼補強?

微軟開始重訂了一個遊戲規則。要求大家 Post 到後台的物件項目不可以超過 1000 個。包括以下:


1.<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
2.<input type="hidden" id="h_field" runat="server">
3.有已挑選值的 DropDownList (如果下拉清單裡面沒有任何資料,則不算)
4.有已挑選值的RadioButton(如果 RadioButton 裡面沒有任何被選取的資料,則不算)
5.有已挑選值的CheckBox(如果 CheckBox 裡面沒有任何被選取的資料,則不算)
6.有已挑選值的ListBox(如果 ListBox 裡面沒有任何被選取的資料,則不算)

自己詳細檢查了一下出錯的那一頁程式,上面的輸入控制項的確玲郎滿目啊!!

不過,剛開始對於微軟這種補強方法,還真的有點失落,心裡滴咕著,這算甚麼補強嘛,是補弱吧~那真的有需要使用到這麼多欄位需求時,難道得跟客戶說:


事實並非如此的。雖然微軟預設只提供上限 1000 ,但如果有更大的需求時,可以在 web.config 裡的 <appSettings> 加上 :

<add key="aspnet:MaxHttpCollectionKeys" value="2500" />

數值 value 可依實際你所想要允許的最大數量來決定。

Denial of Service 攻擊,的確是一種很難克服的難題。就算你的程式本身沒有問題,還是很有機會被攻擊,更何況被有心人士找到漏洞呢。相信日後這種攻擊手法,會越來越常見。這次微軟非常迅速的發佈更新,讓所有的 .Net 網站可以降低被攻擊的機率,真的值得讚賞。只是,我並沒有因此而得到特別的喜悅。因為我是接到來電說:「paladin,快來看,你的系統當掉了!」。


參考:

01:ASP.NET 發現重大資安弱點影響範圍涵蓋 ASP.NET 1.1 ~ 4.0
02:具有大量表單金鑰、檔案或 JSON 裝載成員的 ASP.NET 請求失敗,並產生例外狀況
03:ASP.NET Security Update Shipping Thursday, Dec 29th

2012年3月7日 星期三

自動調整 iframe 高度


以前使用 iframe 時,總是無可避免當 iframe 裡的資料過長時,就得透過 scroll bar 來捲動,如果刻意將 iframe 的高度設很高,則又擔心有些時候會太浪費網頁版面,如果能夠讓 iframe 依據內容而調整高度,似乎是個比較有彈性的作法。


關於這個想法,在網路上找到兩種類似的作法。


一、Lost-In-Code
在這個網站,提供了一個方便你外掛的 javascript,jquery.autoheight.js。日後只需要將 iframe 的 class 設定為 『autoHeight』,就能夠自動調整 iframe 的高度了。雖然他取名為 jquery,但仔細去觀察 jquery.autoheight.js 這隻程式,裡面並沒有用到 jquery 的語法。作者雖然在他的網頁上註明要引用 jquery 函式庫,但我發現,不引用也是可以的。但實際操作上,自己有遇到一個問題,就是去點選瀏覽器右上方的「最大化/往下還原」時,因為不會重算父親的高度而因此破功。所以自己再加上 $(window).resize() 去重算就可以解決。也因為如此,我還是把 jquery 引用進去好了。


    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.autoheight.js" type="text/javascript"></script>
    <script>
        $(function () {
            //攔 resize 事件,並呼叫 jquery.autoheight.js 的 doIframe() 方法
            $(window).resize(function () {               
                doIframe();                
            });
        });
    </script>

<body>
    <form id="form1" runat="server">
    <div>
    <div class="dean_ch">這是父親頁面</div>
    以下是 iframe <br />
    <iframe width="100%" src="FrameA.aspx"  scrolling="auto" frameborder="0"  class="autoHeight" id="ifA"></iframe>    
    </div>
    </form>
</body>

另外 『91』與 『梅問題』 也針對這方法做了介紹,可以參考。


二、Phine Solutions
這個方式適用於每個 iframe,自己去控制呼叫者(父親)所設定的高度。此外,為了處理「最大化/往下還原」的問題,也加上 $(window).resize() 的處理。

    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script>
        function DoAdjHeight() {
            var theFrame = $("iframe", parent.document.body);
            theFrame.each(function () {
                $(this).height($(document.body).height() + 35);
            });
        }
        $(function () {

            DoAdjHeight();

            $(window).resize(function () {
                DoAdjHeight();
            });


        });
    </script>



以上兩種方法,發現它們都會在程式裡面加上一個特別數字 35,似乎是一種最適切的高度位置吧!自己曾經刻意將瀏覽器的縮放比例任意變大變小,的確在有些情況,上述的兩種方式也會突然冒出 scroll bar ,但這種特殊的操作行為,或許可以當成例外,畢竟會這樣使用的人不多;但果真要去處理的話,可能就要去調整這特別的數字。目前,我將他設定為100,幾乎任何縮放比例都可以顯示正常。所以這地方,如果有需要時才去調整這參數會比較適當。

測試程式下載 】
於測試程式中,TestA.aspx 是使用 Lost-In-Code 的方法,而 TestB.aspx 則是使用 Phine Solutions。



參考:
01:http://www.lost-in-code.com/programming/jquery-auto-iframe-height/ (lost-in-code)
02:http://www.dotblogs.com.tw/hatelove/archive/2010/08/12/javascript-iframe-autoheight.aspx (91)
03:http://www.phinesolutions.com/use-jquery-to-adjust-the-iframe-height.html (Phine Solutions)
04:http://www.minwt.com/?p=2425

2012年2月23日 星期四

javascript 上的 reverse()


字串的反轉,在 SQL、C# 使用 Reverse 就可以輕鬆完成。但同樣的需求發生在 javascript 上,卻有點不一樣。在 javascript 裡,並非不支援 Reverse,而是 Reverse 的對象不是字串,而是陣列。所以在使用上,就不能直接呼叫使用,必須稍微做些調整。

看了網路上所介紹到方法,大約可分成兩類:

第一種:透過迴圈方式,將字串由後往前抓一遍
String.prototype.reverse = function() {
    var s = "";
    var i = this.length;
    while (i>0) {
        s += this.substring(i-1,i);
        i--;
    }
    return s;
}

var s="paladin";
alert(s.reverse());
 

第二種:把字串轉成陣列,並將陣列反轉後再組成字串
String.prototype.reverse=function(){return this.split("").reverse().join("");}

var s="paladin";
alert(s.reverse());

個人還蠻喜歡第二種寫法,太簡潔了。日後只需要用:


alert("paladin".split('').reverse().join(''));

就可以把名字倒過來寫了。

此外,還有一個很少使用到的 html 標籤 <bdo>,它可以不透過 javascript 就直接將字串反轉,甚至 <bdo> 標籤裡頭所包含的物件呈現順序也是可以自行設定為由右至左或由左至右,只需去設定 dir 屬性即可(rtl:由右至左,ltr:由左至右)。
<div><bdo dir="rtl">paladin <img alt="1" /> <img alt="2" /></bdo></div>
參考:
01:Reverse-a-txet
02:How do you reverse a string in place in JavaScript?
03:HTML <bdo> Tag

2012年2月20日 星期一

讓 GridView 具有簡單又不失專業的 EmptyDataTemplate


使用 GridView 時,如果它的資料來源沒資料時,顯示個「找不到、0筆資料...」類的訊息,似乎可以讓使用者很清楚的區別。這個功能只需要在 <EmptyDataTemplate>做些訊息的設定即可。但如果是希望即使沒資料,也必須讓 GridView 的 Header 都顯示出來,或是有自訂的樣式時,那要怎麼處理呢?

我在 sidesofmarch 的「Cleaning up the GridView’s EmptyDataTemplate (damn those tables!)」發現了作者的怒吼,認為與其花很多時間在研究 <EmptyDataTemplate>,不如簡單地去判斷是否資料來源有資料,如果沒有,則隱藏目前的 GridView,再進一步去顯示自己所要顯示的資訊。

另外也比較了「ListView EmptyDataTemplate - how to use EmptyDataTemplate」這篇文章,透過 CSS 的定義去達成效果,似乎比較符合自己目前的期待,但如果能夠有更簡單一點的作法,會比較漂亮一些。

更仔細點觀察 GridView ,當我有設定 <EmptyDataTemplate> 且資料來源為 0 時,如果 GridView 原先有放了 4 個欄位,則 <EmptyDataTemplate> 會產生一段 HTML Code:

<tr class="Theader"><td colspan="4"></td></tr>

不管日後在  <EmptyDataTemplate> 放任何東西,都會放在 <td colspan="4"> 裡面,如此就會讓我原先已經套好視覺的 GrideView 顯得與既有的其它 GridView 看起來就會有點不一樣,有點怪,有點壞。

不過,我發現,如果在 <EmptyDataTemplate> 設定一個自訂的 <tr>:

<tr><th>上傳文件</th><th>上傳日期</th><th>說明</th><th>建檔人</th></tr>

,則所產生出來的 HTML,就會有兩個 <tr>,也就是:

<tr><td colspan="4"></td></tr>
<tr><th>上傳文件</th><th>上傳日期</th><th>說明</th><th>建檔人</th></tr>





這樣子的效果,除了第一個 <tr>會讓畫面很突兀外,基本上都已經滿足我的需求了。為了讓第一個 <tr>在畫面上消失,我試著使用 jQuery 把它隱藏起來。

在  <EmptyDataTemplate> 裡,我放入:
<tr class="tr_empty"><th>上傳文件</th><th>上傳日期</th><th>說明</th><th>建檔人</th></tr>


jQuery 則是:
<script>
    $(function () {

        $(".tr_empty").each(function () {

            $(this).parent().find("tr").first().hide();

        });

    });

</script>


這段 jQuery,是在頁面上搜尋所有有定義 class name 為 tr_empty 的物件,往上找到他的父親,<tbody>,再從 <tbody> 裡面往下搜尋第一個 <tr>,並將其隱藏。

這樣就可以將第一個 <tr>隱藏,只剩下我所要呈現的第二個 <tr>

參考:
01:Cleaning up the GridView’s EmptyDataTemplate (damn those tables!)
02:ListView EmptyDataTemplate - how to use EmptyDataTemplate

2012年2月17日 星期五

讓頁尾永遠放在下面

一般來說,網頁的頁首應該要擺最上面,頁尾要擺最下面,中間則是放些比較正經的內容。但如果某個頁面實在乏善可陳,就容易出現頁尾下方空出一大片空白的現象,如何把頁尾永遠放到最下面,真的只是「面子」問題,單純就是看得比較順眼就是了。


在「jQuery教學-重現DIV區塊高度100%的夢想」提到了 autoheight.js ,會透過 jQuery 去計算扣掉頁首、頁尾後需要擴展的內文高度,實際練習後發現非常簡單,真不愧為作者所號稱的懶人包。

使用時,記得將 jQuery 以及  autoheight.js 引用:


 
<script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
<script src="Scripts/autoheight.js" type="text/javascript"></script>

在網頁的排版上,將頁首、內文、Footer 分別各放在一個 Div 裡,參考一下作者所提供的類別定義:


  • none="ture"->直接取得實際高度,排除所有CSS的設,如:border、margin、padding等。
  • _height="none"->扣除高度。
  • _height="auto"->高度100%
所以我的頁面上,大至會有以下安排:
   <div _height="none"> 頁首
   <div _height="auto"> 內文
   <div _height="none"> 頁尾

如此便可以簡單達成頁首、頁尾分別擺放在網頁的上面以及下面。

參考:梅問題:jQuery教學-重現DIV區塊高度100%的夢想

備份下載 :autoheight.js