2011年5月27日 星期五

Global.asax 的 Application 事件觸發順序


在 Global.asax 所定義的事件,依據 ASP.NET Application Events in Global.asax 分類,可以分為:每次頁面的請求都會觸發,與依據條件來觸發兩種。在上圖所表示的,則是每個被使用者要求的頁面都會觸發的事件順序。

舉例來說,如果在 Application_BeginRequest 事件寫一行 Response.Write("paladin"); 則這個網站的每個頁面開頭第一行都會秀出 paladin 這個字眼。

另外依據條件來觸發的事件,則是如下:
從 Visual Studio 2005 開始,預設已經在方案總管理看不到 Global.asax 這檔案了。但如果個人有這方面的需求,還是可以透過新增的方式把這檔案加入。當你打開這 Global.asax 檔案時,會發現他是屬於 Code Inline 的寫法,對於習慣 Code Behind 的人來說,會有一點點不同,詳細的比較可以參考保哥文章(關於 Code Behind 與 Code Inline 開發模式的使用時機與技巧)。假如你想在  Global.asax 撰寫 Application_BeginRequest 事件,則只要直接加入下面一段就可以:

void Application_BeginRequest(object sender, EventArgs e)
    {
        //撰寫自己的 BeginRequest 程式碼
        Response.Write("***************");
    }

Steven Cheng 曾在論壇中提過另外有關 Code Behind 的寫法。
1.在與 Global.asax 同一層的檔案結構,新增一個 globalcode.cs 檔案。並讓這個類別繼承 HttpApplication。

//globalcode.cs

public partial class globalcode : HttpApplication
{
  public globalcode()
  {

  }

  void Application_BeginRequest(object sender, EventArgs e)
  {

    HttpContext.Current.Response.Write("paladin is here..");
  }
}

2.將原先 Global.asax 裡的
<%@ Application Language="C#"  %>
修改成
<%@ Application Language="C#"  CodeFile="globalcode.cs" Inherits="globalcode" %>

如此,也可以讓 Global.asax 以 Code Behind 來撰寫了。

參考:
01:ASP.NET Application Events in Global.asax
02:如何:使用 Visual C# .NET 建立 ASP.NET HTTP 模組
03:Working with the ASP.NET Global.asax file
04:Application_BeginRequest does not seem to run
05:關於 Code Behind 與 Code Inline 開發模式的使用時機與技巧

縮小我的 javascript 大小

近日看好幾個 open source 的程式,他們不約而同的都習慣將 xxx_src.js 另外再寫一支 xxx.js 。理由何在,就是要縮小 javascript 的檔案大小,這樣一來,使用者在讀取網頁時,也會因為所需下載的 javascript 檔比較小,而加快網頁傳送速度。xxx.js 是經過壓縮後的檔案,對於 javascript 檔而言,所謂的「壓縮」,僅僅去除多餘的空白甚至是註解而已,用以減少檔案的大小。但是,經過壓縮過後的 javascript,相對也會造成「閱讀不易」的缺點,所以這幾個 open source 的作法,就是同時保留原始的程式 xxx_src.js 與 壓縮過的 xxx.js 。而程式實際上的運行,當然是使用 xxx.js 。只是日後有任何修改需求時,需要先去修改 xxx_src.js ,然後再將他進行壓縮後,把結果放到 xxx.js 。

在網路上,目前有看到兩個不錯的線上壓縮 javascript 的網站,經過測試後,效果都還不錯。分別是 javascriptcompressor 與 YUI Compressor。

javascriptcompressor:預設的處理方式,會將原始程式多餘的空白、註解都刪除。但如果你還想進一步縮小的話,他有一個 「Shrink variables」選項可以勾選,打勾後你會發現,他連程式裡面函數的參數名稱,都會用最簡單的方式來取代。譬如說,你有個函式: function iNeed(woman,lady,spicy_girl) ,壓縮後的結果,就會變成 function iNeed(a,b,c) ,由原本 37 個字元變成 21 個字元。


YUI Compressor:預設的處理方式,會將原始程式多餘的空白、註解都刪除,並將函數的參數名稱,用最簡單的方式來取代。但如果你不希望函數裡的變數被取代時,可以將 JavaScript Options 裡的 「Minify only, no symbol obfuscation.」這選項打勾,這樣就可以避免變數名稱被替換。

參考:
01:http://javascriptcompressor.com/
02:http://refresh-sf.com/yui/

2011年5月19日 星期四

加強 Rich Editor 的安全性

為了讓網站提供一個豐富的編輯介面,一般都會去選擇一個功能強大的 Rich Editor / Html Editor 來使用。在 ASP.Net 裡,為了能夠順利配合 Rich Editor 的使用,會將頁面的 ValidateRequest 屬性設成 false。

<%@ Page Language="C#" ValidateRequest="false" %>

當 ValidateRequest 屬性為 true 時,ASP.Net 會去偵測頁面上是否有所謂的惡意字元,如果有發現的話,就會丟出 HttpRequestValidationException。但當你有 Rich Editor 的需求時,就不得不關閉 ASP.Net 預設所提供的安全防護。


假如當你關掉 ValidateRequest 後,使用者在你的 Rich Editor 編輯器輸入以下內容:

<style>@import"javascript:location.href='http://yahoo.com.tw'";</style>

之後存到資料庫,日後只要使用者瀏覽這段內容時,他的網頁就會被導到 http://yahoo.com.tw,造成我們耳熟能詳的釣魚網站攻擊。當然,比較新的瀏覽器已經針對這些有問題的寫法進行防堵,Fire Fox、Chrome、IE8 目前並不會執行上面那段程式碼,但 IE6 卻會。也就是說,當使用者的瀏覽器是 IE6 時,只要他瀏覽到被注入惡意程式碼網站時,就會發生網頁轉址的現象。

為了避免網站被值入惡意程式,可以參考[參考01][參考02]的作法,先對使用者網頁所輸入的內容利用 HttpUtility.UrlEncode() 進行編碼,然後在針對你所允許的 HTML 與法進行開放的動作。尤其是[參考02],連參考資訊都整理的非常豐富,值得好好拜讀。



參考:
01:How To: Prevent Cross-Site Scripting in ASP.NET
02:警告:为了安全请不要随意将ASP.Net的validateRequest="false" 

2011年5月18日 星期三

<button>在 IE6 與 IE8 有不同的表現行為

在網頁上,要顯示一個按鈕,除了使用
<input type="button" value="btn" id="btn" name="btn">
之外,你還可以用
<button id="btn" name="btn"></button>
來達到目的。

但使用<button>這語法,最近卻讓我吃足了苦頭,發現在 IE6 與 IE8 竟然會有不同的執行結果。經過觀察後發現,在 IE6使用<button>,並不會觸發 PostBack,但在 IE8 卻會。

原來,<button> 其實有一個屬性:type,可以允許你挑選 submit、button、reset。選擇 submit 時,就會觸發 Asp.Net 的 PostBack,而 button 則是視為一般的按鈕事件,並不會觸發 PostBack。 當你不指定時,就會以預設行為模式來處理。而有趣的地方,就在於這預設行為模式怎麼判定。在 IE6,預設行為模式是 button,也就是不會觸發 PostBack,但是在 IE8,則改成 submit,變成會觸發 PostBack了,另外,非IE 的瀏覽器,則預設行為一律都是 submit。

以下是測試程式:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Button.aspx.cs" Inherits="Button" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>未命名頁面</title>
    <style>
    * { font-size:14px }
    p {width:400px}
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <button id="btn" name="btn" value="btnTest">Default</button><br />    
    <button id="btnSubmit" name="btnSubmit" value="btnSubmit" type="submit">Force Submit</button><br />    
    <button id="btnButton" name="btnButton" value="btnButton" type="button">Force Button</button><br />    
    </div>
 <script>
        document.write('<p><font color="blue">Browser Name:</font><br/>'+navigator.appName+navigator.appVersion+'</p>');
  document.write('<font color="blue">Default:</font> ' + document.getElementById('btn').type + '<br />');
  document.write('<font color="blue">Force Submit:</font> ' + document.getElementById('btnSubmit').type + '<br />');
  document.write('<font color="blue">Force Button:</font> ' + document.getElementById('btnButton').type + '<br />');
  

 </script>
    
    </form>
</body>
</html>


執行後於各瀏覽器的結果:
所以,為了能在不同瀏覽器上達到預期的效果,請別忘了在使用 <button> 時,定義好他的 type 屬性。

參考:
01:IE 8 Button Element Bug
02:HF9015: IE6 IE7 IE8(Q) 中 BUTTON 元素的 type 属性默认值不是 submit

第56號教室

在加州的貧民小學裡,有位擔任五年級的老師雷夫.艾奎斯( Rafe Esquith),靠著對於教育的熱誠,並沒有放棄貧瘠之地的學生,而這些學生當中,不乏來自黑道與毒品的家庭。在雷夫的想法裡,我的學生並非沒有能力,只是他們太缺乏自信,從住家的周遭環境一直到學校,這一切對他們都是不友善的。如果老師僅僅依據教科書照本宣科來教,最後再以考試來判定學生學習成果,對這些孩子來說,是一點幫助都沒有,反而會讓他們更加放棄自己。雷夫摒棄傳統制式地教學,把我們熟知的大富翁遊戲,活生生地搬到教室裡,讓學生可以體驗、並選擇所要擔任的角色。此外,也透過虛擬貨幣(教室幣)的流通,讓孩子從小可以從遊戲中培養正確的金錢觀念。從中穿插歷史、數學等原先會讓他們頭疼的議題,讓這些冰冷的知識融入生活中,似乎讓雷夫找到可以跟孩子溝通的方法了。

與其將雷夫的教學行為視為「職業」,倒不如說是「生活」,每年48週,每週6天,每天12小時,他幾乎快與學生生活在一起了。下課後,陪學生編排莎士比亞名劇、打球、玩音樂,假日還陪學生到各地博物館、音樂廳參訪,而這些絕大多數的費用,都是雷夫自己掏腰包的。也因此,在他的教室裡,彷彿為這些孩子打造了一個快樂天堂,他們稱之為「第56號教室」。

在雷夫多年的經營下,昔日他所指導的學生陸陸續續唸完知名大學、也開始在業界有著不錯的工作。這些學長、學姐,自然感念當年雷夫對他們的付出,於是在週末的雷夫教室裡,常常出現這些學業事業有成的學長姊回來幫忙,更有人為雷夫成立了基金會,開始接受外界捐款,使得雷夫的教育方式可以繼續延續下去。

如今,雷夫的知名度曾獲頒「美國最佳教師獎」、「大英帝國榮譽成員」、「善用生命獎」,甚至有許多學校將校長職位虛位以待,就等雷夫點頭入座。而雷夫的回答則是:「我一輩子只想當五年級的老師,如果我沒做到,我就是騙子」。

參考:第56號教室的奇蹟: 讓達賴喇嘛、美國總統、歐普拉都感動推薦的老師

2011年5月17日 星期二

讓 fMath 的設定檔改抓 web.config

舉例來說,數學符號產生工具 fMath,他將可以設定的參數都放在 configMathMLEditor.xml 裡,由上圖可知,fMath 在執行時,會去把 XML 檔案讀進來。這的確是個很方便的維護方法,可是在 Asp.Net 裡自己已經有一個 Web.Config 了,是否可以將 configMathMLEditor.xml  裡某個屬性「urlGenerateImage」改成去讀取 Web.Config 的值呢?

進一步去看 fMath 的使用方式如下:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" width="850" height="460" id="editML" name="editML" align="middle">
 <param name=wmode value="transparent">
 <param name="allowScriptAccess" value="sameDomain"/>
 <param name="allowFullScreen" value="true"/>
 <param name="loop" value="false"/>
 <param name="quality" value="high" />
 <param name="flashVars" value="configUrl=configMathMLEditor.xml"/>
 <param name="movie" value="mathml/MathMLEditor.swf?configUrl=configMathMLEditor.xml" />
 <embed src="mathml/MathMLEditor.swf?configUrl=configMathMLEditor.xml"
     wmode="transparent"
  flashVars="configUrl=configMathMLEditor.xml"
  loop="false"
  quality="high"
  width="850"
  height="460"
        id="editML"
  name="editML"
  align="middle"
        swliveconnect="true"
  allowScriptAccess="sameDomain"
  allowFullScreen="true"
  type="application/x-shockwave-flash"
  pluginspage="http://www.adobe.com/go/getflashplayer" />
</object>

裡面有提到使用 configMathMLEditor.xml 的地方一共有四個。而我的作法,則是另外寫一隻新的 config.ashx,讓它先去讀取 configMathMLEditor.xml 的原始資料,而這個 xml 裡,已經先將 urlGenerateImage 的值用 $Capture 來取代了。接著再使用取代變數的方式,將 $Capture 以 Web.Config 裡 appSettings 的值來替換。如此,上面提到有4個地方引用了 configMathMLEditor.xml ,就要改成 config.ashx 了,也就是如下:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" width="850" height="460" id="editML" name="editML" align="middle">
 <param name=wmode value="transparent">
 <param name="allowScriptAccess" value="sameDomain"/>
 <param name="allowFullScreen" value="true"/>
 <param name="loop" value="false"/>
 <param name="quality" value="high" />
 <param name="flashVars" value="configUrl=config.ashx"/>
 <param name="movie" value="mathml/MathMLEditor.swf?configUrl=config.ashx" />
 <embed src="mathml/MathMLEditor.swf?configUrl=config.ashx"
     wmode="transparent"
  flashVars="configUrl=config.ashx"
  loop="false"
  quality="high"
  width="850"
  height="460"
        id="editML"
  name="editML"
  align="middle"
        swliveconnect="true"
  allowScriptAccess="sameDomain"
  allowFullScreen="true"
  type="application/x-shockwave-flash"
  pluginspage="http://www.adobe.com/go/getflashplayer" />
</object>

最後的重點,就是要撰寫自己的 config.ashx ,簡單的測試程式如下:

<%@ WebHandler Language="C#" Class="config" %>

using System;
using System.Web;
using System.IO;

public class config : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        
        context.Response.ContentType = "text/xml";
        
        Stream iStream = null;
        
        // Identify the file to download including its path.
        string filepath =Path.Combine(context.Server.MapPath("./"), "configMathMLEditor.xml");

        // Identify the file name.
        string filename = System.IO.Path.GetFileName(filepath);

     
        // Open the file.
        iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
                    System.IO.FileAccess.Read, System.IO.FileShare.Read);

        //Read Data from FileStream and save to string
        StreamReader sRead = new StreamReader(iStream);
        string strNewFile = sRead.ReadToEnd();
        sRead.Close();

        //將變數取代為 web.config  的值
        strNewFile = strNewFile.Replace("$Capture", System.Configuration.ConfigurationManager.AppSettings["CaptureUrl"]);

        context.Response.Write(strNewFile);
        context.Response.Flush();
        context.Response.End();
      
        
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }

}

經過一番打造之後,就可以讓 fMath 裡的設定值,改成去抓 web.Config 了。

參考:
01:response.WriteFile 無法下載大型檔案
02:http://www.fmath.info/
03:FileStream - editing an existing text file

javascript 抓取 web.config 的 appSettings

在 aspx 頁面的 javascript 程式裡,如果想要能夠抓取 web.config 裡的設定資訊時,可以使用 Literal 這個伺服器控制項來完成。當您想以程式設計方式設定文字且不要加入額外的 HTML 標記時,可將 Literal Web 伺服器控制項加入網頁。Literal 控制項十分實用,因為這個控制項可以在不加入不屬於動態文字的項目下,將文字動態加入網頁。舉例來說,當你使用 Label 控制項時,網頁輸出的結果就會是 <span>xxx</span>,但如果是使用 Literal 控制項,則只會輸出 xxx 而已。

假設我目前的 web.config 設定了 TestEmpNo 如下
<appSettings>
  <add key="TestEmpNo" value="526275" />
</appSettings>

在 .aspx 頁面裡,我的 javascript 就可以寫成如下:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ConTest.aspx.cs" Inherits="ConTest" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script>
        alert('Admin Empno:' + '<asp:Literal runat="server" Text="<%$ appSettings:TestEmpNo%>" />');
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
    </div>
    </form>
</body>
</html>

這裡是搭配以前所提到「SqlDataSource 使用 web.config 的 appSettings」的文章,透過<%$ appSettings:TestEmpNo%> 去抓取 Web.config 內容。

如果你有很多的 javascript 都有共用資訊的需求時,統一放在 web.config 裡也不失為一個好方法。但前提必須是沒有機密性的資料才行,不然你只要檢視網頁原始碼,就....就被看光光了啦~

參考:
01:HOW TO:將 Literal Web 伺服器控制項加入至 Web Form 網頁
02:ASP:Literal控件用法
03:SqlDataSource 使用 web.config 的 appSettings

2011年5月16日 星期一

如何取得 web.config 裡的 httpRuntime 資訊

常常會有機會需要去取 web.config 裡的設定值來參考,尤其是資料庫的連線字串。遇到這種情況,可以很簡單地透過  ConfigurationManager.AppSettings.Get(key);  來完成。但如果有人要的是 裡的資訊時該怎麼辦呢? 因為有可能想要知道目前 web.config 是否有針對檔案上傳的大小作限制。

遇到這情況,就要改用以下方式來撰寫:

HttpRuntimeSection section = (HttpRuntimeSection)ConfigurationManager.GetSection("system.web/httpRuntime");

進一步完成一個測試程式如下:

HttpRuntimeSection section = (HttpRuntimeSection)ConfigurationManager.GetSection("system.web/httpRuntime");
        int maxFileSize = section.M
axRequestLength ; string strTimeOut= section.ExecutionTimeout.ToString(); Response.Write(string.Format("WebConfig Settings: maxRequestLength: {0} executionTimeout: {1}", maxFileSize, strTimeOut));


圖片的上半部是執行結果的圖,而下半部則是當 Web.Config 沒有設定 時所跑出的結果。可以發現,當你沒有設定 時,預設值是 4096 KB,而等候逾時期間則是 110秒。


後記:這一篇文章其實是上禮拜就 PO 過了,沒想到隔天竟然被 Google 移除。最近 Google 的伺服器其實不大穩定,官方說明是因為美國大停電,造成 Blogger 暫時變成唯讀,無法編輯。我也在此同時發現自己無法編輯 Blogger 文章,時間長達2天之久。但我也發現最近 PO 的文章竟也離奇失蹤了,心裡期待著 Blogger 會將這篇文章還給我,但還是落空了。還好自己有習慣用 Google Reader 將自己的文章列入,所以雖然 Blogger 將文章移除,但 Google Reader 還是有幫我保留一份,也讓丟失的文章得以重見天日。


2011年5月11日 星期三

揭鳥因毛禍獵刀


聽到班長於課堂中提到一句:「揭鳥因毛禍獵刀,犛牛愛尾命難逃;蜂釀蜜後身先死,財寶積多枉苦勞」,一聽就知道裡面會有好幾個故事可以講(文出於「佛教聖眾因緣集」)。

一、揭鳥因毛禍獵刀

揭鳥,是深山中的一種鳥,牠的羽毛非常漂亮,所以特別愛惜它,萬一被雜草或其他的東西粘住,牠便不敢輕舉妄動,深怕這一動,美麗的羽毛就會脫落。當遇到危難時,其牠飛禽早就跑光了,而惜羽甚於命的揭鳥,可能還在煩惱羽毛會被扯掉的問題,而一動也不動的呆在那兒。也正因為如此,在獵人眼中,這種鳥就變成「兵家必爭之鳥」了。

二、犛牛愛尾命難逃

有一譯是說:犛牛,也是因為愛惜牠美麗的尾巴,所以特別珍貴。但也因此遭受人類的覬覦,而面臨被捕殺的命運。

但有另一種解釋是說:犛牛對尾巴非常的愛護保惜。當牛鬥爭時,牠的尾巴因恐受損,而挾在股間不敢露出來。其實牛的尾巴乃是最不懂用的東西,馬尾才是有用之物。然而馬之尾巴,卻亂拂一場。凡夫深著五欲之事,也和犛牛在愛惜其沒有甚麼價值之尾一樣。對于生死大事,當置之不理。對於于不甚緊要之事,卻照顧得無微不至。 文出於:六祖壇經機緣品─「汝若但勞勞,執於念經,以為功課者,何異犛牛愛尾?」

三、蜂釀蜜後身先死

蜜蜂天天飛去採取花粉蜜,辛辛苦苦的釀成糖蜜,做為幼蜂的飼料。結果偷蜜的人,用煙火熏灼,把蜜蜂統統驅離蜂巢,不但被偷去了全部的蜂蜜,並且連窩巢中的蜂卵也被殺盡。蜜蜂的釀蜜召禍,與人類的積財召患,又有什麼兩樣呢?

這整句話,其實就是要告訴我們:財寶積多枉苦勞。我們平日為了累積財富,汲汲營營去計較,甚至不惜去傷害別人。原本工作的目的只為圖一口飯吃而已,慢慢卻變了。因為我需要更大的房子、因為我需要為子女鋪路、因為我希望老了以後能夠輕鬆一點...,但等到你真的老了,你也還是只能吃得下一口飯而已,甚至吃得更少。在老子道德德經的第四十四章也同時提到:「是故甚愛必大費,多藏必厚亡。」愛惜必定造成極大的耗費,而聚斂越多,損失必定越大。最後引「四十二章經」的話來總結:「財色之于人,譬如小兒貪刀刃之蜜甜,不足一食之美,然有截舌之患也。」受財色所誘惑的人,就像用舌頭在刀口上舔蜜一樣,雖然可以嚐到一時的甜頭,但一不小心連舌頭都會被割掉。

參考:

2011年5月10日 星期二

用 javascript 選取 class name 物件

習慣了 jQuery 所提供的選擇器 (Selectors),可以很方便的選取頁面上有相同 class name 的物件。但當你在沒有辦法使用 jQuery 的情況下,就得事事都要親力而為了。或許,以後的以後,像是 HTML 5 的標準裡,已經新增了  getElementsByClassName() 這方法,但我目前還沒有勇氣告訴使用者,本網站拒絕不支援 HTML5 的瀏覽器參訪。

但這問題還好,其實可以在 google 上找到各式各樣的範例來參考,也不用擔心你會找不到。只是自己將測試過、合用的範本記錄下來,方便日後 Copy ...

我在 anyexample 網站找到一段適用於目前各主要瀏覽器都可正常使用的程式 getElementsByClass():

<html>
<head>
<script>
function getElementsByClass( searchClass, domNode, tagName) { 
 if (domNode == null) domNode = document;
 if (tagName == null) tagName = '*';
 var el = new Array();
 var tags = domNode.getElementsByTagName(tagName);
 var tcl = " "+searchClass+" ";
 for(i=0,j=0; i<tags.length; i++) { 
  var test = " " + tags[i].className + " ";
  if (test.indexOf(tcl) != -1) 
   el[j++] = tags[i];
 } 
 return el;
} 

function SearchClassSwitch()
{

 var cssArray=getElementsByClass('dv');

 for(i=0; i<cssArray.length; i++) 
 {
  if (cssArray[i].style.display=='')
   cssArray[i].style.display = 'none';
  else
   cssArray[i].style.display = '';
 }
}


</script>
</head>
<div class="dv">讓我藏起來</div>
<div class="dv">看不見</div>
<input type="button" value="隱藏切換" onclick="SearchClassSwitch();" />
</html>

他執行的結果就會如下圖:


既然先前有提到, HTML 5 會開始支援 getElementsByClassName() 方法,趁這機會也來測試一下:

<html>
<head>
<script>
function getElementsByClass( searchClass, domNode, tagName) { 
 if (domNode == null) domNode = document;
 if (tagName == null) tagName = '*';
 var el = new Array();
 var tags = domNode.getElementsByTagName(tagName);
 var tcl = " "+searchClass+" ";
 for(i=0,j=0; i<tags.length; i++) { 
  var test = " " + tags[i].className + " ";
  if (test.indexOf(tcl) != -1) 
   el[j++] = tags[i];
 } 
 return el;
} 

function SearchClassSwitch()
{

 var cssArray=getElementsByClass('dv');

 for(i=0; i<cssArray.length; i++) 
 {
  if (cssArray[i].style.display=='')
   cssArray[i].style.display = 'none';
  else
   cssArray[i].style.display = '';
 }
}

function Html_5()
{
 var cssArray=document.getElementsByClassName('dv');
 for(i=0; i<cssArray.length; i++) 
 {
  if (cssArray[i].style.display=='')
   cssArray[i].style.display = 'none';
  else
   cssArray[i].style.display = '';
 }
}

</script>
</head>
<div class="dv">讓我藏起來</div>
<div class="dv">看不見</div>
<input type="button" value="隱藏切換" onclick="SearchClassSwitch();" />
<input type="button" value="隱藏切換 Html5" onclick="Html_5();" />
</html>

結果是:

IE8   不行
FireFox 3.6 OK
Chrome 11.x OK
Opera 11.x OK



參考:
01.HTML5 技術體系中的 JavaScript
02.JavaScript getElementsByClass function

2011年5月3日 星期二

使用 MSSQL 打造仿 MySQL 的group_concat 函数的山寨版

在 MySQL 有提供一個函數:group_concat,他可以讓你將資料根據某個欄位進行分組,並將分組後的結果,以逗號來分隔多筆資料。譬如說,可以將下列的資料:
轉變成:

這時候,可以善用 SQL 2005 後針對 xml 所提供相關函式來協助。透過一步一步的觀察,可以找到不錯的解決方式。

首先,先建立好我的測試資料:
create table #tmp (id int identity(1,1),empname varchar(10),area nvarchar(100))

insert into #tmp(empname,area) values ('paladin','新竹縣')
insert into #tmp(empname,area) values ('paladin','新竹市')
insert into #tmp(empname,area) values ('paladin','高雄市')
insert into #tmp(empname,area) values ('ken','嘉義市')
insert into #tmp(empname,area) values ('ken','雲林縣')
insert into #tmp(empname,area) values ('hugo','台南市')
insert into #tmp(empname,area) values ('hugo','台北市')


接著使用  「for xml path」來指定符合我們所要的 xml 結果:

select area  from #tmp sun  for xml path('')

產生結果是:

目前看起來,還需要進一步刪掉左右兩邊的 Tag。這問題,可以透過我們設定查詢欄位的連結字串來消除,可以只是簡單的在查詢欄位加上個逗號來完成。

select area+','  from #tmp sun  for xml path('')


xml 的結果整理好後,接著透過父子關係來處理。

select parent_tb.empname,
(select area+','  from #tmp sun 
  where sun.empname=parent_tb.empname 
   for xml path('')) area 
from #tmp parent_tb


依目前的資料,只需再進行 group 就可以了。

select parent_tb.empname,
(select area+','  from #tmp sun 
  where sun.empname=parent_tb.empname 
   for xml path('')) area 
from #tmp parent_tb 
group by empname


經過這幾個步驟的測試後,終於完成山寨版的 group_concat 功能了。

如果您想要得到字串結果不希望後面留下逗號,可以透過 stuff 函數來完成,調整一下原先的程式變成:

select parent_tb.empname,
 stuff((select','+area  from #tmp sun 
  where sun.empname=parent_tb.empname 
   for xml path('')),1,1,'') area 
from #tmp parent_tb 
group by empname


Ref
01.MySQL中group_concat函数
02.sql 2008如何实现mysql中group_concat?
03.What's New in FOR XML in Microsoft SQL Server 2005
04.STUFF (Transact-SQL)