【HTML5&CSS3進階03】Jser與Csser如何一塊兒愉快的翻新老組件

上次,咱們造成了兩種header的佈局,一種flexbox,一種float,最後與身邊作重構的同事交流下來,選擇了float的佈局。javascript

事實上佈局的選型不須要我關注,個人參與或者一些意見多數是自我提高,但要說html結構徹底控制於csser的話就不必定了css

在整個header組件的代碼過程當中,我與重構同事就一些地方發生了重複的交流,爭論,今天就header組件的佈局以及功能實現,聊一聊js與css的配合html

而後header組件自己是一個老組件,咱們順便探討下,這類老組件應該如何翻新比較合適。html5

最初的結構

最開始重構的同事給了我一個已經作好了的頁面:java

咱們針對其中一些小的體驗上作了討論,而且知會到設計組,便改了,很順暢,而後我開始了愉快的代碼,這是其中一塊HTML的結構:git

1 <header class="cm-header" style="top: 50px;">
2   <span class="fl cm-header-icon icon-back "></span>
3   <span class="fr cm-header-btn">確認</span>
4   <span class="fr cm-header-icon"><i class="icon-home"></i></span>
5   <span class="fr cm-header-icon"><i class="icond-list"></i></span>
6   <h1 class="cm-page-title">
7     頁面標題</h1>
8 </header>

這裏除去h1標籤中的文字不說,由於其中可能表現的很是複雜,咱們後面再說,其中的按鈕有如下功能:程序員

① 第二行:回退按鈕github

② 第三行:確認json

PS:左邊採用float佈局因此第一個元素在最右邊數據結構

③ 第四行:home標籤

④ 第五行:三個點,點擊會出一個側邊欄

以上即是HTML的實現,可是對與程序員來講,頭部除了按鈕(btn)之外就只有圖標(icon),因此以上的結構事實上js通常是不買帳的

Jser須要的結構

與重構同事交流下來,緣由是這樣的:

① 由於回退比較特殊,因此多了一個樣式,具體什麼我沒記住了

② icon表明背景圖,icond表明CSS3畫的,CSS3畫的可擴展性高,好比換顏色什麼的

③ ......

當時雙方的討論仍是比較激烈的,可是對icond所有變成icon,重構同事不一樣意,因而也就做罷,通過一輪討論,結構變成了這樣:

1 <header class="cm-header" style="top: 50px;">
2   <span class="fl cm-header-icon"><i class="icon-back"></i></span>
3   <span class="fr cm-header-btn">確認</span>
4   <span class="fr cm-header-icon"><i class="icon-home"></i></span>
5   <span class="fr cm-header-icon"><i class="icond-list"></i></span>
6   <h1 class="cm-page-title">
7     頁面標題</h1>
8 </header>

作了很小的變化,將back的結構與其它icon類型按鈕作了統一,因而我開始了愉快的代碼

PS:注意,icond與icon類型的標籤會不一樣程度的在header處出現,沒法控制

結構的問題

由於公司的header一直便存在,我作的過程當中必須考慮到兩個方面的問題:

① 方便擴展可是要作到接口兼容

② 須要經過各個標籤的tagname與Hybrid進行聯調

也就是說,每一個標籤叫什麼名字,是已經定死了的,甚至一些標籤的回調也被限制了,我這裏的數據結構大概以下:

 1 {
 2 left: [],
 3 center: [],
 4 right: [
 5   {
 6     'tagname': 'home', callback: function () {
 7       console.log('返回');
 8     }
 9   },
10   { 'tagname': 'search' },
11   {
12     'tagname': 'list', callback: function (e) {
13        //......
14     }
15   },
16   { 'tagname': 'tel', 'number': '56973144' },
17   {
18     'tagname': 'commit', 'value': '登陸', callback: function () {
19       console.log('登陸');
20     }
21   },
22   {
23     'tagname': 'custom', 'value': '定製化',
24     itemFn: function () {
25       return '<span class="cm-header-btn fr js_custom">定製化</span>';
26     },
27     callback: function () {
28       console.log('定製化');
29     }
30   }
31 ]

能夠看到,一個tagname一個按鈕,而如今問題來了:咱們並不知道某個tagname應該是icon或者是icond

可是根據是否存在value字段,咱們是能夠判斷其是否應該具備i子標籤,這個時候咱們是怎麼解決的呢?

創建tagname與classname的映射關係,好比:

1 var map = {
2   'home': 'icon',
3   'list': 'icond'
4 }

固然,這種作法,天然十分讓人感到難受,若是小圖標統一爲icon,我在模板中能夠統一如此代碼:

1 <span class="cm-header-icon <%=dir %>  js_<%=item.tagname %>" >
2   <% if(item.value) { %>
3     <%=item.value %>
4   <% } else { %>
5     <i class="icon-<%=item.tagname %>"></i>
6   <% } %>
7 </span>

可是因爲多了一個映射關係,個人代碼便很差看了,而且業務邏輯還變得複雜了起來,因而帶着這些考量再次找到了重構同事,重構同事也很明事理,立刻答應改了:

1 <header class="cm-header" style="top: 50px;">
2   <span class="fl cm-header-icon"><i class="icon-back"></i></span>
3   <span class="fr cm-header-btn">確認</span>
4   <span class="fr cm-header-icon"><i class="icon-home"></i></span>
5   <span class="fr cm-header-icon"><i class="icon-list"></i></span>
6   <h1 class="cm-page-title">
7     頁面標題</h1>
8 </header>

不考慮h1中的樣式的話,搞定上面的代碼,對咱們來講,真的是太簡單了啊!!!

 1 <header class="cm-header">
 2 <%
 3 var i = 0, len = 0, j = 0, keyagain = 0;
 4 var left = left;
 5 var right =  right.reverse();
 6 var item = null;
 7 var dir;
 8 var btnObj = null;
 9 %>
10 <%for(keyagain=0; keyagain < 2; keyagain++) { %>
11   <% 
12     if(keyagain == 0) { dir = 'fl'; btnObj = left; } else { dir = 'fr'; btnObj = right; }
13   %>
14   <% for(i = 0, len = btnObj.length; i < len; i++) { %>
15     <% item = btnObj[i]; %>
16     <%if(typeof item.itemFn == 'function') { %>
17       <%=item.itemFn() %>
18     <%} else { %>
19       <span class="cm-header-icon <%=dir %>  js_<%=item.tagname %>" >
20         <% if(item.value) { %>
21           <%=item.value %>
22         <% } else { %>
23           <i class="icon-<%=item.tagname %>"></i>
24         <% } %>
25       </span>
26     <%} %>
27   <%} %>
28 <%} %>
29 </header>

PS:從代碼着色來看,js中用到的left與Right是關鍵字,這個得處理...

定製化需求

能夠看到,一個循環,咱們即可以輕易的生成左邊和右邊的按鈕,可是立刻問題來了,咱們須要擴展怎麼辦,上面就會有如下問題:

① tel標籤默認是a標籤,咱們這裏倒是span標籤

1 <a href="tel:56973144" class="cm-header-btn fr js_tel "><i class="icon-tel"></i></a>

② back按鈕咱們通常會作成a標籤,用以解決javascript出錯在Hybrid的假死問題

說白了,就是雖然標籤按鈕應該有統一的結構,可是須要保留定製化的能力

這裏定製化的工做交給了各個標籤的itemFn這個函數,他返回一個字符串,而且具備必定規則,這裏取一個代碼片斷:

 1 handleSpecialParam: function (data) {
 2   var k, i, len, item;
 3   for (k in data) {
 4     if (_.isArray(data[k])) {
 5       for (i = 0, len = data[k].length; i < len; i++) {
 6         item = data[k][i];
 7         if (this['customtHandle_' + item.tagname]) {
 8           this['customtHandle_' + item.tagname](data[k][i], k);
 9         } //if
10       } //for
11     } //if
12   } //for
13 },
14 
15 _getDir: function (dir) {
16   var kv = { left: 'fl', right: 'fr' };
17   return kv[dir];
18 },
19 
20 //處理back的按鈕邏輯
21 customtHandle_back: function (item, dir) {
22   dir = this._getDir(dir);
23   item.itemFn = function () {
24     var str = '<a href="http://m.ctrip.com/html5/" class="cm-header-btn ' + dir + ' js_' + item.tagname + ' " >';
25     if (item.value) {
26       str += item.value + '</a>';
27     } else {
28       str += '<i class="icon-' + item.tagname + '"></i></a>';
29     }
30     return str;
31   };
32 },

當發現某個按鈕不知足需求或者有定製化需求時,便想辦法設置其itemFn便可,時候上這個代碼能夠直接寫到初始化的json串去

花樣百出的title

到title時,發現其表現便五花八門了,這個時候通常是根據不一樣的類型生成不一樣的HTML結構,框架給默認的幾個選項,不支持便本身定製itemFn

 1 <% item = center; %>
 2 <%if(typeof item.itemFn == 'function') { %>
 3   <%=item.itemFn() %>
 4 <%} else if(item.tagname=='title' ||  item.tagname=='subtitle') { %>
 5   <h1 class="cm-page-title js_<%=item.tagname %>" >
 6     <%if(typeof(item.value) == 'object' && item.title.value == 2) { %>
 7       <span class="cm-title-l"><%=item.value[0]%></span>
 8       <span class="cm-title-s"><%=item.value[1]%></span>
 9     <%} else { %>
10       <%=item.value %>
11     <%} %>
12   </h1>
13 <%} else if(item.tagname=='select'){ %>
14   <h1 class="cm-page-select-title js_<%=item.tagname %>" >
15     <%=item.value %>
16   </h1>
17 <%} else if(item.tagname=='tabs') { %>
18   <h1 class="cm-page-tabs-title js_<%=item.tagname %>" >
19     <%for(j = 0; j < item.data.items.length; j ++) { %>
20       <span data-key="<%=item.data.items[j].id %>" class="<%if(item.data.index===j){ %>active<%} %>" ><%=item.data.items[j].name %></span>
21     <% } %>
22   </h1>
23 <% } else{ %>
24 
25 <%} %>

事件綁定的實現

header組件自己繼承至Abstract.View這個類,因此只要設置

this.events = {}

便能以事件代理的方式將事件綁定至根元素,而header的事件通常就是click事件:

 1 setEventsParam: function () {
 2   var item, data = this.datamodel.left.concat(this.datamodel.right).concat(this.datamodel.center);
 3 
 4   for (var i = 0, len = data.length; i < len; i++) {
 5     item = data[i];
 6     if (_.isFunction(item.callback)) {
 7       this.events['click .js_' + item.tagname] = $.proxy(item.callback, this.viewScope);
 8     }
 9   }
10 },

這裏有一個須要注意的點即是,事件綁定的鉤子即是咱們的tagname,這個是惟一的,咱們會爲每一個標籤動態生成「.js_tagname」的類,以方便事件綁定

老接口的兼容

以前便說了,該組件是一個老組件的翻新,因而各個業務團隊已經使用了,好比原來是這樣調用的:

 1 this.header.set({
 2   title: '基本Header使用',
 3   subtitle: '中間副標題',
 4   back: true,
 5   backtext: '取消',
 6   tel: { number: 1111 },
 7   home: true,
 8   search: true,
 9   btn: { title: "登陸", id: 'confirmBtn', classname: 'header_r' },
10   events: {
11     returnHandler: function () {
12       console.log('back');
13     },
14     homeHandler: function (e) {
15     }
16   }
17 });

而如今咱們指望的調用方式是這樣的:

1 this.header.set({
2   left: [],
3   center: {},
4   right: [
5     { tagname: 'home', callback: function () { } },
6     { tagname: 'tagname', value: 'value', data: {}, itemFn: function(){}, callback: function () { } }
7   ]
8 });

這個時候咱們應該怎麼作呢?固然是不破不立,先破後立,固然是要求業務團隊改!!!而後被無情的噴了回來,因而作了接口兼容

翻新老組件,接口兼容是必須的,若是不是底層機制發生顛覆,而顛覆能夠帶來顛覆性的成績,接口仍是不建議改!

這裏上面即是新接口的調用,下面是老接口的調用,效果以下:

代碼&demo

源碼:https://github.com/yexiaochai/cssui/tree/gh-pages

demo:http://yexiaochai.github.io/cssui/demo/debug.html#header

反饋:若是文中有何不足,請您留言

相關文章
相關標籤/搜索