天天看點

JavaScript進階程式設計 III

    在本系列的終結篇中,我們繼續學習另外一個有用的JavaScript案例,主要是通過DOM随意操作和重寫HTML頁面。和以前一樣,本文中使用JavaScript應該不經修改就可以在目前所有主要的浏覽器上使用。

        使用你的滑鼠進行魔術變換(Magic Mutating with Your Mouse)

         在建立表單時,你有時可能希望根據某些次級條件改變輸入域的類型。例如,假設你正在編寫一個圖書館的搜尋界面。如果通過作者搜尋,你可能想使用一個标準文本框。如果通過圖書類型(比方說精裝本或普通),你可能想提供一個下拉清單;以及如果使用圖書内容搜尋,就提供一個大文本域。試試下面的,你就會比較清楚了:

Search for: AuthorBindingContent

      JavaScript通過一個回調函數實作這個功能,隻要改變搜尋類型的值,回調函數就會被觸發。  

表單的布局代碼為:

        這裡使用技術其實很簡單。無論何時使用者改變searchtype的值(通過從下拉清單中選擇),onchange屬性就會調用我們的JavaScript函數,UpdateSearchField,将剛剛選擇的值和表單中搜尋框的id作為參數。該函數建立一段HTML代碼,然後使用新代碼替換目前的搜尋框。很顯著,我們正在随意重寫網頁的部分代碼。

        然後,我們複制input(它的id已經作為傳入參數--該節點最終會被我們的新節點替代)的部分或者所有屬性到新節點中,此時用到了助手函數CopyAttributes。你可能需要改變這個函數以适應你的需求。例如,你可能想要複制對象類或者對象值。複制以後,節點的屬性就設定好了。

       對于SELECT元素,我們就需要多一點工作了,需要為它建立OPTION。這個在一個循環中完成,和建立其他元素的方式一樣,使用createElement和createTextNode。後面的工作和前面一樣,隻不過不是建立HTML标簽,而是建立一個包含純文字的DOM節點。它是包含在HTML的OPTION标簽之間的文本。然後,使用DOM函數appendChild将這些option插入到SELECT标簽中間。如果一步步地看for循環中的過程,這個過程可能更加容易了解。

循環次數

生成的HTML

<SELECT>

</SELECT>

1

<OPTION value="HardCover">HardCover</OPTION>

2

<OPTION value="Paperback">Paperback</OPTION>

3

<OPTION value="Magazine">Magazine</OPTION>

     最後,我們新的searchbox節點終于建立完畢,我們需要将它放在頁面的某個位置。同時需要删除現在的searchbox。很友善地,replaceChild函數就是用來滿足我們需要的,使用我們建立的新節點替換已存在的節點(以及它的子節點,如果有的話)。這樣,我們建立的新HTML就替換了已有的id為searchbox的HTML代碼塊。至此,大功告成。

      當然,這種技巧并不局限于搜尋表單中的INPUT框,實際上通過頁面上某些事件的觸發,你可以将任何元素變為任何其他的元素。增加一點想象力,許多有趣和令人印象深刻效果就可以通過該技術實作。

動态表格(Dynamic Tables)

     在使用者界面上經常期望使用者可以輸入不确定函數的資料。例如,當填寫訂單時,使用者會一行輸入一條記錄。有了JavaScript,我們再也不用擔心是否為訂單預留了足夠的控件,讓使用者根據需要添加和删除行就可以了。下面是一個即時的示範,在上個例子的基礎上更近一步。

Catalog #

Description

Quantity

Unit Price

Total

<b>Grand Total:</b>

        要在最後增加一行,隻需要Append Row按鈕。要删除一行,就點選行尾的紅色“X”。當隻剩下最後一行時就不能删除了。另外注意,隻要你輸入了數量和價格或者删除了一行,Total和Grand Total的值就會更新。這些是如何實作的呢?首先,我們使用HTML建立一個初始的表格。注意,初始時必須至少有一行。

    這隻是一個由表單包圍的标準HTML表格,其每一個單元格都包含一個供使用者輸入資料的input标簽。注意,每一個input标簽的name和id值的都是以“_1”結尾,删除按鈕的id亦是如此。它用來保證标簽的唯一性,同時表明标簽屬于哪一行——目前就是第一行。例如,第3行中price列的name和id就是price_3,等等。Total和Grand Total域是不可用的,因為它們是計算列,是隻讀的。所有的任務都有三個JavaScript函數完成:AppendRow與同名按鈕相關聯,DeleteRow與删除X關聯,以及UpdateTotal,隻要在Quantity或者Price域按下某個鍵就會被觸發,計算總和。首先來看第一個函數:

      這個函數使用表格id作為唯一的輸入參數,計算表單的總數和總數之和。它首先計算行數,然後在其上循環。注意,第1行(下标為0)是表格的頭,是以我們跳過它從第2行(下标為1)開始。它讀取每一行的數量和價格資訊,這裡就利用了這些列id的命名規則;接着,兩者值都不為0,則将其相乘計算總數。計算結果儲存到目前行的Total列中。總數之和也被計算出來放在Grand Total域中。注意,盡管它們處于不可用狀态,我們仍然可以使用JavaScript函數對其進行指派。現在,我們來看AppendRow函數:

    再次,表格的id是唯一的輸入參數。我們擷取DOM樹中表格的第一個tr的句柄放在變量row中,然後檢查其父節點以擷取表格中總行數。接着,我們使用DOM函數cloneNode建立一個目前行的拷貝。這個函數的輸入參數表示我們最好還是包含所有子節點,是以我們獲得整個行的拷貝,而不是一個空的tr節點。助手函數,rowrenumber,用來根據相應的行數(它是表單可輸入行的總數加1)修改所有id。DOM函數appendChild将新行添加到表格中。最後,我們把輸入域,因為它們複制于第一行——我們隻想複制結構,而不複制資料。X被放在第1行的删除列中,新行中也是(當隻剩下一行時它就被空白替代,這些使用者就不會嘗試删除了)。我們來簡單看一下重新編号id的函數:

     這個從以變量newrow傳入的tr節點開始周遊DOM樹。它完成兩個級别的周遊,查找所有沒有id的域。我們隻做兩個級别深入(例如,for循環的兩個嵌套),因為我們知道在表格中這些id在什麼地方。第一級是td标簽,第二級是input标簽,也就是我們的所有id所在的地方。因為我們對id采用了特别的命名方{type}_{rownumber},現在我們就可以利用它,使用JavaScript的split函數将字元串分開,然後使用新的行号替換舊的。我們同時更新name域,如果存在的話,以及第一列的tabIndex。

     表單的最後一部分是删除行的函數:

     在HTML中,這個函數在span标簽中被調用,以this作為輸入參數。是以進入該函數,el就是被點選的span标簽的指針,并且我們必須向上走兩級找到DOM樹中的tr節點。我們也可以使用this.parentNode.parentNode作為輸入參數。首先,我們必須保證至少有兩行,因為隻剩下一行時是不允許删除的。然後,我們使用DOM函數removeChild删除行節點。注意,它是作為父元素tbody節點的方法被調用的。

     接下來,我們周遊表格樹在每一個tr節點上停止。在被删除行以後的行,如果有的話,都需要重新編号,因為它們行号已經減少了1(例如,删除行4意味着行5變成行4,行6變成行5,等等,而行1、2和3保持不變)。我們使用友善的rowrenumber函數完成這個任務,這個函數在前面已經讨論過。然後,我們停下來檢查是不是隻剩下一行(也就是說,在删除的行前要有兩行)。如果是這樣,我們使用空白替換X來隐藏删除按鈕,以使使用者不會嘗試删除它(但是如果使用者不知何故地要删除,什麼都不會發生,因為在上面我們已經對這種情況做了檢查)。最後,删除一行意味着Grand Total的值可能改變,除非删除行為空,是以我們調用UpdateTotals保證計算結果保持一緻。

      這可能是到現在為止我們看到的最複雜的例子了,但是如果你将它拆分為不同的組成部分,你會發現其實非常簡單。這個例子大量利用了DOM函數和DOM樹,并且很有希望能讓你對按照自己的意願動态操作HTML頁面有一個初步認識。作為練習,嘗試為每一行增加一列,該列包含一個Insert Row按鈕,點選按鈕可以在目前行之前插入一空白行。提示:應該隻需對AppendRow函數做一下簡單調整就可以實作。

Elusive Text

    在這個最後的例子中,仍然是使用DOM,我們将學習一個簡單的方法,用來在輸入域中顯示臨時的文本,這個文本在輸入域不完全為空時消失。這個方法可以用來節省空間,例如,直接把輸入域的标題放在輸入框内而不是左邊和上面。下面就是一個可以運作的例子:

Enter Login Name    Enter Password   

    建立這個簡單的頁面的HTML代碼如下所示:

    首先我們為浮動文本定義CSS樣式。絕對定位用來讓文本位于頁面上其他元素的最上層(這裡是輸入框)。同樣,z-index設為high用來保證文本出現在最上層,而不會隐藏到其他元素後面。overflow設定為hidden,因為我們不想在文本周圍出現滾動條。如果它不能正好符合空白,将會被截斷。

   這個表單是一個标準的表單,和函數UpdateHelpText相關連,在輸入域中有鍵被按下或釋放,或者滑鼠被釋放時。這個函數,如我們将要看到的,根據輸入區域是否為空來隐藏或者顯示文本。是以,隻要我們開始在輸入框中鍵入資訊,浮動文本就會消失。當我們完全删除它的内容是,它又會魔術般重制。我們在表單之後就調用這個函數,初始化文本并把它正确定位在表單内。在每一個input标簽之後的是div标簽,包含了文本,利用了我們上面建立的helptext CSS類。注意,因為我們設定z-index比較高使文本在最上層,現在就有了一個問題,點選文本時就選中了這個文本而不是移動光标到了輸入區域,而後者可能正是使用者想要的。為了修正這個問題,在滑鼠點選幫助文本時我們調用一個函數ChangeFocus。下面我們看一下這兩個JavaScript函數:

   ChangeFocus相當簡單。它從目前節點開始(div标簽),在DOM樹中回溯直到找到第一個input标簽。在input标簽上調用focus将文本光标送到輸入域,就好像使用者直接點選了input區域。結果就是點選浮動文本就好像直接點選了輸入區域。注意,我們可以使用id将div标簽和相應的input标簽連接配接起來——例如,input标簽的id為login,div的id為login_label,我們就可以很容易從一個節點找到另外一個節點——但是,我們使用HTML頁面結構的知識來找到節點。如果我們使用id,那麼關于頁面結構的知識和假設就沒有必要了,是以對于一般的應用程式它可能不失為一種較好的方法。

總結(Summary)

    在這一部分,我們探讨了一些使用JavaScript操作DOM的技巧。通過這些技巧,開發者可以修改HTML頁面的任何部分,可以增加原來頁面沒有的内容,删除内容,甚至可以四處拖拽。這些例子隻不過是淺嘗辄止,一點想象力就可以實作。有一點很重要,需要指出,盡管現在多數浏覽器都允許重寫計算機記憶體中的HTML頁面,如果你做了過多的更改,事情可能變得有點使人不太愉快,有些浏覽器對這種情況的處理比較好。特别地,像嘗試使用回調函數建立新節點的情況在輸入變量上可能有些不太容易把握,這種情況在IE和Mozilla家族的浏覽器之間的處理方法就大相徑庭。我希望你已經發現這幾篇文章的意義,也希望他們可以激發您的靈感,使用一小段的進階JavaScript讓你網站更加生動有趣。

繼續閱讀