分类: 前端

以html+css为主的网页技术

  • Compass + cleancss 导致的灵异小问题

    Compass + cleancss 导致的灵异小问题

    问题不大,不过很诡异。代码如下:

    SASS:

    .some-class
      display: none
    
    .other-class:blank
      display: none
    

    HTML:

    <div class="some-class">...</div>
    

    理论上说,这样这个div应该不显示。在本项目中,它的确没显示;但是在另外一个将本项目作为依赖的项目中,它却显示出来了。

    经检查,本地开发和部署时,直接使用 compass compile 生成CSS,compass 配置中,设置输出模式(output_style)为 compressed,结果是这样的:

    .some-class{display:none}.other-class:blank{display:none}
    

    而在作为依赖时,用到的则是 grunt-contrib-cssmin 处理过的CSS,刚才那句就被压缩成

    .some-class,.other-class:blank{display:none}
    

    :blank伪类尚未被Chrome中支持,于是整条规则都被忽略,导致div显示出来。

  • 啊,前端的轮子啊

    啊,前端的轮子啊

    时势造英雄。

    IT科技几十年来诞生过不少概念,但直接拿来就能作为宣传材料的不多,HTML5算一个。借助智能手机、微信微博快速普及带来的平台优势,HTML5快速成长,接连攻陷投资界、创业界、广告界,现在说起H5,在我的圈子里几乎无人不知无人不晓。

    资金充裕,整个行业自然地位上升,财务上升;继而自然从业者增加;继而就会出产大量的产品。而作为前端从业人员,最大的感觉就是:轮子真TM多啊……这不,我前几天计划学习一下近来出现的各种新技术,今天正好看到Bable最新入门The Complete Guide to ES6 with Babel 6,正好我写xgame-api的时候饱受node不能完全支持ES6之苦,马上坐正准备好好看。

    然后就看到:

    Keeping up to date

    Here’s a little story: while writing these guides, I learned that the require hook in babel-core used by Gulp and Mocha is already set to be deprecated.

    跟上变化

    讲个段子:就在我写这套入门的时候,babel-core里的require钩子,就是Gulp和Mocha使用的那个,被标记为“弃用”

    Babel6是24天之前发布的,这套文章是上周写的,核心API已经发生变化了。这让我不禁去想,紧跟业界潮流的成本会不会太高了?ES6的确提供了不少语法糖,以及终于像样的模块管理。如果现在就开始使用ES6,不仅可以提前享受到这一切,到标准真正到来的时候,可以省去升级的时间。

    不过这里面之考虑了标准变化,没有考虑需求变化和产品变化。如果是抽象性很强的代码,比如某种算法库,或者某个功能组件,那么从开发之日起,遇到的变化可能不会太多太大。但如果是业务代码,就不得不遭遇各种各样的修改和变化,于是功能稳定便于维护就更加重要。紧跟潮流,意味着必须付出多余的精力去应对各种不稳定导致的节外生枝,一不小心,业务逻辑就要延期交付鸟。

    又有个切身例子。Tiger Prawn(后面简称TP)立项的时候,模块管理无论是AMD还是CMD还是sea.js都难以让人满意,所以我就用了最简单的直接引用和命名空间。前几日用Browserify感觉不错,赶上TP遇到个问题,就寻思着重构下。结果努力一天之后,架构变化还是太复杂,遂放弃。次日修正实际问题只花了半天。生产中,还是优秀的架构设计更重要,毕竟开发和维护效率至上;追新追潮流几乎都可以算是作死的行为。

    那么什么时候学习新技术呢?程序员都应该做业余项目,不是私活,而是根据一些平日里瞎想出来的需求,用一些新技术去实现,即使实现不了,也能够实地演练各种新技术。这样,有朝一日新技术成熟了,就可以很快应用到新产品中。

  • 纯CSS实现toggle按钮

    将来要用到,提前准备一个。

  • 新项目:诺兹多姆,时间的守护者

    其实也不算新项目。加入点乐之后,我的主要任务之一就是做后台。我是个自视甚高的人,所以要做就做最好用的后台,经过不懈努力,现在的点乐后台V3.3版,无论从开发的角度还是使用者的角度来看,(应该说)都已经非常好用了。

    不过问题仍然有很多,主要分为两方面:性能和弹性。

    性能

    先说性能,最初的版本是完全PHP的,用户操作都基于表单。我将其改成加载局部HTML片段,用户操作主要用ajax实现。显然,后台不可能停工,所以我边改边上线,架构上基本继承前人的设计,比如,大多数页面没有分页功能(很多同事用习惯了,也要求不要加分页)。

    于是,大多数页面都要读取符合条件的全部数据,然后由Javascript实现排序、筛选、搜索等功能。当初我这么做的时候,公司业务量小,没什么问题;如今公司发展起来了,业务量越来越大,每次都加载全部数据,页面性能越来越差。尤其是手机端,很多页面一开就死。无奈之下,我只好先用Javascript分页顶着。

    弹性

    曾经懵懂少年的我动手时还不太会用Backbone,没有理解其精髓,只是觉得Backbone.View的事件代理比较好,写起来蛮舒服;更没想到借助Robotlegs的优秀设计开发Nervenet,所以前端架构上也有问题。比较严重的是数据同步(所谓数据双向绑定),即操作人员修改了某数据,保存到服务器后再回来,页面应该更新被修改的数据。那时候Knockout刚出来,Angular和Ember都还不堪大用,数据双向绑定对我来说只是个梦想。还好我码功扎实,也能解决的七七八八,比如主功能区共用一个Model作为中介(Mediator Pattern,中介者模式),数据交互都从它那儿走,其它组件监听change事件来更新视图。

    不过这样终究不是办法,因为随着功能的增加,代码里各种写死的属性和方法越来越多,代码日益臃肿,弹性也是个大问题。

    诺兹多姆诞生

    恰在此时,新项目来了。新项目也需要后台,我当然不会直接把点乐后台拿来重用,而是花了一些时间剥离其中的前端框架,准备将其独立出来,成为一个新项目:诺兹多姆。

    诺兹多姆,青铜龙之王,时间守护者。在炉石传说里,诺兹多姆将强制把每回合的事件降到15秒,这也是我的目的,无论用户要干什么,都能在很短的时间内——不妨假定是15秒——完成。

    这是个专门针对后台的前端框架,能够覆盖各种后台的需求,也能提供漂亮的界面和良好的交互。等各种希望这个项目完成后,能够达到这些目的:

    1. 大大提高后台类应用的开发效率
    2. 前后端分离,独立开发,分开部署
    3. 丰富的组件,覆盖后台常用功能
    4. 灵活的插件机制,扩展能力强大
    5. 漂亮的界面,可以自由更换主题
    6. 全平台覆盖,甚至支持打包成移动应用

    开源

    此项目是在公司做的,当然要以公司的名义开源。当然,开源的结果基本上还是只有我们公司在用……

    现在还在开发中,离最终目标还很远。

    [github repo=”Dianjoy/nozdormu”]

    致谢

    此项目中用到了大量开源代码,感谢他们的创作者,是他们让世界变得更加美好。

    感谢公司给我这个机会。感谢同事们让公司不断成长,给我留下尝试的机会。

  • boostrap3的不良部分

    Bootstrap 3的脑残部分略多啊……比如.hidden-xs,明显应该是在超小设备上隐藏,结果里面赫然写着:display: block !important,important个毛啊……

  • [教程]纯CSS实现多选组件

    [教程]纯CSS实现多选组件

    产品篇

    在我们的后台中,需要设置广告精准投放的区域,也就是要在全国31个省、自治区、直辖市中选择。那么,出现下面这幅景象也就理所应当了:
    1

    这样做有几个问题:

    1. 选项很多,没有规律,找起来很累
    2. 如果是一个已经选择了部分选项的广告,修改时仍然需要用肉眼寻找,无法一眼看出来投放到哪些省份
    3. 选完一个,再选下一个,还要从头找,甚至会被已经选过的影响

    于是我想,首先应该把所有选项分为“已选中”和“未选中”两批,解决第2个问题,减轻第3个问题;其次复选框本身的价值不大,可以被替换为其它样式;唯一可能引入的问题,就是点选时,用户的预期是看到复选框里出现一个小对勾,表示选中,如果我把它移开放到“已选中”组里,用户可能会迷惑,需要一些时间学习。

    于是我跟某产品经理朋友聊了聊这个想法,他表示确实可能造成用户迷惑,不过如果能加入动画效果,那么基本没问题。嗯,开始动手。

    技术实现篇

    近日flexbox规范定案,浏览器相继支持display:flex;,同时传来一条好消息,新实现比老实现display:box;快很多。这次我打算用flexbox来解决问题,因为里面有一个很重要的属性:order(之前叫box-ordinal-group),它可以改变布局中元素的排列顺序,配合CSS3新增的选择器,应该可以满足需要。

    第一步 分拆选中/未选中

    (关于flexbox的知识,可以通过Google了解,虽然搜到的多是上一个版本,不过和最终版差别不大,只是叫法不同。本文不再过多讲解,我就当大家都会了)

    <input type="checkbox">本身的样式不能修改,所以我们必须借助<label>的帮助;实现选中/未选中区分,那自然就要用到伪类:checked;选择器一定是从外到内、从前到后的,没法选择父级元素,所以不能用<label>去包<input>,那么最终布局就只能是:

    <div>
        <input type="checkbox" name="q[]" id="q1" />
        <label for="q1">小宝3225</label>
        <input type="checkbox" name="q[]" id="q2" />
        <label for="q2">王老白白白</label>
        <input type="checkbox" name="q[]" id="q3" />
        <label for="q3">空夫31</label>
        <input type="checkbox" name="q[]" id="q4" />
        <label for="q4">谷大白话</label>
        <input type="checkbox" name="q[]" id="q5" />
        <label for="q5">Meathill</label>
        <input type="checkbox" name="q[]" id="q6" />
        <label for="q6">一毛不拔大师</label>
    </div>

    很简单哈,不解释了。CSS3新增了“下一节点”选择器 +,用来选择某节点的下一个节点,结合:checked伪类就可以将选中的<input>和它临近的<label>通过改变order属性移到前面去:

    #container {
      display:flex;
      flex-direction:row;
      flex-wrap:wrap;
    }
    #container input,
    #container label {
      order: 2; //所有选项、label顺序为2
    }
    input[type=checkbox]:checked,
    input[type=checkbox]:checked + label {
      order: 0; // 越小越靠前
    }

    不过这样只是把选中的内容提前,视觉上没有真正的分割。所以我决定再加入一根分割线,上面是选中的,下面是未选的。这个时候我们需要用到 ~ 这个选择器,选择某节点后面的节点:

    hr {
      display:none; // 默认情况下,没选任何选项,分割线隐藏
      order: 1; // 分割线顺序为1
      width:100%; // 保证独霸一行
    }
    input[type=checkbox]:checked ~ hr {
      display:block; // 有选项被选中后才会显示分割线
    }

    Demo如下:

    这样基础功能实现了。不过视觉上,排版仍然不整齐,选中的选项和未选中的选项区分不算太明显,所以下一步我准备美化下checkbox。

    第二步,美化checkbox

    做法与前面类似,也要用到CSS3新增的选择器。前面为了实现<label>提前,没有用它包裹<input>,所以在选项很多很长导致换行的时候,可能出现复选框和标签脱离的尴尬状况。好在复选框的价值可以用别的样式取代,所以先把小方框隐藏起来,转而将<label>作为操作目标,再来点边框底色圆角(参考自Bootstrap 3),就可以了:

    input[type=checkbox] {
      display: none;
    }
    label {
      min-width: 120px;
      border: 1px solid #CCC;
      padding: 2px 8px;
      text-align: center;
      margin: 0 5px 5px 0;
      background: #FFF;
      color: #333;
      border-radius: 3px;
      box-sizing: border-box;
    }
    label:hover {
      border-color: #ADADAD;
      background: #EBEBEB;
      cursor: pointer;
    }
    input[type=checkbox]:checked + label {
      order: 0;
      background-color: #5cb85c;
      border-color: #4cae4c;
      color: #FFF;
    }
    input[type=checkbox]:checked + label:hover {
      background-color: #47a447;
      border-color: #398439;
    }

    这样看起来还有上升空间,如果加上几个图标响应用户操作,那么学习成本会更低,对操作后的预期也会更准确。于是引用CDN上的font-awesome,使用:before伪类加上小图标,就得到了最终效果:

    我无意中发现,这样批量添加删除时,鼠标可以常点不动,应该也是个意外的收获吧。

    第三步,加入动画教育用户(失败)

    至此功能基本做好了,不过由于修改了行为,可能导致用户迷惑,所以准备加个动画帮助用户理解这个交互。

    可惜作为一个新功能,浏览器的支持尚不完善,虽然规范中规定“animatable: yes”,但是实测在Chrome v.30也无法工作:http://jsfiddle.net/meathill/Ka66W/1/

    看来只有等新版浏览器发布后再去完善了。

    兼容性

    使用纯CSS做组件,几乎不用担心兼容性问题,因为浏览器本身就做了很好的向下兼容,代码最多不生效,一般不会错。

    具体到这个组件,因为只针对视觉效果,没有增删改任何浏览器行为,所以兼容性也没有任何问题。不过最终效果呢,只有支持flexbox和CSS3选择符的浏览器才能正常渲染。

    我的环境是Window 8 + Chrome v.30,以及小米2 + Chrome v.30,测试通过。

    后记

    如今CSS很强,纯CSS可以实现很多功能,希望今后能做出更多有价值的东西。分享这个组件的实现,希望对大家有用。

  • 悲催的Android Webview——记新版广告墙开发

    悲催的Android Webview——记新版广告墙开发

    前一阵很辛劳,所以荒废了博客。前几天终于完成了这项艰苦卓绝的工程:HTML5版广告墙,决定写篇文章,记录一下踩过的坑。

    项目介绍

    广告墙属于典型的列表式应用:打开后是无尽列表,通过滑动手指驱使列表滚动,上拉加载更多内容,下拉刷新列表(这次没做)。单击列表中的某项,打开详细页;单击上面的后退按钮,退回上一级页面。

    考虑到目标平台是Android系统,内嵌WebKit内核,我采用了时下流行的HTML5+CSS3技术,并且做好了向下兼容的准备。为了减少代码量,我放弃了jQuery,也没有考虑半调子Zepto,全部使用原生开发。操作上,开始想用Hammer.js封装手势,后来处理滚动效果处理得不好,最终选择了iScroll 5。模板方面,前端使用Handlebars.js,后端使用Mustache,所以编译时还写了段代码做转换。

    项目代码托管在github上,因为前端不涉及到业务逻辑,所以仓库是公开的,有兴趣的同学以clone下来看看。

    技术选型大概聊这些,接下来开始进入正题。

    性能vs兼容性

    实际上,随着硬件的发展,对于列表式这种非性能敏感性应用来说,性能早已不是制约其发展的首要因素了。另一方面,有一定专业精神的前端如我,平时也会注重积累各种性能优化的点,并不断在项目中实验、实施——所以我就很搞不明白,技术实力强大如jQuery,为啥会弄出个jQuery Mobile这种完全不能用的东西出来……

    相反,在这个项目中,由于平台分裂带来的兼容性问题成了最大的绊脚石。尤其是这些兼容性问题,极少找到现成的解决方案……下面举两个例子:

    Vivo X909 和z-index

    正常情况下,position:absolute;的dom节点在不显式设置z-index属性区分层级的时候,默认后面的节点会显示在前面的节点上方。大多数浏览器里都是这样,可是Vivo X909就不行。

    列表大家都知道,点击某项,详情会从右边滚动进来。由于没有设置-webkit-tap-highlight-color(现在想想应该做的,下周回去加上吧),所以这个bug当时的的表现是“单击没有反应”。这种情况很难调试,我开始以为JS有错,试了很久,后来配置好Weinre(见上篇博客),才找到症结所在。最后把几个图层分别加上z-index,问题解决。

    早期Android的animation-fill-mode

    我们仍有20%的用户在使用2.3.x版本的Android,所以这块市场必然不能放弃。在小米2上调是好的动画,在老HTC G10上,滑出的图层在动画结束后会消失。这次好在有了上次的经验,直接上Weinre;加上caniuse.com也有明确的描述,所以很快解决了。


    各种难以定位、缺少文档、找不到解决方案的兼容性问题层出不穷,让我不禁感慨:本以为买上了WebKit的康庄大道,谁知尼玛这豆腐渣工程下面全是坑啊……

    滚屏

    前面提到,开始时间比较富裕,我也比较自信,想借助Hammer.js,慢慢打磨出色的滚动体验。后来无尽的bug环伺左右,实在无法专心研究,便退而求其次,使用iScroll了。

    iScroll如今已经发展到5,其作者自信的表示:“我尽全力提升了其在Android平台上的表现,我相信现在它已经达到了极致。”实验效果确实很理想,无论是高配置的小米,还是老旧的HTC G10,都表现出色。将来有空的时候得好好研究下他是怎么做的。

    工程实践

    随着时代发展,Web开发这种脚本语言也开始分裂成开发、部署两个阶段,这是个好事情。核心代码需要为维护、测试留下足够的空间,势必在性能表现上有所不足,部署时根据应用场景的不同输出不同的结果正好能解决这个问题。这个项目中我使用Grunt,花费了不少精力在编写Gruntfile.js上。

    开发时,我将测试用的数据,按照设计好的数据结构写在define.js中,生产环境中用服务器生成真实数据来替代。不同的应用场景,也用其配置。如今,目标支持的6个场景都能稳定工作,效果不错。

    Windows下不好配ruby,使用SASS是个问题,不过后来找到了Prepros,问题迎刃而解。

    技术细节分享

    简单构造选择器

    jQuery功能确实强大,不过实在太重型了;备胎Zepto轻便一些,不过功能也弱一些,语法做不到全兼容,用起来不舒服。仔细考虑这个项目的需求,无非是:找到某个元素,给它添加事件侦听;或者判断它是否包含某个class,那么自己写一个直接到Dom节点的选择器就足够了。后来看到篇文章,里面详细测试了querySelectorgetElementById众的性能,发现还是后者大大领先——尤其在移动平台上,于是基本放弃了用前者的念头。

    这个选择器实现起来很容易:

        var $ = window.$ = function (selector, root) {
          root = root || document;
          var dom;
          switch (selector.charAt(0)) {
            case '#':
              selector = selector.substr(1);
              dom = document.getElementById(selector);
              break;
    
            case '.':
              selector = selector.substr(1);
              dom = root.getElementsByClassName(selector)[0];
              break;
    
            default :
              dom = root.getElementsByTagName(selector)[0];
              break;
          }
          return dom;
        };
    

    另外,$还可以用来当命名空间,保存一些需要全局使用的变量和方法。

    使用Handlebars.js生成预编译模板

    预编译模版可以降低运行时的运算量,对于移动开发这种能省则省的应用场景,实在是必备之物。另一方面,为了方便开发,把模版放在HTML里更好调整。好在有“编译输出”这个步骤,可以两全其美。

    Handlebars.js的预编译是把模版转换成根据数据生成HTML的JS代码,可以使用uglify压缩。编译之前最好过滤掉多余的空格和换行符,这样生成的JS文件也会小很多。

    当然,再预编译也不如直接渲染HTML来的快,所以在服务器端生成页面的时候,一定要包含实际内容,让用户尽快得到信息。而PHP没有现成的Handlebars实现(我也不打算写),好在有mustache.php,并且handlebars兼容mustache(其实不完全,也被坑了),所以我写了一小段代码将Handlebars模版转换成Mustache模版

    使用attr()实现CSS中的多语言

    除了传统的中文版,这次还要制作英文版。英文版与中文版的功能几乎完全一致,只是文字全部换成英文。最简单的做法就是搞个en.html——因为使用了模版,JS里不包含任何文字内容;但是“上拉加载更多”这个功能目前使用CSS的:after实现——初衷是不修改dom,不引起relayout——当然,反正我用着Sass,增加个语言包不难,不过我还是希望把修改集中在一个地方。

    后来查了下CSS表达式的兼容性,发现Android 2.1起就支持,那就好办了,先把各阶段文案放到HTML的属性里:

        // 中文
        <div id="list" data-normal="继续上拉,加载更多广告" data-more="松开加载" data-loading="加载中..." data-no-more="没有其它广告了,再看看吧" data-error="加载失败,请稍候重试" data-over=""></div>
        // 英文
        <div id="list" data-normal="Pull up to load" data-more="Release to load" data-loading="Loading..." data-no-more="No more ads" data-error="Load failed" data-over=""></div>
    

    然后在CSS里使用attr()访问即可

        #list.over
          &:after
            content: attr(data-over)
    
        #list.more
          &:after
            content: attr(data-more)
    
        #list.loading
          &:after
            content: attr(data-loading)
    
        #list.no-more
          &:after
            content: attr(data-no-more)
    
        #list.error
          &:after
            content: attr(data-error)
    

    WebKit中z-indextranslateZ(0)混用,导致click事件半失灵的解决

    1. 要在浏览器里触发下载,只要链接到一个浏览器打不开的文件就行。链接跳转是在click触发的浏览器内建行为,换言之,链接跳转依赖于click的正常触发。
    2. clicktouch event晚200~300ms,所以为了保证用户体验,我们通常用taptouchstart + touchend)来替代click响应用户操作
    3. 所以一个“点击下载按钮”的操作,就会先响应tap事件,弹出详情浮层,告知用户获得积分的最终条件,然后等待浏览器开始下载
    4. CSS动画最好使用translateZ(0)促使浏览器启用GPU加速
    5. 这个bug在以上条件下产生,影响这个过程

    之前我在PC版Chrome里也曾见识过这个bug(最新版没验证,大概20+的时候吧)。具体表现为,一个层,它的translateZ有赋值,它可能被移到某处(通过translateXtranslateY,或者动画过程中),不过还未到达那里,但是它仍然会拦截那里mousedown/mouseup事件,导致click事件无法正常触发。

    这个bug非常难排查,因为它并不影响tap;视觉上看不到那个层,会想当然的认为不应该跟那个层有关系;并且,WTF,如果按得时间稍长,比如半秒,就可以触发click……因为触发条件比较复杂,我甚至很长时间没找到规律。最后只好请出“小黄鸭调试法”,试图向索隆解释发生了什么,然后发现无法正常下载的按钮都是被详情浮层遮盖着,继而回忆起以前解决过类似问题。

    最后解决这个问题的方案我其实不很满意,不过确实能顺利工作:

    1. 层的当前位置不会覆盖按钮,先不给层增加动画
    2. tap后,给click留出足够的时间(我这里用400ms),再给层添加动画
    3. 确保click可以响应,通过setTimeout给层添加动画——本来300ms就测试通过,不过为了保险,我还是决定写成400

    仍然悬而未决的问题

    虽说项目已经快上线了,但仍有几个不解之谜……

    1. 某些手机仍然无法下载(启动<a>
    2. 快速拖动的时候可能把内容拖走(应该是触发了浏览器默认行为)

    总结

    做完项目回头看,系统分裂造成的兼容性问题比想象中多很多,原先以为最多是有些2.x版本不支持,做向下兼容就好,谁知还有各种实现细节问题。不过,在PC Chrome + 手机Chrome + Weinrealert之后,只要有耐心,大多数问题时可以解决的。

    说完开发效率,另一方面再来看看产品表现。我们对性能不需要太乐观,也不用太悲观,敏感型应用,老老实实用原生开发,或编译成原生应用;非敏感型,跨平台高兼容性(咦,这眼泪哪里来的)的优势很明显。通过我实际看到移动端的表现,我们这些页面仔应该更有信心,将来移动大市场里,Web App的份额绝对不比Native App要少。

    这项事业任重而道远,需要我们一起努力。

  • 前端框架点评

    前两天被人@,提问前端框架的问题。这还是第一次在微博上被陌生人提问,感觉有点小激动。今天写篇小文章,简单点评一下我用过的前端框架,希望对大家有所帮助。

    本文涉及的范围

    本文要点评的前端框架,都是我用过的,以JavaScript框架为主,也可能包括一些工具类库。由于前端的特殊性,弱语言HTML和CSS是必不可少的工具,运行机制导致很多成熟的工程学方法没法用,必须用工具库补充。烦请大家不要抠字眼,我一向很马虎,呵呵。

    Bootstrap

    一定要先推荐Bootstrap。它由Twitter的两位工程师首创,当前版本2.3.1,涵盖了文本、布局、表单、工具,除了暂时没有日期选择器之外,几乎包含了网页所需的一切。我们知道,网页出现是为了传递信息,而成熟的印刷业对其影响很大,表现出来就是,HTML中很多标签,比如H1~H6p等,都是有具体语义的。但很多前端框架并未给予他们足够的重视,所以往往需要我们重置或者自己动手写。Bootstrap则不是这样。它不仅包含大量组件,还帮助我们给每种可能用到的语义标签都定义了样式,又设计出很多helper;而其网格系统也能帮我们出色完成排版工作。总而言之,使用它几乎足以满足任何网页开发需求。

    值得一提的是,Bootstrap的组件和Widget都基于HTML标签构建,非常好用;而响应用户操作则基于对document的侦听,所以几乎不会对我们其它代码逻辑造成影响,我们可以放心大胆的在项目当中使用。当然,Bootstrap依赖jQuery,后者近期遭遇到一些纷争。不过我还是要强烈推荐这个框架。

    jQuery UI

    比较遗憾的是,虽然号称UI,但jQuery UI只能算作一个组件库。因为缺少基础排版支持,所以既想使用jQuery UI,又想和自己的样式保持一致会有些困难,需要花费不少时间做修改。

    抛开这一点,jQuery UI也是个很好的工具,几个widget都很实用,文档丰富详实,如果项目本身对交互要求不高,只是个别地方需要一些功能补充,jQuery UI确实是不错的选择。尤其当我们需要draggablesortableresizable这几个功能时,选择的余地真的不大。

    HTML5 Boilerplate

    这个工具力图给开发者提供一个舒适的起点。他们为项目建立了标准化的目录结构,并且把常用的资源都整理在合适的位置,这样我们只需要替换它们就能保证项目对所有浏览器都有完美表现。同时,他们重置了CSS,引入了GA统计,在页面中标记了合适的输入点,这样我们能尽快投入到项目逻辑的开发中。

    但这个工具的问题也很明显,它努力达成的目标,是消弭浏览期差异。假如我们只需要统一的环境就能开展工作,那H5B就足够了;而实际上,我们通常需要更多,比如排版、比如组件。所以,这个工具对我来说,学习的意义大于使用的意义。

    HTML KickStart

    我见过的前端框架里只有这个跟Bootstrap的覆盖面接近。它的设计更好,更鲜艳更有活力,不过最近的开发好像慢了下来。我并没有真正使用它,因为它的组件太少了。

    Backbone

    非常好的MV*框架,真的像根脊柱一样,把被jQuery拆的七零八散的js统一在合理的框架内,让整个项目都变得协调了。引入Collection真的很天才,结合其出色的View设计——其实Backbone的View并不是传统意义上的View,更像mediator——可以看出,Backbone的设计者对Web开发有着深刻理解。哦,差点忘了,Router还能帮我们很好的整理单页应用的逻辑。

    不过js毕竟是一门很烂的语言——或者让我换种表述方式,缺陷很多,所以它尚无法做到像AS3的Robotlegs那样好。不过,如果我们希望把代码组织的更好,Backbone类的框架是不可或缺的。

    Rachet

    Rachet的目的和架构,很像Bootstrap,不过它瞄准在移动端。Rachet现在仍处于比较低级的阶段,组件太少,开发起来不太方便,最多相当于Kickstart把。可惜的是,移动端移动框架普遍较弱,在我看来,基于它的Junior仍然是不二之选。

    Zepto

    说到移动端开发就不能不提Zepto。它力图实现一个十分类似jQuery的库,不过只考虑支持移动端,所以体积会小很多,速度也会快不少。Zepto的升级频率最近也不高,不过还是到了1.0。新版Zepto放弃了ruby,使用coffee作为编译工具,支持使用者自由组合功能模块,相当贴心。

    不过具体使用时,还是会有一些函数的用法和jQuery不同,需要注意。举个例子,css函数,jQuery里支持数字,比如$('div').css('height', 100),会把页面中所有div的高度设置为100px;而Zepto不行,必须$('div').css('height', '100px')才会正常工作。

    jQuery Mobile

    强大的jQuery团队最失败的作品。贪恋于风光的过去,迷信“全覆盖”,使得jQuery Mobile步履蹒跚,在移动平台上无法取得足够好的性能表现(早期版本甚至不支持固定底部)。而这直接带来了对HTML在移动端的质疑。有很多本不擅长前端开发的人,看到Phonegap,就直接上jQuery Mobile,结果发现性能不行,体验很差,便在各种渠道大放厥词,说HTML不行。这种行为直接伤害了HTML和Hybrid应用。

    jQuery Mobile失败不仅在于此。jQuery本身方便快捷,使用它很容易养成不重视代码组织的习惯;而在移动端,单页应用占据主流,这意味着,使用jQuery Mobile很可能无法良好的组织代码,直接带来项目维护成本的提高。另外,早期版本的设计也不可避免的带有大量Web痕迹,导致实用性不足。所以直到今天,jQuery Mobile都还是个悲剧。

    不过,jQuery 2.0已经beta2了,我们知道,jQuery 2.0里放弃了对IE678的支持,性能会得到不小的提升;jQuery Mobile 1.3已经引入了对hash的支持,组织代码好多了;同时,随着3次小版本更新,越来越多适用于移动应用的组件加入进来,大大扩充了军备库。我认为,未来的jQuery Mobile,值得一试。

    Sencha Touch

    大家可能还记得,Facebook闹过一阵,说HTML5不够好,还把他们的手机客户端用原生重做了一遍。之后Sencha的两位工程师表示不服,以HTML5为基础开发了功能几乎完全一样的客户端,他们用到的框架就是Sencha Touch。据说,它的性能比jQuery Mobile好不少。

    但是,真正用了就知道,sencha touch实在太不“前端”了。它里面几乎没有给HTML和CSS留下太多位置,明明十分强力的布局样式工具,被封装成了单薄的接口;但是没少往JS框架里加内容。结果就是用起来非常复杂,随时需要看文档;自定义困难,它里面甚至有一个属性叫“html”,用来填入大段文本类型的html;数级嵌套起来的对象也很让人头疼,而且感觉维护也不会太容易。所以我做了一半就还是放弃了。劝大家也别去尝试了。

    如果有哪位高人愿意以实际经验教育我的,我洗耳恭听。

  • 诡异BUG之:IE下表单必须提交两次

    做前端遇鬼是常事儿,比如今天,就遇到个:

    1. IE6~10
    2. jQuery 1.8.3
    3. Backbone 0.9.2
    4. HTML5头
    5. 一个Backbone.View,内部有一个formView托管formsubmit事件,根据classaction进行不同的操作,或者验证数据提交表单
    6. 第一次提交,验证通过,return true,没反应
    7. 第二次提交,验证通过,return true,表单提交
    8. 其它浏览器表现正常

    提炼这些Bug描述就花了不少时间,反复Google也没有什么结果。比如,StackOverflow上类似问题的解答是:submit原本不会冒泡,所以应该直接侦听form;但实际上jQuery1.4之后已经支持submit事件的托管了,我的实验也支持这个结果。

    至于导致问题的原因,我还没想明白也没测出来,留着以后再做吧。最后用Hack的方式暂时解决:

    if ($.browser.msie && isIEFirstSubmit) {
      isIEFirstSubmit = false;
      setTimeout(function () {
        form.submit();
      }, 50);
    }

    有了解这个的高人还请不吝赐教。有感兴趣的同学可以去试试:http://www.dianjoy.com/dev/#/user/updateuser