上次,我们形成了两种header的布局,一种flexbox,一种float,最后与身边做重构的同事交流下来,选择了float的布局。

事实上布局的选型不需要我关注,我的参与或者一些意见多数是自我提升,但要说html结构完全控制于csser的话就不一定了

在整个header组件的代码过程中,我与重构同事就一些地方发生了重复的交流,争论,今天就header组件的布局以及功能实现,聊一聊js与css的配合

然后header组件本身是一个老组件,我们顺便探讨下,这类老组件应该如何翻新比较合适。

最初的结构

最开始重构的同事给了我一个已经做好了的页面:

我们针对其中一些小的体验上做了讨论,并且知会到设计组,便改了,很顺畅,然后我开始了愉快的代码,这是其中一块HTML的结构:

 <header class="cm-header" style="top: 50px;">
<span class="fl cm-header-icon icon-back "></span>
<span class="fr cm-header-btn">确认</span>
<span class="fr cm-header-icon"><i class="icon-home"></i></span>
<span class="fr cm-header-icon"><i class="icond-list"></i></span>
<h1 class="cm-page-title">
页面标题</h1>
</header>

这里除去h1标签中的文字不说,因为其中可能表现的非常复杂,我们后面再说,其中的按钮有以下功能:

① 第二行:回退按钮

② 第三行:确认

PS:左边采用float布局所以第一个元素在最右边

③ 第四行:home标签

④ 第五行:三个点,点击会出一个侧边栏

以上便是HTML的实现,但是对与程序员来说,头部除了按钮(btn)以外就只有图标(icon),所以以上的结构事实上js一般是不买账的

Jser需要的结构

与重构同事交流下来,原因是这样的:

① 因为回退比较特殊,所以多了一个样式,具体什么我没记住了

② icon代表背景图,icond代表CSS3画的,CSS3画的可扩展性高,比如换颜色什么的

③ ......

当时双方的讨论还是比较激烈的,但是对icond全部变成icon,重构同事不同意,于是也就作罢,经过一轮讨论,结构变成了这样:

 <header class="cm-header" style="top: 50px;">
<span class="fl cm-header-icon"><i class="icon-back"></i></span>
<span class="fr cm-header-btn">确认</span>
<span class="fr cm-header-icon"><i class="icon-home"></i></span>
<span class="fr cm-header-icon"><i class="icond-list"></i></span>
<h1 class="cm-page-title">
页面标题</h1>
</header>

做了很小的变化,将back的结构与其它icon类型按钮做了统一,于是我开始了愉快的代码

PS:注意,icond与icon类型的标签会不同程度的在header处出现,无法控制

结构的问题

因为公司的header一直便存在,我做的过程中必须考虑到两个方面的问题:

① 方便扩展但是要做到接口兼容

② 需要通过各个标签的tagname与Hybrid进行联调

也就是说,每个标签叫什么名字,是已经定死了的,甚至一些标签的回调也被限制了,我这里的数据结构大概如下:

 {
left: [],
center: [],
right: [
{
'tagname': 'home', callback: function () {
console.log('返回');
}
},
{ 'tagname': 'search' },
{
'tagname': 'list', callback: function (e) {
//......
}
},
{ 'tagname': 'tel', 'number': '56973144' },
{
'tagname': 'commit', 'value': '登录', callback: function () {
console.log('登录');
}
},
{
'tagname': 'custom', 'value': '定制化',
itemFn: function () {
return '<span class="cm-header-btn fr js_custom">定制化</span>';
},
callback: function () {
console.log('定制化');
}
}
]

可以看到,一个tagname一个按钮,而现在问题来了:我们并不知道某个tagname应该是icon或者是icond

但是根据是否存在value字段,我们是可以判断其是否应该具有i子标签,这个时候我们是怎么解决的呢?

建立tagname与classname的映射关系,比如:

 var map = {
'home': 'icon',
'list': 'icond'
}

当然,这种做法,自然十分让人感到难受,如果小图标统一为icon,我在模板中可以统一如此代码:

 <span class="cm-header-icon <%=dir %>  js_<%=item.tagname %>" >
<% if(item.value) { %>
<%=item.value %>
<% } else { %>
<i class="icon-<%=item.tagname %>"></i>
<% } %>
</span>

但是由于多了一个映射关系,我的代码便不好看了,并且业务逻辑还变得复杂了起来,于是带着这些考量再次找到了重构同事,重构同事也很明事理,马上答应改了:

 <header class="cm-header" style="top: 50px;">
<span class="fl cm-header-icon"><i class="icon-back"></i></span>
<span class="fr cm-header-btn">确认</span>
<span class="fr cm-header-icon"><i class="icon-home"></i></span>
<span class="fr cm-header-icon"><i class="icon-list"></i></span>
<h1 class="cm-page-title">
页面标题</h1>
</header>

不考虑h1中的样式的话,搞定上面的代码,对我们来说,真的是太简单了啊!!!

 <header class="cm-header">
<%
var i = 0, len = 0, j = 0, keyagain = 0;
var left = left;
var right = right.reverse();
var item = null;
var dir;
var btnObj = null;
%>
<%for(keyagain=0; keyagain < 2; keyagain++) { %>
<%
if(keyagain == 0) { dir = 'fl'; btnObj = left; } else { dir = 'fr'; btnObj = right; }
%>
<% for(i = 0, len = btnObj.length; i < len; i++) { %>
<% item = btnObj[i]; %>
<%if(typeof item.itemFn == 'function') { %>
<%=item.itemFn() %>
<%} else { %>
<span class="cm-header-icon <%=dir %> js_<%=item.tagname %>" >
<% if(item.value) { %>
<%=item.value %>
<% } else { %>
<i class="icon-<%=item.tagname %>"></i>
<% } %>
</span>
<%} %>
<%} %>
<%} %>
</header>

PS:从代码着色来看,js中用到的left与Right是关键字,这个得处理...

定制化需求

可以看到,一个循环,我们便可以轻易的生成左边和右边的按钮,但是马上问题来了,我们需要扩展怎么办,上面就会有以下问题:

① tel标签默认是a标签,我们这里却是span标签

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

② back按钮我们一般会做成a标签,用以解决javascript出错在Hybrid的假死问题

说白了,就是虽然标签按钮应该有统一的结构,但是需要保留定制化的能力

这里定制化的工作交给了各个标签的itemFn这个函数,他返回一个字符串,并且具有一定规则,这里取一个代码片段:

 handleSpecialParam: function (data) {
var k, i, len, item;
for (k in data) {
if (_.isArray(data[k])) {
for (i = 0, len = data[k].length; i < len; i++) {
item = data[k][i];
if (this['customtHandle_' + item.tagname]) {
this['customtHandle_' + item.tagname](data[k][i], k);
} //if
} //for
} //if
} //for
}, _getDir: function (dir) {
var kv = { left: 'fl', right: 'fr' };
return kv[dir];
}, //处理back的按钮逻辑
customtHandle_back: function (item, dir) {
dir = this._getDir(dir);
item.itemFn = function () {
var str = '<a href="http://m.ctrip.com/html5/" class="cm-header-btn ' + dir + ' js_' + item.tagname + ' " >';
if (item.value) {
str += item.value + '</a>';
} else {
str += '<i class="icon-' + item.tagname + '"></i></a>';
}
return str;
};
},

当发现某个按钮不满足需求或者有定制化需求时,便想办法设置其itemFn即可,时候上这个代码可以直接写到初始化的json串去

花样百出的title

到title时,发现其表现便五花八门了,这个时候一般是根据不同的类型生成不同的HTML结构,框架给默认的几个选项,不支持便自己定制itemFn

 <% item = center; %>
<%if(typeof item.itemFn == 'function') { %>
<%=item.itemFn() %>
<%} else if(item.tagname=='title' || item.tagname=='subtitle') { %>
<h1 class="cm-page-title js_<%=item.tagname %>" >
<%if(typeof(item.value) == 'object' && item.title.value == 2) { %>
<span class="cm-title-l"><%=item.value[0]%></span>
<span class="cm-title-s"><%=item.value[1]%></span>
<%} else { %>
<%=item.value %>
<%} %>
</h1>
<%} else if(item.tagname=='select'){ %>
<h1 class="cm-page-select-title js_<%=item.tagname %>" >
<%=item.value %>
</h1>
<%} else if(item.tagname=='tabs') { %>
<h1 class="cm-page-tabs-title js_<%=item.tagname %>" >
<%for(j = 0; j < item.data.items.length; j ++) { %>
<span data-key="<%=item.data.items[j].id %>" class="<%if(item.data.index===j){ %>active<%} %>" ><%=item.data.items[j].name %></span>
<% } %>
</h1>
<% } else{ %> <%} %>

事件绑定的实现

header组件本身继承至Abstract.View这个类,所以只要设置

this.events = {}

便能以事件代理的方式将事件绑定至根元素,而header的事件一般就是click事件:

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

这里有一个需要注意的点便是,事件绑定的钩子便是我们的tagname,这个是唯一的,我们会为每个标签动态生成“.js_tagname”的类,以方便事件绑定

老接口的兼容

之前便说了,该组件是一个老组件的翻新,于是各个业务团队已经使用了,比如原来是这样调用的:

 this.header.set({
title: '基本Header使用',
subtitle: '中间副标题',
back: true,
backtext: '取消',
tel: { number: 1111 },
home: true,
search: true,
btn: { title: "登录", id: 'confirmBtn', classname: 'header_r' },
events: {
returnHandler: function () {
console.log('back');
},
homeHandler: function (e) {
}
}
});

而现在我们期望的调用方式是这样的:

 this.header.set({
left: [],
center: {},
right: [
{ tagname: 'home', callback: function () { } },
{ tagname: 'tagname', value: 'value', data: {}, itemFn: function(){}, callback: function () { } }
]
});

这个时候我们应该怎么做呢?当然是不破不立,先破后立,当然是要求业务团队改!!!然后被无情的喷了回来,于是做了接口兼容

翻新老组件,接口兼容是必须的,如果不是底层机制发生颠覆,而颠覆可以带来颠覆性的成绩,接口还是不建议改!

这里上面便是新接口的调用,下面是老接口的调用,效果如下:

代码&demo

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

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

反馈:如果文中有何不足,请您留言

【HTML5&CSS3进阶03】Jser与Csser如何一起愉快的翻新老组件的更多相关文章

  1. 【HTML5&CSS3进阶04】CSS3动画应该如何在webapp中运用

    动画在webapp的现状 webapp模式的网站追求的就是一个体验,是HTML5&CSS3浪潮下的产物,抛开体验不说,webapp模式门槛比较高: 而体验优化的一个重点便是动画,可以说动画是w ...

  2. 【HTML5&CSS3进阶学习02】Header的实现·CSS中的布局

    前言 我们在手机上布局一般是这个样子的: 其中头部对整个mobile的设计至关重要,而且坑也很多: ① 一般来说整个header是以fixed布局,fixed这个产物在移动端来说本身坑就非常多 ② 在 ...

  3. 【HTML5&CSS3进阶学习01】气泡组件的实现

    前言 气泡组件在实际工作中非常普遍,无论是网页中还是app中,比如: 我们这里所谓气泡组件是指列表型气泡组件,这里就其dom实现,css实现,js实现做一个讨论,最后对一些细节点做一些说明,希望对各位 ...

  4. 03.Web大前端时代之:HTML5+CSS3入门系列~H5功能元素

    Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 2.功能元素 1.hgroup 对网页或区段(secti ...

  5. Web大前端时代之:HTML5+CSS3入门系列

    准备来一波新技术,待续.... Old: 联系源码:https://github.com/dunitian/LoTHTML5 文档下载:https://github.com/dunitian/LoTD ...

  6. 08. Web大前端时代之:HTML5+CSS3入门系列 ~ QQ空间时间轴

    Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 大前端系列,主要就是使用CSS3.0来实现,注释我已经打 ...

  7. 12款非常精致的免费 HTML5 & CSS3 网站模板

    01. Joefrey Mahusay 很炫的单页网站模板,基于 HTML5 & CSS3 制作,适合用于设计师个人简历.摄影师和平面设计师的个人作品展示. 演示  下载 02. Folder ...

  8. HTML5+CSS3学习笔记(二) 页面布局:HTML5新元素及其特性

    HTML5的语义化标签以及属性,可以让开发者非常方便地实现清晰的web页面布局,加上CSS3的效果渲染,快速建立丰富灵活的web页面显得非常简单. 本次学习HTML5的新标签元素有: <head ...

  9. HTML5+CSS3 loading 效果收集--转载

    用gif图片来做loading的时代已经过去了,它显得太low了,而用HTML5/CSS3以及SVG和canvas来做加载动画显得既炫酷又逼格十足.这已经成为一种趋势. 这里收集了几十个用html5和 ...

随机推荐

  1. 基于SOA架构的TDD测试驱动开发模式

    以需求用例为基,Case&Coding两条线并行,服务(M)&消费(VC)分离,单元.接口.功能.集成四层质量管理,自动化集成.测试.交付全程支持. 3个大阶段(需求分析阶段.研发准备 ...

  2. 分享一个MySQL分库分表备份脚本(原)

    分享一个MySQL分库备份脚本(原) 开发思路: 1.路径:规定备份到什么位置,把路径(先判断是否存在,不存在创建一个目录)先定义好,我的路径:/mysql/backup,每个备份用压缩提升效率,带上 ...

  3. Linux 中我该如何备份系统

    系统备份概述 在前面的一些文章中,我反复提到经常会把系统搞崩溃,所以备份系统就是一件不容忽视的事情.由于 Linux 系统本身的优越性,系统的备份和还原还是比较容易的.主要表现在以下方面: Linux ...

  4. 利用log4j+mongodb实现分布式系统中日志统一管理

    背景     在分布式系统当中,我们有各种各样的WebService,这些服务可能分别部署在不同的服务器上,并且有各自的日志输出.为了方便对这些日志进行统一管理和分析.我们可以将日志统一输出到指定的数 ...

  5. Java在DOS命令下的运行及其API文档制作过程

    该文档主要描述java程序在DOS命令下的运行,以及一些常用的命令 常用DOS命令: d: 回车 盘符切换 dir(directory):列出当前目录下的文件以及文件夹 md (make direct ...

  6. ABP文档 - 审计日志

    文档目录 本节内容: 简介 关于 IAuditingStore 配置 通过特性启用/禁用 注意 简介 维基百科:“一个审计追踪(也叫审计日志)是一个安全相关的时序记录.记录组.和/或记录源和目标,作为 ...

  7. Entity Framework 6 Recipes 2nd Edition(10-9)译 -> 在多对多关系中为插入和删除使用存储过程

    10-9. 在多对多关系中为插入和删除使用存储过程 问题 想要在一个无载荷的多对多关系中使用存储过程(存储过程只影响关系的连接表) 解决方案 假设有一个多对多关系的作者( Author)表和书籍( B ...

  8. Entity Framework 6 Recipes 2nd Edition(11-9)译 -> 在LINQ中使用规范函数

    11-9. 在LINQ中使用规范函数 问题 想在一个LINQ查询中使用规范函数 解决方案 假设我们已经有一个影片租赁(MovieRental )实体,它保存某个影片什么时候租出及还回来,以及滞纳金等, ...

  9. C#经典笔试题-获取字符串中相同的字符以及其个数

    public Dictionary<char,int> GetStrSameAs(string str){ //将字符串转换成一个字符数组. char[] charArray=str.To ...

  10. [转]VS2015中臃肿的ipch和sdf文件

    使用VS2010建立C++解决方案时,会生成SolutionName.sdf和一个叫做ipch的文件夹,这两个文件再加上*.pch等文件使得工程变得非常的庞大, 一个简单的程序都会占用几十M的硬盘容量 ...