<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      為WebForms說幾句話,以及一些ASP.NET開發上的經驗(3)

      2007-12-23 18:44  Jeffrey Zhao  閱讀(14174)  評論(68)    收藏  舉報

      四、生成復雜的ID難以使用JavaScript操作

      我在上一篇文章的最后提到了,雖然使用WebForms我們能夠對于頁面上的HTML屬性和樣式等進行自由的定制和控制,但是有一點是毋庸置疑的,我們沒有辦法(正常的辦法吧,Hack不算)讓服務器端控件在客戶端生成一個簡單的ID。例如,一個TextBox控件,在服務器端的ID是txtUserName,但是最終在客戶端生成的ID可能是LoginForm_txtUserName,因為它被放在一個ID為LoginForm的NamingContainer中。

      有了組件模型,就出現了大量控件。控件最主要的目的之一就是復用,而復用的一個特點就是應該高度內聚,而不依賴于外部環境。因此,為了使組件內部的服務器控件最終生成的客戶端ID能夠在頁面上唯一,WebForms引入了NamingContainer這個概念。在NamingContainer中的服務器端控件最終在客戶端生成的ID,會使用NamingContainer的“客戶端ID”作為前綴。如此“遞歸”的做法保證了服務器控件在客戶端的ID唯一。

      Web 2.0在業界風卷殘云般的勢頭至今還未停歇,與其有密切相關的AJAX技術也被廣泛使用。AJAX技術從根本上講,是一種在瀏覽器中使用JavaScript實現的技術,因此使用JavaScript操作DOM元素的情況非常多見。在非WebForms的頁面中我們可以編寫如下的代碼:

      <input type="text" id="textBox" />

      <script language="javascript" type="text/javascript">
          document.getElementById("textBox").value = "Hello World!";
      </script>

      但是由于NamingContainer的緣故,我們在使用WebForms的服務器端的控件時就可能無法通過textBox在客戶端獲得文本框(生成的<input />元素)。為了解決這個問題,服務器端的控件模型提供了一個ClientID屬性,通過這個屬性,我們就可以在服務器端得到控件最終在客戶端的ID。例如,如果上面的代碼放在一個用戶控件里的話,就一定必須寫成如下形式:

      <%@ Control Language="C#" AutoEventWireup="true" %> 

      <asp:TextBox runat="server" ID="textBox" />

      <script language="javascript" type="text/javascript">
          document.getElementById("<%= this.textBox.ClientID %>").value = "Hello World!";
      </script>

      此時,當控件被放到頁面上之后,它在客戶端生成的代碼則會是:

      <input name="DemoControl1$textBox" type="text" id="DemoControl1_textBox" />

      <script language="javascript" type="text/javascript">
          document.getElementById("DemoControl1_textBox").value = "Hello World"!;
      </script>

      請注意<input />元素的name和id,它們都留下了NamingContainer的痕跡。由于我們在頁面上使用了<%= %>標記直接輸出了服務器控件的ID,這樣在客戶端的JavaScript代碼也就可以正確訪問到服務器端<asp:TextBox />對應的客戶端<input />元素了。

      這種在設計器很難預測的客戶端ID,就是使用WebForms時所謂的“客戶端ID污染”。

      接下來我們不妨來看一個略為復雜點的例子:

      <%@ Control Language="C#" AutoEventWireup="true" %> 

      <asp:TextBox runat="server" ID="textBox" />

      <script language="javascript" type="text/javascript">
          var counter = 0;

          function increase()
          {
              document.getElementById("<%= this.textBox.ClientID %>").value = (counter++);

              window.setTimeout(increase, 500);
          }

          increase();
      </script>

      上面這段JavaScript代碼的作用是每500為一個計數器加1,并且顯示在文本框上。隨著項目的發展,頁面上復雜的JavaScript代碼會越來越多,于是我們就會想辦法將其轉移到js文件中并且在頁面上引用它們。使用js文件的好處很多,便于進行代碼管理是一方面,但是最重要的好處之一還是對于性能的提高。如果JavaScript代碼完全寫在頁面上,這樣每次加載頁面都需要下載這些JavaScript代碼,而js文件可以緩存,這樣客戶端只需要在第一次加載時下載這個文件就可以了。減少了客戶端與服務器之間數據通信的大小,也就加快頁面加載的速度,提高了性能。

      不過問題就此出來了:為了能夠正確引用到頁面上的某個服務器控件生成的DOM元素,我們就必須在頁面中使用<%= %>標記來輸出控件的ClientID,但是<%= %>無法寫在js文件中,這可怎么辦?于是很多人著急了起來,我也不時會收到此類問題,似乎很難找到合適的解決辦法。于是“客戶端ID污染”似乎也就成了一個使用WebForms時非常嚴重的問題。

      有些朋友會說:“這個沒有問題啊,仔細觀察ClientID的組成方式能夠很容易找到規律的。”服務器控件的ClientID是由自身ID和它所在的NamingContainer“樹”來共同決定的,因此在理論上我們也完全可以在設計器得到“已經放置在頁面中”的某個服務器控件的客戶端ID,并將其寫進JavaScript代碼中。話雖如此,的確沒錯,但是這個解決方案實在不好,因為它違背了控件的重要特性:“復用”。作為一個控件來說,它可能會被放在任意的NamingContainer樹下,也就是說,它的客戶端ID在不同的環境中并不固定。另外,如果控件上層NamingContainer樹中有任何一個的服務器端ID被修改的話,js文件中使用的ID就需要進行改變,這樣實在不利于的維護,隨著項目增大,此類問題會愈發明顯。

      那么我們究竟該怎么做呢?

      在設法解決這個問題之前,我們先來思考一下這個問題。如果我們沒有使用WebForms進行開發,就在普通的頁面上編寫代碼,那么我們對于上面的功能會如何將其提取到js文件中呢?嗯,就直接在代碼中通過textBox這個ID來獲得DOM元素吧。那么好,請您先回答我以下幾個疑問:

      1. 為什么要寫textBox而不是其他ID呢?
      2. 如果其他頁面上有個同樣需要實現的功能,而那個文本框的id是txtCounter,那么該怎么作呢?
      3. 如果一張頁面上有兩個文本框需要顯示這樣的計數器,那么又該怎么做呢?

      上面的幾個疑問其實只反應了一件事情,那就是這個計數器的復用性實在太差。什么叫做好的復用性呢?那么我們來看一下一個典型的示例,MaskedEditExtender。我們來看看它是怎么做的:

      <ajaxToolkit:MaskedEditExtender
          TargetControlID="TextBox1"
          Mask="9,999,999.99"
          MessageValidatorTip="true"
          OnFocusCssClass="MaskedEditFocus"
          OnInvalidCssClass="MaskedEditError"
          MaskType="Number"
          InputDirection="RightToLeft"
          AcceptNegative="Left"
          DisplayMoney="Left"
          ErrorTooltipEnabled="True" />

      MaskedEditExtender的第一個屬性TargetControlID,就可以決定了究竟是為哪個文本框添加效果,然后效果的樣式可以由MaskType和Mask決定,獲得焦點的樣式和輸入錯誤的樣式可以由OnFocusCssClass和OnInvalidCssClass屬性決定,連字符輸入的順序都可以定制。

      這就是復用:愛怎么用,就怎么用。愛給誰用,就給誰用。想什么時候用,就什么時候用。

      要復用,一般總需要組件化或模塊化,內部實現通用的功能,而具體的信息應該由外部傳入。例如我們上面的計數器就應該進行改造(用到了MS AJAX Lib里的Function.createDelegate方法):

      function Counter(textBoxId, interval)
      {
          this._counter = 0;
          this._textBox = document.getElementById(textBoxId);
          this._interval = interval;
      }
      Counter.prototype =
      {
          run : function()
          {
              this._textBox.value = (this._counter ++);
              window.setTimeout(
                  Function.createDelegate(this, this.run), this._interval);           
          }
      };

      現在這個技術器的復用性已經有質的飛躍了,因為我們可以隨意指定一個客戶端的文本框進行顯示,并且可以自由地設置計數器增長的間隔時間。于是我們在WebForms頁面中就可以寫如下的代碼了:

      <asp:TextBox runat="server" ID="textBox1" />
      <asp:TextBox runat="server" ID="textBox2" />

      <script language="javascript" type="text/javascript">
          new Counter("<%= this.textBox1.ClientID %>", 500).run();
          new Counter("<%= this.textBox2.ClientID %>", 1000).run();
      </script>

      現在WebForms客戶端ID污染已經不構成問題了吧!

      其實解決客戶端ID污染的做法用一句話就能說清:“將不變的部分提取至js文件,將變化的部分(例如服務器控件的客戶端ID)留在頁面中”。但是我在這里將它上升到組件化的高度,因為它能讓我們開發出更優秀的客戶端程序。組件化的客戶端編程方式較之傳統的零散function的做法,更有利于代碼的管理,并且增強了復用性和可維護性。有人說,客戶端ID污染問題使腳本代碼很難做到“內聚”——可能他的意思是將腳本代碼提取到js文件中吧——但是我認為,這種污染“迫使”我們使用組件化的方式進行客戶端開發,而這種組件化或者模塊化的做法恰恰提高了代碼的內聚性。

      不過,似乎組件化的編程方式會寫更多的代碼,不是嗎?從理論上來說,可能的確是。不過需要注意的是,我上面提出的例子非常簡單,簡單到了其中的一半代碼是用于“組件化”編程的“骨架”上。而對于一個略為復雜的功能來說,例如一個通用的表單驗證組件,或者客戶端級聯組件,增加的這點“骨架”還算得了什么呢?

      這也算是一種因禍得福吧。

       

      相關文章:

      為WebForms說幾句話,以及一些ASP.NET開發上的經驗(1):ViewState、性能

      為WebForms說幾句話,以及一些ASP.NET開發上的經驗(2): 生成丑陋的HTML,難以進行樣式控制

      未完待續:

      五、MVC

      六、單元測試

      主站蜘蛛池模板: 小嫩模无套内谢第一次| 精品国产国语对白主播野战| 日本精品不卡一二三区| 亚洲av专区一区| 青草草97久热精品视频| 亚洲熟妇色xxxxx欧美老妇| 制服jk白丝h无内视频网站| 亚洲精品美女一区二区| 国产精品九九九一区二区| 亚洲中文字幕人妻系列| 国内揄拍国内精品少妇| 奇米影视7777久久精品| 日韩av中文字幕有码| 男人用嘴添女人下身免费视频| 中文字幕一区二区人妻| 在线观看成人年视频免费 | 九九久久自然熟的香蕉图片| 美女扒开尿口让男人桶| 国产精品久久久久7777按摩| 日产日韩亚洲欧美综合下载| 午夜在线观看成人av| 亚洲熟妇色自偷自拍另类| 国产精品视频一区二区三区无码| 亚洲av鲁丝一区二区三区黄| 少妇粉嫩小泬喷水视频www| 精品国产亚洲区久久露脸| 日韩精品中文字幕人妻| 亚洲18禁一区二区三区| 中文字幕日韩视频欧美一区| 亚洲色大成网站www永久一区 | 国产美女裸身网站免费观看视频| 高清国产美女一级a毛片在线| 久久午夜夜伦鲁鲁片免费无码影院 | 天镇县| 精品中文人妻在线不卡| 亚洲伊人久久综合成人| 成人AV无码一区二区三区| 资源在线观看视频一区二区| 一本久道中文无码字幕av| 国产精品一品二区三四区| 无码专区 人妻系列 在线|