.摘要
本文在實(shí)例的基礎(chǔ)上討論了HTC(HTML Component)的編程方法,提出了一種編寫(xiě)腳本組件的基本模式。
2.目標(biāo)讀者
HTML開(kāi)發(fā)人員,腳本開(kāi)發(fā)人員,系統(tǒng)分析人員
3.背景知識(shí)
HTML, DHTML, CSS
4.引言
HTC(HTML Component)直譯為HTML組件,并不是一項(xiàng)新技術(shù)??墒钦f(shuō)談不上是一門技術(shù)。實(shí)際上只是IE瀏覽器內(nèi)置的一種腳本封裝機(jī)制。由于討論的人很少,而Microsoft也沒(méi)有什么技術(shù)支持,故而應(yīng)用的人很少。但是HTML有著很好的特性可以使我們的開(kāi)發(fā)工作高效。并且你有可能發(fā)現(xiàn),HTC或許可以改變你以往開(kāi)發(fā)應(yīng)用的方式。
在MSDN online對(duì)HTC的定義僅如下幾句:
HTML Components (HTCs) provide a mechanism to implement components in script as Dynamic HTML (DHTML) behaviors. Saved with an .htc extension, an HTC is an HTML file that contains script and a set of HTC-specific elements that define the component.
(HTC是由HTML標(biāo)記、特殊標(biāo)記和腳本組成的定義了DHTML特性的組件.)
一般而言,HTC是組件化了的腳本過(guò)程。盡管引入瀏覽器的機(jī)制不同,但遵循相同的SDK規(guī)范。無(wú)論是腳本運(yùn)行環(huán)境,還是DOM文檔結(jié)構(gòu)。但HTC有著極高的擴(kuò)展性。也就是說(shuō),HTC可以為我們的HTML引入高級(jí)的自定義行為(behavior)。例如自定義的attribute, method, 或者事件。這就說(shuō)明,我們可以使用HTC機(jī)制來(lái)開(kāi)發(fā)一個(gè)有著高級(jí)特性的,可重用的,可擴(kuò)展的組件。
5.實(shí)例
ButtonStyleFlat.htc:
另外有一個(gè)sample用于參考:
sample.html:
(分別見(jiàn)以下文本框)
ButtonStyleFlat.htc:
<PUBLIC:COMPONENT>
<PUBLIC:ATTACH EVENT="ondocumentready" ONEVENT="DoInit()" />
<PUBLIC:ATTACH EVENT="onmouseover" ONEVENT="DoMouseOver()" />
<PUBLIC:ATTACH EVENT="onmouseout" ONEVENT="DoMouseOut()" />
<PUBLIC:ATTACH EVENT="onmousedown" ONEVENT="DoMouseDown()" />
<PUBLIC:ATTACH EVENT="onmouseup" ONEVENT="DoMouseUp()" />
<PUBLIC:ATTACH EVENT="onclick" ONEVENT="DoClick()" />

<PUBLIC:PROPERTY NAME="ColorOver" />
<PUBLIC:PROPERTY NAME="ColorOut" />
<PUBLIC:PROPERTY NAME="ColorDown" />
<PUBLIC:PROPERTY NAME="ColorUp" />
<PUBLIC:PROPERTY NAME="Scheme" />

<PUBLIC:EVENT NAME="onPush" ID="push" />

<PUBLIC:METHOD NAME="showMessage" />

<script>
function DoInit(){
switch(Scheme){
case "Normal":
if(ColorOver==null) ColorOver='Orange';
if(ColorOut==null) ColorOut='RoyalBlue';
if(ColorDown==null) ColorDown='Black';
if(ColorUp==null) ColorUp='YellowGreen';
break;
default:
if(ColorOver==null) ColorOver='Orange';
if(ColorOut==null) ColorOut='RoyalBlue';
if(ColorDown==null) ColorDown='Black';
if(ColorUp==null) ColorUp='YellowGreen';
}
runtimeStyle.borderWidth='0px';
runtimeStyle.textAlign='center';
runtimeStyle.padding='3';
runtimeStyle.verticalAlign='bottom';
runtimeStyle.color='white';
runtimeStyle.cursor='hand';
runtimeStyle.background=ColorOut;
runtimeStyle.unselectable='on';
}

function DoMouseOver(){
runtimeStyle.background=ColorOver;
}

function DoMouseOut(){
runtimeStyle.background=ColorOut;
}

function DoMouseDown(){
runtimeStyle.background=ColorDown;
}

function DoMouseUp(){
runtimeStyle.background=ColorUp;
}

function DoClick(){
push.fire();
}

function showMessage(){
alert("showMessage run.");
}
</script>
</PUBLIC:COMPONENT>sample.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Sample</title>
</head><body bgcolor=#ffffff>
<button id=oButton style='behavior: url(ButtonStyleFlat.htc)'>push me please</button>
<script>
oButton.onpush=function(){
oButton.showMessage();
}
</script>
</body>6.分析
讓我們看看所完成的工作。我們把ButtonStyleFlat.htc和sample.html放在一起。我們打開(kāi)sample.html,發(fā)現(xiàn)button變平了,而且鼠標(biāo)經(jīng)過(guò),移開(kāi),按下,彈起動(dòng)作時(shí)顏色都會(huì)發(fā)生變化,而且仔細(xì)看腳本會(huì)發(fā)現(xiàn),我們可以handle一個(gè)onpush事件和調(diào)用showMessage()方法。
這一切的變化都來(lái)自style='behavior:url(ButtonStyleFlat.htc)', 一個(gè)behavior聲明。而不用在HTML中寫(xiě)任何腳本。我們不探討behavior的用法,僅僅講解如何開(kāi)發(fā)一個(gè)完整的HTC。
一個(gè)完整的HTC由兩個(gè)部分組成:我們把它們叫做API聲明和腳本實(shí)現(xiàn)。API聲明由以下部分組成:
a. PUBLIC:COMPONENT
這一部分組成了HTC的最外圍元素。僅僅定義了所包容的內(nèi)容是一個(gè)組件
b. PUBLIC:ATTACH
本部分定義了對(duì)于客戶事件的處理
c. PUBLIC:PROPERTY
公開(kāi)的屬性定義
d. PUBLIC:EVENT
公開(kāi)的事件定義
e. PUBLIC:METHOD
公開(kāi)的方法定義
由于本文僅僅是一個(gè)tutorial, 僅分析使用到的語(yǔ)法, 更多規(guī)范可以參考MSDN文檔:
ms-help://MS.MSDNQTR.2004JAN.1033/Behavior/workshop/components/htc/reference/htcref.htm
(地址可能需要修改)
或者聯(lián)機(jī)版本:
http://msdn.microsoft.com/workshop/components/htc/reference/htcref.asp?frame=true
(以下規(guī)范基于IE5.0及以上版本)
PUBLIC:ATTACH
表示綁定事件與處理過(guò)程
EVENT: 表示事件句柄名
ONEVENT: 表示處理過(guò)程名
PUBLIC:PROPERTY
表示公開(kāi)到環(huán)境的屬性
NAME: 屬性名
屬性可設(shè)置類似C# property的讀寫(xiě)器, 分別是get和put過(guò)程. 設(shè)置屬性之后, 可使用HTML語(yǔ)法指定組件的屬性值為任意值。
PUBLIC:METHOD
公開(kāi)到環(huán)境的方法
NAME: 方法名
PUBLIC:EVENT
可由環(huán)境catch的事件
NAME: 事件名
ID: 內(nèi)部引用名稱
腳本實(shí)現(xiàn)
API聲明僅定義了組件公開(kāi)到環(huán)境的編程接口, 在組件中需要使用腳本來(lái)實(shí)現(xiàn)內(nèi)部邏輯. 腳本實(shí)現(xiàn)主要有以下部分:
1. 定義事件處理過(guò)程
2. 定義PROPERTY的取設(shè)過(guò)程
3. 定義METHOD的具體實(shí)現(xiàn)
4. 定義EVENT的引發(fā)邏輯
5. 其他內(nèi)部過(guò)程
其中EVENT的引發(fā)一般在其他過(guò)程中進(jìn)行. 而腳本的語(yǔ)法與普通HTML頁(yè)上的腳本沒(méi)有什么不同.
7.實(shí)例講解
以上的Button Style Flat雖然很短小, 但可以基本說(shuō)明本文的中心內(nèi)容, 即HTC編程思想. 我們接著看上面提供的實(shí)例:
a. 在第一行我們注意到, 改實(shí)例將ondocumentready事件交給了一個(gè)OnInit()的腳本過(guò)程處理(ATTACH語(yǔ)法). Ondocumentready是component特有的事件. 表示當(dāng)表示component的前端HTML完全載入的時(shí)刻.可以說(shuō)ondocumentready事件是components初始化時(shí)的過(guò)程. 在我寫(xiě)的所有HTC中, 都ATTACH了這個(gè)事件. 這一習(xí)慣不知道從什么時(shí)候開(kāi)始的. 慢慢我發(fā)現(xiàn)不能離開(kāi)ondocumentready了. 只要我們的HTC中需要一個(gè)類似初始化的過(guò)程, 我們就需要指定ondocumentready時(shí)刻發(fā)生的過(guò)程. 在本實(shí)例中, 我們?cè)趏ndocumentready所綁定的過(guò)程中初始化了button的最初樣式. 即根據(jù)schema特性決定button的外觀.
b. 定義一組鼠標(biāo)事件. 一般而言, 我們的component都是可見(jiàn)的. 而HTML頁(yè)中與用戶交互的主要?jiǎng)幼魇鞘髽?biāo)的
動(dòng)作. 所以, 通常情況下, 我們總是會(huì)deal鼠標(biāo)的五個(gè)基本事件mouse over, mouseout, mouse down, mouse up 和click. 同樣是一個(gè)習(xí)慣, 我通常不加考慮的ATTACH 這五個(gè)事件. 即使綁定的過(guò)程是空的.
c. PROPERTY, 可以定義get和put過(guò)程做屬性的取設(shè)器. 一般情況下都可以省略這兩個(gè)過(guò)程. 除非要對(duì)設(shè)置的值進(jìn)行合法性校驗(yàn).
d. EVENT的引發(fā). PUBLIC:EVENT聲明的ID attribute用于script部分的內(nèi)部引用. 當(dāng)需要引發(fā)該事件時(shí), 僅需要使用類似: push.fire()命令就可以. 環(huán)境就是開(kāi)始準(zhǔn)備catch該事件. 相當(dāng)簡(jiǎn)單.
e. METHOD實(shí)現(xiàn). METHOD的name attribute直接代表<script>部分的函數(shù)名. 因此可以直接聲明一個(gè)同名的function. 可以有返回值, 也可以沒(méi)有返回值. 在本實(shí)例中我們僅僅發(fā)出了一個(gè)客戶端消息.
注意, 實(shí)例中的push事件和showMessage()方法都是沒(méi)有什么實(shí)際意義的. 放在實(shí)例中僅僅為了說(shuō)明編程方法.
8.總結(jié)
到這里為止, 我們可以總結(jié)一下簡(jiǎn)單模式下, 我們可以做的工作: 如何創(chuàng)建一個(gè)有效的HTC組件
a. ATTACH ondocumentready事件, 在過(guò)程中實(shí)現(xiàn)初始化時(shí)的步驟.
b. 分別ATTACH鼠標(biāo)的五個(gè)基本事件. 如果該組件設(shè)計(jì)了鍵盤(pán)事件, 也進(jìn)行同樣的綁定過(guò)程.
c. 如果組件設(shè)計(jì)了特定的客戶端事件, 僅需要定義并且在需要的時(shí)候引發(fā).
d. 特定的METHOD語(yǔ)法也很簡(jiǎn)單. 僅需要聲明一個(gè)METHOD, 并且在SCRTIPT部分實(shí)現(xiàn)同名函數(shù)即可.
e.考慮更復(fù)雜和實(shí)用的應(yīng)用
button實(shí)在是太簡(jiǎn)單和太不值得一提了. 我們來(lái)考慮一個(gè)很受歡迎的東西: treeview. 一個(gè)所有web開(kāi)發(fā)人員都非常熱愛(ài)的東西.
我們知道, 現(xiàn)在實(shí)現(xiàn)treeview的方法很多. 多美觀, 多實(shí)用的都有. 我們給自己提出需求, 來(lái)看一看用HTC如何設(shè)計(jì)一個(gè)好用而且節(jié)省成本的treeview.
需求
可以使用客戶端數(shù)據(jù)填充其內(nèi)容; 外觀與windows 資源管理器一致; 可以catch到expand/collipse事件; 可以catch到節(jié)點(diǎn)的click事件; 可以定義節(jié)點(diǎn)展開(kāi)/收縮的模式(記憶模式); 可以由接受環(huán)境指令expand/collipse指定的節(jié)點(diǎn).
分析
如果以上需求都可以實(shí)現(xiàn), 那么將是一個(gè)非常"高級(jí)"的treeview了. 我們逐一分析上述需求:
1. 使用客戶端數(shù)據(jù)填充: 既然是treeview, 則必然由節(jié)點(diǎn)構(gòu)成. 既然是節(jié)點(diǎn), 就必然體現(xiàn)一定的數(shù)據(jù). 而數(shù)據(jù)的由來(lái)一般情況下是由后端傳送來(lái)的. 這就要求我們最好使用一種數(shù)據(jù)格式. 不需要更改, 在后端和前端都可讀. 一般朋友都會(huì)想到用XML. 這是很好的想法. 這樣, 我們的treeview必須能夠按照一定的規(guī)則讀取XML數(shù)據(jù). 將節(jié)點(diǎn)解析出來(lái), 并且使用一定的輸出方法輸出目標(biāo)HTML形成帶有圖標(biāo), 文本, 節(jié)點(diǎn)線的外觀. 這樣過(guò)程一般在OnInit()過(guò)程中進(jìn)行.
2. expand/collipse事件. 有時(shí)候環(huán)境需要了解treeview的狀態(tài). 例如展開(kāi)某個(gè)節(jié)點(diǎn)是自動(dòng)顯示某些內(nèi)容. 因此環(huán)境必須隨時(shí)了解treeview里發(fā)生了什么. 這樣需要我們分別定義expand/collispse事件. 在某些情況下自動(dòng)地引發(fā)他們.
3. 節(jié)點(diǎn)的click事件很重要. 一般情況下, 用戶單擊某節(jié)點(diǎn)是總是會(huì)期望得到什么.
4. 設(shè)定展開(kāi)/收縮的模式. 我們可以指定treeview是否自己記住展開(kāi)的節(jié)點(diǎn)的狀態(tài). 而有些情況下我們希望treeview不會(huì)太長(zhǎng)而希望不準(zhǔn)同時(shí)展開(kāi)兩個(gè)節(jié)點(diǎn). 這需要我們定義一個(gè)PROPERTY. 可以通過(guò)HTML attribute或者script設(shè)定該值從而影響compenent的behavior.
5. 接受環(huán)境指定改變節(jié)點(diǎn)狀態(tài). 如果我們希望不經(jīng)過(guò)用戶操作而自動(dòng)打開(kāi)某節(jié)點(diǎn)(不經(jīng)過(guò)頁(yè)發(fā)回), 希望通過(guò)環(huán)境的script命令操作treeview. 我們可以定義一些列METHOD, 例如expandNode(id): 展開(kāi)指定id值的節(jié)點(diǎn).
這樣, 我們就開(kāi)發(fā)了一個(gè)有著高級(jí)特性的treeview component. 而且該組件的重用性是很高的. 我們只需要在HTML中插入一個(gè)特定的標(biāo)記, 類似<Timeline:Treeview ><xml data…. /></Timeline:Treeview>. 你的 HTML頁(yè)就會(huì)出現(xiàn)一個(gè)非常漂亮的樹(shù)型目錄了.
結(jié)束
本文論述了開(kāi)發(fā)HTC的一般性方法. 作者希望可以通過(guò)本文, 使廣大web工作者認(rèn)識(shí)到HTC的優(yōu)勢(shì).
注:HTC有很好的思想,但由于其基于IE,不穩(wěn)定。代碼復(fù)雜之后,會(huì)出現(xiàn)很多莫名其妙的BUG。
例如,MS IE WebControls中的TreeView,如果每個(gè)節(jié)點(diǎn)都帶一個(gè)小圖標(biāo),節(jié)點(diǎn)有上百個(gè)或者數(shù)百個(gè),此時(shí),就有可能出現(xiàn)莫名其妙的問(wèn)題。為此事詢問(wèn)Microsoft,得到的答復(fù)是,HTC中的圖片太多,導(dǎo)致過(guò)多圖片請(qǐng)求,需要修改IE在注冊(cè)表中的配置解決。Microsoft并且認(rèn)為不是BUG,而認(rèn)為只是一個(gè)需求缺陷。
一:我們使用復(fù)雜的客戶端技術(shù)處理一些邏輯,一定程度上就是為了降低網(wǎng)絡(luò)訪問(wèn)頻率。當(dāng)帶寬不是問(wèn)題的時(shí)候,復(fù)雜邏輯由服務(wù)端處理其實(shí)從整體上來(lái)看是比較合理的(安全性,可擴(kuò)展性等)。因?yàn)楫吘笽E的Script runtime能力很有限。
二:ASP和ASP.NET在我看來(lái)主要區(qū)別中的一點(diǎn)就是:
ASP基于HTTP的擴(kuò)展與封裝做的很弱,他存在的價(jià)值就是COM的黏合劑。在MS平臺(tái)下使用ASP+COM/COM+完成靈活、交互復(fù)雜的系統(tǒng)還是很吃力的事情,當(dāng)你的客戶端請(qǐng)求不想只局限于FORM的get,post的時(shí)候,借助HTC+Microsoft.XMLDOM/XMLHTTP就可以實(shí)現(xiàn)一些相對(duì)傳統(tǒng)ASP技術(shù)實(shí)現(xiàn)起來(lái)比較困難的功能,比如Http異步請(qǐng)求,頁(yè)面的局部刷新等等。所以HTC+Microsoft.XMLDOM/XMLHTTP對(duì)我來(lái)說(shuō)的確在很大程度上彌補(bǔ)了ASP的不足。
ASP.NET將http過(guò)程做了很不錯(cuò)的封裝,ViewState幫助我們完成信息從服務(wù)端到客戶端的交互(不過(guò)也有若干缺陷),我們幾乎不需要在客戶端考慮Post的問(wèn)題了。所以當(dāng)ASP.NET使Client和Server之間的界限變得不是那么硬的時(shí)候。Client的處理能力仿佛加強(qiáng)了。
所以隨著ASP.NET出現(xiàn),我沒(méi)有再像以前那樣把基于IE瀏覽器的客戶端技術(shù)看得過(guò)重。
HTC標(biāo)準(zhǔn)由微軟提出,運(yùn)行在IE特定環(huán)境之下,微軟的支持與否對(duì)HTC的健康發(fā)展至關(guān)重要。
HTC是一個(gè)不錯(cuò)的基于IE的組建模型技術(shù),思想也很不錯(cuò)。但是如果嘗試使用HTC去構(gòu)建基于IE的Rich Client體系的話,存在著一定的風(fēng)險(xiǎn)。HTC做一些相對(duì)輕型的可重用組件還是不錯(cuò)的。所以我們討論的只是HTC定位的問(wèn)題,而非此種技術(shù)的好壞。
另:我從來(lái)沒(méi)有使用HTC訪問(wèn)數(shù)據(jù)庫(kù),也從來(lái)沒(méi)有在IE端直接訪問(wèn)數(shù)據(jù)庫(kù)的經(jīng)驗(yàn)和經(jīng)歷。我只是利用HTC封裝的組件處理Microsoft.XMLDOM從服務(wù)端Load的XmlStream,利用數(shù)據(jù)綁定做顯示,再通過(guò)Microsfot.XMLHTTP提交到服務(wù)端來(lái)完成最常見(jiàn)的數(shù)據(jù)CIUD工作。
我感覺(jué)隨著微軟下一代操作系統(tǒng)Longhorn對(duì)Internet支持的逐漸強(qiáng)大,Avalon做為新一代界面框架將成使資源展現(xiàn),搜索時(shí)所處的位置是本機(jī)還是網(wǎng)絡(luò)有所弱化。我沒(méi)見(jiàn)過(guò)Longhorn是什么樣子,但我感覺(jué)到時(shí)候我們?cè)L問(wèn)internel上的資源,不管html,圖像,媒體??梢酝ㄟ^(guò)更多途徑。IE+DHtml的展現(xiàn)方式不會(huì)被取代,但更強(qiáng)大的Xaml將成為Microsoft的主要方向。微軟對(duì)DHTML整個(gè)體系的支持都在減弱。
經(jīng)驗(yàn)是,不建議基于Web開(kāi)發(fā)過(guò)于復(fù)雜的UI界面。因?yàn)镮E這個(gè)開(kāi)發(fā)環(huán)境不夠可靠,而且微軟也不打算改進(jìn)它。