EasyStruct.js輕鬆創建可填入式html模板結構

友情提醒:由於舊版本的EasyStruct有一些bug,現在已經全部修復,而且增加了一份詳細的demo,如果想要使用的話可以直接下載demo去看了,下面這篇介紹可以跳過了。

友情鏈接:http://download.csdn.net/detail/sinolzeng/8578461




在前端開發的工作中,經常會碰到這樣的情況,加載頁面數據的時候,有一部分內容的結構是重複的,只是數據不一樣。比如說論壇、貼吧裏面的各個樓層,還有一些類似工資報表、銷售報表的每一行,舉個例子:


function addTr(data1,data2,data3){

    return '<tr style="text-align:center"><td style="height:40px;">'+data1+'<td><td style="height:40px;">'+data2+'<td><td style="height:40px;">'+data3+'<td></tr>';

}

類似這樣的結構體,在前端開發中很常見。我們通常會創建一個這樣的東西之後,放到一個for或者each類型的循環中用於加載數據。但是,這種結構的寫法看起來多少有些不便。而且要改動也不是很方便。所以,爲了工作上的便捷,我開發了一個創建結構的純js插件。用戶可以輕鬆創建結構體了。該插件經過壓縮後只有4kb的大小,絕對不會佔用你的網頁空間!而且不依賴於jquery或其他框架,可以獨立使用。


好了,下面說一下這款插件如何使用,非常簡單,舉個例子,只需要寫:


EasyStruct("#id").struct("table")(); 就可以在指定的id下方創建一個table了。我在EasyStruct中實現了一個簡單的選擇器,可以用#id選擇id,用.class獲取class的第一個,用tag獲取tag的第一個。由於不希望該選擇器太過複雜佔用js插件空間,所以只實現了以上三個選擇,不過我相信已經完全夠用了。畢竟這個插件的作用僅僅是用來創建結構而已。

肯定會有人覺得,用EasyStruct這一串字符來寫非常麻煩,嗯,沒錯,我也覺得很麻煩,所以我寫了一句:var es = EasyStruct;

此時上面的那一句就等價於 es("#id").struct("table")();

如果用戶不喜歡es的名字呢,很簡單,只需要這樣: var 你設定的名字 = EasyStruct; 或者 var 你設定的名字 = es;    這樣就可以隨便你使用了。

細心的朋友可能會注意到,我剛剛寫的es("#id").struct("table")(); 後面還有一個(),嗯,沒錯,這個絕對不是我寫錯了或者寫多了,而是本來如此。爲什麼呢,因爲前面的es("#id").struct("table")返回的是一個function,而不是直接執行。不採用直接執行是爲了我們可以填入參數嘛。

下面再看第二個例子:

es("#id").struct("table|style[background-color]|")("red");

這一次,在“table”的字樣後面,多出了"|style[background-color]|"的字樣,而後面的花括號裏填入了“red”,很簡單,就是我們在這個結構中定義了一個設置這個table的background-color的接口,而後面的“red”就是用戶自己填入的值,這樣,我們就可以將table的背景色定義爲紅色。

當然,上面的寫法也可以寫成:

var table = es("#id").struct("table|style[background-color]|");

table("red");

這樣看起來是不是就清晰很多了呢?沒錯,EasyStruct的作用就是用來創建這樣的結構的,只要我們將結構賦值給一個變量,就可以在任何地方隨意地調用該結構了,放在for循環裏面自然就不是什麼問題了。當然了,很多時候,我們需要填入的可不止一個變量是不是?看下面:

var table = es("#id").struct("table|style[background-color color]|");

table("red","yellow");

很簡單,直接寫進去就可以了!這樣的話呢,我們就把字體顏色給設置成了黃色了!

var table = es("#id").struct("table|style[color background-color]|");

table("red","yellow");

當然,如果上面的background-color和color的位置倒換過來的話,就會變成紅色字體,黃色背景了。變量和出現的順序是一一對應的。如果你填入的變量過多,比如在後面多了一個“blue”,是沒有任何效果的。如果少了,比如說沒有“yellow”,就相當於沒有添加背景色而已,也不會出現什麼bug的。

我的設定是在兩個豎槓中間可以填入一定的值來表示需要傳入的參數內容。這些值需要用空格隔開。

可以傳入的值包括:“id”  “class”  “html”  “style”

“id”和“class”沒什麼好說的,“html”就是文本內容,“style”就是平時整個style的內容。舉個例子:

var div = es("#id").struct("div|id class html style|");

div("aaa","bbb","哈哈","display:block;color:red;width:100px;height:50px");

等價於jquery的寫法:

function div(id,class,html,style){

        $("#id").append('<div id=“'+id+'” class="'+class+'" style="'+style+'">'+html+'</div>');

};

div("aaa","bbb","哈哈","display:block;color:red;width:100px;height:50px");

另外,如果有單個或多個的attr或者style的話,也可以以花括號的形式填充進去,舉個例子:

var div = es("#id").struct("div|style=[background-color color] attr[onclick onmouseover]|");

div("red","yellow","alert(1)","this.color=\'green\'");

相當於創建了一個這樣的東西:<div style="background-color:red; color:yellow" οnclick="alert(1)" οnmοuseοver="this.color=\'green\'"></div>

相信上面的例子已經說得很清楚了吧。接下來可能有人就會說,我需要填寫的內容並不是全部啊,有些東西是固定的,比如table的id,我就希望它是固定的,而不需要自己填進去。這個當然是可以的。舉例:

var table = es("#id").struct("table(#aaa .bbb $background-color:red;color:yellow; @οnclick=alert(1) @οnmοuseοver=alert(2))");

table();

此時相當於:<table id="aaa" class="bbb" style="background-color:red; color:yellow" οnclick="alert(1)" οnmοuseοver="alert(2)"></table>

語法方面很簡單,將需要直接填入而不是以參數形式賦值的內容放在一個小括號裏面。如果是id的話,就寫#id,class就寫.class,style就直接在前面加一個$,attr就加一個@,中間加一個=符號,這樣就可以將這些內容直接寫進去了。小括號的內容和豎槓的內容是可以同時存在的,並且他們誰先睡後都可以,不影響執行。

比如: var div = es.struct("div($color:red;)|id class attr[onclick]|");

div("aaa","bbb","alert(1)");

相當於:<div style="color:red" id="aaa" class="bbb" οnclick="alert(1)"></div>

這樣一來,無論是寫成固定的,還是參數形式的,都沒有任何問題了,是不是挺方便的呢?

當然,解決了上面的問題之後,還是沒辦法做實際應用的,因爲我們平時所寫的結構體不可能只有一個dom元素,舉個最最簡單的例子,我們用上面的方法創建了一個tr結構,然後放到一個for循環中執行了10次,這樣確實可以在一個table裏面插入10個tr,但是這10個tr裏面可是連一個td都沒有的。完全不能用呢。別急,現在我們就來解決這個問題:


對應EasyStruct來說,每一次創建結構可以傳入的參數是無限制個數的,而且後面的參數會變成前面參數的子參數,舉例:

es("#id").struct("div","span");

這樣的寫法相當於在指定的id下方創建了一個div,然後又在div下方創建了一個span。至於想對這個span和div進行各種屬性處理的,這裏就不贅述了,和上面的寫法是一樣的。如果是用豎槓直接填入參數的話,那麼執行的順序也是從左到右,如果div裏面有豎槓,就先執行,再判斷span裏面有沒有豎槓,再執行,以此類推。

但是這裏就有一個問題了,比如說,我想在這個div下面放兩個span,如果我這樣寫呢:

es("#id").struct("div","span","span");

很明顯是錯的,這樣寫就相當於在這個div裏面放span,在這個span裏面又放了一個span,是層級關係,而不是同級關係。

於是我想了一個解決方法,可以改成這樣:

es("#id").struct("div",["span","span"]);

現在兩個span被放在同一個數組之中,而這個數組作爲第二個參數,那麼他們就會並列地成爲上一個參數div的子參數了。

聽起來好像已經解決問題了,是吧?其實沒有的,因爲就實際工作而言,我們需要的結構體有時候複雜得很,不可能像我舉的例子這麼簡單。我舉的例子只是爲了方便讀者理解而已。

那麼我們再看看另一種情況,我先以html的形式寫出來:

<table>

  <tr>

  <td></td><td></td><td></td>

  </tr>

  <tr>

  <td></td><td></td><td></td>

  </tr>

</table>

這個結構就是,一個table下面有兩個tr,而這兩個tr下面分別有三個td。對於table來說,這樣的結構實在是太簡單太常見了。但是對於我們的EasyStruct來說,就會出問題了。且看:

es("#id").struct("table",["tr","tr"],   ["td","td","td","td","td","td"]);

如果我們以上面的寫法來寫的話,出現的結構就是第一個tr下面有六個td,但是第二個tr下面什麼都沒有!如果我們寫第四個參數的話,它是變成第一個tr下面的第一個td的子參數,那完全不是我們想要的結果……

於是肯定有人會說,那我們是不是可以把結構改成這樣:

es("#id").struct("table",[ "tr" , ["td","td","td"] , "tr", ["td","td","td"] ]);

理論上講,要通過函數來實現上面的嵌套式設計雖然有一定難度,但並不是什麼問題。但是這樣的結構讀起來並不直觀,也不好理解。所以筆者最後放棄了這樣的設計模式,而是改成了下面的形式:

es("#id").struct("table",["tr","tr"],["(0)td","(0)td","(0)td","(1)td","(1)td","(1)td"]);

採用這樣的寫法看起來就變得很直觀了,前三個td的前面多了一個0,也就是指定它們會放在上一層的第一個對象的下方。(由於index通常都是以0代表第一個的,所以筆者在這裏也遵循了數組的常用習慣),而後面的三個很明顯地就會被放在第二個tr的下方了。

這樣一來,寫起來雖然稍微長了一點,但卻可以很直觀地解決這個問題了!(在我設計過程中擬定的五六種佈局模式中,這種是筆者認爲最簡單最好理解的,所以筆者最終選用了這樣的佈局方式,如果有更好的佈局方式,歡迎留言指教,感激不盡!)


採用了這種模式之後,es.struct()裏面的每一個參數就代表一個樹狀結構的層。第一個參數是第一層,第二個參數是第二層,第三個參數是第三層……以此類推,每一層裏面如果有多個dom對象,就將該層變成一個數組,在沒有特別指定的情況下,每一個參數都會成爲上一層的第一個dom對象的子對象。而經過特別指定之後,就會成爲指定對象的子對象了。

舉個例子,我們上面的例子用了三層,假設現在我想在第一個tr下面的第一個td下面放一個div,想在第二個tr的第二個td下面放一個span,那麼就這樣寫:

es("#id").struct("table",["tr","tr"],["(0)td","(0)td","(0)td","(1)td","(1)td","(1)td"],["(0)div","(4)span"]);

我們來看看第四層,裏面的div前面的序列號是0,也就是它會被放在上一層的第一個裏面,而上一層的第一個又會追朔到上上一層的第一個tr……(其實在寫的時候我們只需要用線性思維就可以了,並不需要一層層地往上考慮,這裏只是爲了寫得明白一些方便讀者理解而已。)再看看span,它的index是4,所以它會被放在上一層的第五個下面。而上一層的第五個td就是第二個tr對象的第二個子對象。這樣一來,我們就實現了這樣的結構:

<table>

    <tr>

          <td>

                <div></div>

          </td>

          <td>         

          </td>

          <td>         

          </td>

    </tr>

    <tr> 

          <td>

          </td>

          <td>   

                <span></span>     

          </td>

          <td>         

          </td>

    </tr>

</table>

現在,採用分層和決定順序的方式,無論多複雜的dom樹結構都可以排列出來了。採用EasyStruct也不會有任何問題了。現在,不管多複雜的結構我們都可以寫出來了。

至於有人說的["(0)td","(0)td","(0)td","(1)td","(1)td","(1)td"]這樣的寫法看起來很囉嗦,是不是可以簡化一下,筆者之前也有考慮過這個問題。雖然有一些可行的解決方法,但是簡化之後不便於代碼的閱讀。畢竟,我們的設計還需要考慮可讀性的問題。讀者也不希望你寫的代碼再過一兩個月連自己都看不懂吧?

如果真要解決的話,讀者自己寫一個for循環的方法來創建這個數組,配合着EasyStruct使用就行了。舉例:

function tr(){

    var arr = [];

    for(var i=0;i<3;i++){arr.push("(0)td")}

     for(var j=0;j<3j++){arr.push("(1)td")}

    return arr;

}

es("#id").struct( "table", ["tr","tr"], tr(), ["(0)div","(4)span"] );

=_=但是我怎麼覺得這樣寫更不簡便。。。。。

哈哈,對於冗長的tr結構來說還是可能就有必要,一般不要這樣寫。

而且需要注意,我們創建的是結構啊!結構啊!不是要你一次性把全部內容創建出來。因爲EasyStruct裏用到了不少for循環和正則表達式,如果被讀者多次執行的話,效率是很低的,也沒有這個必要。通常我們只需要創建一到兩個結構,然後重複使用這一兩個結構就夠了。對於頁面中的靜態內容部分,雖然可以使用EasyStruct,但執行效率並不高,所以筆者是不推薦大家這樣寫的。

後記:開發這個插件還有一個原因,那就是筆者曾經在設想能不能用js完全代替html,讓看起來相當雜亂的html結構徹底滾蛋呢?實驗了一段時間之後,筆者發現其實是可以的,的確可以寫出完全沒有半點html的頁面。如果是一次設計,以後都不修改的話,那麼這樣寫也倒是沒什麼問題。但如果你需要經常修改你的頁面結構呢?純js的寫法創建的頁面肯定會讓你修改起來蛋碎一地……哈哈。

好了,這次的EasyStruct就講到這裏。我覺得js的研究過程本身是最有意思,最有意義的。如果真的想學好前端開發的話,就不要浸泡在jquery的溫柔鄉里享受,有些問題,比如瀏覽器之間的兼容性問題,還是要自己思考思考的,有條件的話也要自己把jquery給實現一遍。筆者最近差不多搞定屬於自己的jquery了,我取名Joy.js。過幾天我會把整個設計過程寫出來放到博客上和大家一起分享。

 代碼地址:http://download.csdn.net/detail/sinolzeng/8465619



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章