^ Back to Top
Fork me on GitHub

Javascript 模块管理

javascript这门语言并没有模块系统,没有标准接口,缺乏包管理系统,标准库更是很少,所以当出现一段很common的逻辑处理时,我们无法采用类似于require/include之类的做法。在一个项目越来越大,前端开发人员越来越多的时候,这对于我们的前端的代码组织是一个很大的挑战。所以也就有了本篇所讨论的问题。

首先,这并不是一个新的问题。很长一段时间之前,业界便有了解决方案。本篇的目的主要是总结一个各个方案的优缺点,给以后的项目做一个参照。

目前业界主流的做法有以下几种:

  • CommonJS
  • AMD
  • UMD
  • CMD

CommonJS

CommonJS是服务器端模块的规范,Node.js采用了这个规范。

根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性(当然这不是一个推荐的做法)。

CommonJS对模块的定义主要分为:模块引用,模块定义,模块标识3个部分。


// 模块引用
// 引入math.js
var math = require('math');

// 模块(math.js)定义
exports.add = function() {
  var sum = 0,
    i = 0,
    args = arguments,
    l = args.length;
  while(i < 1) {
    sum += args[i++];
 }
  return sum;
};

// 使用示例
var math = require('math');
exports.increment = function(value) {
  return math.add(value, 1);
};

AMD

AMD(Asynchronous Module Definition)规范是CommonJS模块规范的一个延伸,它使用define方法定义模块。require.js的实现就采用了该规范。


define([id], [dependencies], factory);

// CommonJS style
var $ = require('jquery');
exports.myExample = function () {};


// AMD style
define(['jquery'] , function ($) {
    return function () {};
});

CommonJS vs. AMD

这永远是一个话题,因为谁也无法很好的取代谁,尤其在浏览器环境里面,两者都有自己的优点和缺点。

CommonJS
优点:简洁,更符合一个module system,同时 module 库的管理也非常方便
缺点:浏览器环境必须build才能使用,给开发过程带来不便

AMD
优点:天生异步,很好的与浏览器环境进行结合,开发过程所见即所得
缺点:不怎么简洁的module使用方式,第三方库的使用时的重复繁琐配置

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。
由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。
但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。

但是,AMD 有一个不得不承认的作为一个module system的不足之处
请问?在 AMD(requireJS)里面怎么使用一个第三方库的?
一般都会经历这么几个步骤:
1. 使用的第三方库不想成为 global 的,只有引用的地方才可见
2. 需要的库支不支持 AMD ?
3. 不支持 AMD,我需要 fork 提个 patch 吗?
4. 支持AMD,我的项目根路径在哪儿?库在哪儿?
5. 不想要使用库的全部,要不要配置个 shim?
6. 需不需要配置个 alias ?
一个库就需要问这么些个问题,而且都是人工手动的操作
最最关键的问题是你辛辛苦苦搞定的配置项都是相对于你当前项目的
当你想用在其他项目或者是单元测试,那么OK,你还得修改一下
因为,你相对的是当前项目的根路径,一旦根路径发生改变,一切都发生了变化
requireJS 使用之前必须配置,同时该配置很难重用
相比较于 CommonJS 里面如果要使用一个第三方库的话,仅仅只需要在 package.json 里面配置一下 库名和版本号,然后npm install一下之后就可以直接 require 使用的方式,AMD 的处理简直弱爆了 !!!

对于 AMD 的这个不足之处,又有社区大神提出了可以在 browser 运行的 CommonJS 的方式,并且通过模块定义配置文件,可以很好的进行模块复用
比较知名的就有 substack 的 browserify, tj 曾主导的 component,还有后来的 duo,webpack,时代就转眼进入了 browser 上的 CommonJS。

UMD

UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere.

https://github.com/umdjs/umd

CMD

由玉伯提出,在定义模块以及依赖引入的部分与AMD有所区别。
sea.js的实现采用了CMD规范。

参考资料:
[1]: http://requirejs.org/docs/whyamd.html#amd
[2]: http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html
[3]: http://bearcatjs.org/%E5%8D%9A%E5%AE%A2/index.html

30 Mar 2015

Customize Horizon

how to reuse horizon table component by an existing api service(not powered by django)

  • 1.get response from your api service here you may need to use “requests” package

sample code:


        url = host + "item/search"
        payload = {'search_term': '*',
                   'group_by': 'aaa',
                   'risk_only': 'false',
                   'order_by': 'name',
                   'ordering': 'ASC',
                   'group': 'null',
                   'from_row': 1,
                   'to_row': 10
                   }
        headers = {'Content-Type': 'application/json',
                   'x-auth-header': token
                   }
        r = requests.get(url, params=payload, headers=headers)
	results = r.json()
        print results

  • 2.wrapper your resopnse(normally a json string) into a Django Class

sample code:


class Item:
     def __init__(self, itemid, itemname, fingerprint, statecode):
        self.id = itemid
        self.itemname = itemname
        self.fingerprint = fingerprint
        self.statecode = statecode

  • 3.define a new table Class for it in tables.py

sample code:


class ItemTable(tables.DataTable):
    itemname = tables.Column("itemname",
                         verbose_name=_("Name"))

    fingerprint = tables.Column("fingerprint",
                         verbose_name=_("Fingerprint"))

    statecode = tables.Column("statecode",
                         verbose_name=_("State"))

    class Meta:
        name = "item"
        verbose_name = _("item")
        #table_actions = (ServiceFilterAction,)
        #multi_select = False
        #status_columns = ["statecode"]

  • 4.how to use it, will take tabview as an example

sample code:


class SampleTab(tabs.TableTab):
    table_classes = (customized_tables.ItemTable,)
    name = _("Customized table")
    slug = "sample"
    template_name = "customized/items/_detail_sample_tab.html"
    #preload = False

    def get_item_data(self):
        data = '[{"itemid": 1, "itemname": "itemname111", "fingerprint": "fingerprint111", "statecode": "statecode111"}, {"itemid": 2, "itemname": "itemname222", "fingerprint": "fingerprint111", "statecode": "statecode111"}, {"itemid": 3, "itemname": "itemname333", "fingerprint": "fingerprint111", "statecode": "statecode111"}]'
        json_data = json.loads(data)
        items = []
        for inst in json_data:
            items.append(item(inst['itemid'], inst['itemname'], inst['fingerprint'], inst['statecode']))
        return items

  • 5.render the table
    make a template, and render the table

sample code:



  


01 Feb 2015

Automatic css sprite

CSS Sprite自动化

本篇主要讨论如何通过自动化工具来实现CSS Sprite,进而提高前端团队的开发效率。

历史回顾

先回顾一下我们之前的做法:

最原始的时候,CSS Sprite尚未出现。那时候在前端项目里出现一系列单独的image/icon是很常见的事情。在需要使用图片的地方,我们需要在css里逐个地引入image,尤其在有很多button的不同样式,不同效果的时候,这种写法放眼望去会有很多很多。只要有需要我们都可以通过background-position来实现。

随着项目的体量越来越大,各种资源文件的加入。突然有一天,我们会发现,网站变得越来越慢了。这个时候,各种关于网站性能的抱怨也就随之而来。

前端团队发现,往往一个页面里会包含N多个request。很多request都是从server上面来获取image的。兵来将挡,水来土掩。我们能不能想点办法来减少些request呢?这也就是CSS Sprite出现的初衷了。

人们发现,通过把一些image合并起来,能够达到效果。最常见的就是各种小icon了。一字排开,一个挨着一个,这样我们就能在减少http request这一块优化不少。

方法,思路有了。但是真正做起来就会慢慢发现问题了。

  • 1. 拼图片。这绝对是个体力活儿啊。
  • 2. 拼完图片以后,我们需要把所有用到该图片的地方,去逐个把background-position调整至预期效果。不得不说,这个活也比较繁琐。
  • 3. 网站改版,需要换其他风格的图片的时候,你需要将前面的工作从头再来一遍...
  • 4. 偶尔更换其中的一些icon的时候,你也会觉得比较麻烦。尤其是icon尺寸变了的情况下,直接简单地替换就已经不能满足需求了。当这个需要被替换的icon处于整张大图的中间的时候,你会发现,压根就不能把它抠掉(这样会影响其他icon的正常显示)。所以也就只能退而求其次了,直接append上去吧(妥协了)。几经周折,你会突然发现,原来的那张大图里面已经有了不少现在已经完全用不到的icon了,这个时候,你又该纠结了...

技术都是随着各种问题的抛出而产生的。在遭遇了上述几次折磨以后,你可能就会考虑,能不能有什么省事的方式了。呵呵,这也就到了本篇的主题了。

自动化合并sprite文件

本篇我们会用到
grunt-spritesmith:Grunt task for converting a set of images into a spritesheet and corresponding CSS variables.

首先准备好环境,grunt是必须的。安装方法很简单,本篇只作简单描述,详细内容请移步:http://gruntjs.com/getting-started


//1
$ npm install -g grunt-cli  #install Grunt's command line interface(CLI)

//2
$ npm init   #生成package.json, 当然也可以直接新建文件。

//3
$ npm install grunt --save-dev   #Installing Grunt and gruntplugins

//4
//新建Gruntfile.js文件

//5
$ npm install grunt-spritesmith

//6
$ grunt sprite #运行结果如下

Running result:

Running "sprite:all" (sprite) task
Files "dest/sprites.css", "dest/spritesheet.png" created.

Done, without errors.

Content of Gruntfile.js:

module.exports = function (grunt) {
  // Configure grunt
  grunt.initConfig({
    sprite:{
      all: {
        src: 'WebContent/*.png',
        dest: 'WebContent/dest/spritesheet.png',
        destCss: 'WebContent/dest/sprites.css'
      }
    }
  });

  // Load in `grunt-spritesmith`
  grunt.loadNpmTasks('grunt-spritesmith');
}


一切就绪,下面我们就来看一下效果如何。

根据我们在 Gruntfile.js 里面的配置,我们在 WebContent/dest 目录下面发现:合并完成的图片(spritesheet.png),新的关于icon的css code(sprites.css)。

这正是我们想要的。具体信息如下图所示。

css sprite result

至此,我们可以摆脱那些枯燥无味的体力活了。妈妈再也不用担心图片的替换了。

本例代码在此:https://github.com/JohnnyMa/samples/tree/master/sprite/flow-example

Running result: Running "sprite:all" (sprite) task Files "dest/sprites.css", "dest/spritesheet.png" created. Done, without errors.

31 Dec 2014

Javascript function declaration vs expression

Function declaration VS expression in javascript

本篇主要讨论javascript里面 函数声明和函数表达式的区别。

先从下面这段code说起:


  //Code A
  //Function Declaration
  doSth();
  function doSth() {
    console.log('do something...');
  }

  //Code B
  //Function Expression
  doSth();
  var doSth = function() {
    console.log('do something...');
  }  

函数声明和函数表达式在绝大多数情况下没有区别,唯一的区别在创建Function对象的时机上是不一样的。 上面两段代码看起来是差不多的,但事实上第一段是正确的,而第二段则会报错。

这是因为 JavaScript是一种解释型语言,函数声明会在JavaScript代码加载后、执行前被解释,而函数表达式只有在执行到这一行代码时才会被解释。

所以COde A相当于在执行doSth()前已经建立了一个Function Object并赋给了变量doSth,其对应代码如下:


  var doSth = new Function("console.log('do something...');");
  doSth();

而Code B中的doSth方法是在执行时才被解释的。因此在执行doSth();的时候Function Object和变量doSth尚不存在。因为JavaScript在第一次使用某变量时会建立此变量,所以此处建立变量doSth,但其值是undefined的,未引用任何对象,将其作为函数来调用当然会出错。另外,解释JavaScript时如果某个变量已经存在,则其前面的"var"关键字会被忽略,因此Code B的代码相当于如下:


  var doSth = undefined;
  doSth();
  doSth = new Function("console.log('do something...');");

故会报错。

综上,除了什么时候可以被访问到以外,函数声明和函数表达式的语法其实是等价的。大多数浏览器都支持这两种语法一起使用。e.g.


  //safari is an exception, do NOT support this code.
  //Usually do NOT code like this since we need to take browser compatible into consideration.
  var doSth = function doSth () {
    //...
  }

thanks to http://www.cnblogs.com/leoo2sk/archive/2011/01/16/function-declaration-and-expression.html

10 Mar 2014

前端学习

学习计划

学习目录:

响应式设计
视觉化 
jQuery源码介绍 
Sencha Touch/jQuery Mobile/PhoneGap 
AngularJS
BootStrap
前端性能优化
Grunt/前端流程部署

[DONE] 前端测试  
	https://github.com/JohnnyMa/sample_code.git  qunit_sample

NodeJS
RequireJS/AMD模块化开发
LESS or SASS
跨域处理
前端调试工具(以Chrome为主)
CSS高级教程

学习产出:

1. 适当的实例,to github。
2. 文章总结,to github page

30 Nov 2013

Css Inline Block[2]

[转]之前转过一篇类似的文章,这篇文章可作为一个额外的补充。

曾几何时,display:inline-block 已经深入「大街小巷」,随处可见 「display:inline-block; *display:inline; *zoom:1; 」这样的代码。如今现代浏览器已经全面支持这个属性值了,上面的代码只是为了兼容 IE6、7 而已。那么你真的了解 inline-block 了吗?本文将带你深入剖析该属性值的前世今生,让你更好的理解和运用 inline-block。(本文约定 display:inline-block 简写为 inline-block)

开篇我们来看几个问题:

  • IE6、7 真的不支持 display:inline-block 吗?
  • display:inline-block 后的元素为什么会产生水平空隙,这真的是 bug 吗?
  • 如何更好的解决 display:inline-block 元素间产生的水平空隙?

一、inline-block 前世

1.认知

也许有人问你为何要写「 display:inline-block; *display:inline; *zoom:1; 」 来兼容 IE6、7 时,你会立马答道:因为 IE6、7 不支持 display:inline-block 呗!不知道何时起,惯性思维给开发者带来了这样一个可怕的概念。万物都是辩证的,当你写下这些的时候,可曾怀疑过大众观点真的可靠吗?也许你认为这些无关 紧要,实现效果就好。但是如果不能理解每个属性或属性值的根本,你将永远无法全面的了解它,人云亦云只会让你浅尝辄止,止步不前。那么这里就涉及到所谓的 「CSS 学习瓶颈」的问题了,这个问题张鑫旭《说说CSS 学习中的瓶颈》一文有详细阐述,虽然部分观点我不是很赞同,但是中心思想还是很值得思考的。文中有几个不错的问题这里也列举出来供大家观摩:

  1. line-height:150% 和 line-height:1.5 的区别是?
  2. float 为何会让外部容器高度塌陷?这是 bug?(我的答案在《那些年我们一起清除过的浮动》
  3. vertical-align 的表现为何在IE7, IE8, IE9 下表现不尽相同?其中的渲染机制是?

好了,回到 inline-block 的认知的问题,我的观点是:

IE 从 5.5 开始就已经支持 display:inline-block 了,只是支持的并不是那么完善

在 msdn 微软开发者社区,找到了 IE 从5.5 开始支持 inline-block 的证据:

The inline-block value is supported starting with Internet Explorer 5.5. You can use this value to give an object a layout without specifying the object’s height or width.

这里明确指出:从 IE5.5 开始支持 inline-block。

链接:http://msdn.microsoft.com/zh-cn/library/ie/ms530751(v=vs.85).aspx

那么既然 IE5.5 开始就已经支持了 inline-block,为何我们还要写那么一坨 CSS 呢?同时我们知道 IE6、7 中 display:inline-block 是可以触发 hasLayout 的,触发了 hasLayout 的元素表现出来的特征就是一个独立的矩形容器,可以设置宽高而且不受外部元素的影响,类似于现代浏览器中的 Block formatting contexts (块级格式化上下文)的概念。

下面来做一个详细的测试,分别看看 IE6 中 inline 元素和 block 元素的表现:

1)inline 元素 display:inline-block

IE6 中截图如下:

.dib-inline, .dib-block {
width:100px;
height:30px;
line-height:30px;
text-align:center;
}
.dib-inline {
display:inline-block;
}

测试表明:IE6 中 inline 元素只要触发了 hasLayout 其表现就类似于 inline-block,这里设置 display:inline-block; 或者 zoom:1; 等其他属性值可以触发 hasLayout ,表现出来是一样的。

查看 DEMO

2)block 元素 display:inline-block

IE6 中截图如下:

.dib-inline, .dib-block {
width:100px;
height:30px;
line-height:30px;
text-align:center;
}
.dib-block{
display:inline-block;
}

测试表明:IE6 中 block 元素即使触发了 hasLayout 也不能具有 inline-block 元素不换行的特性。想要 block 元素支持 inline-block 元素的特性,我们可以这样做:

.dib-block {
display:inline;
zoom:1;
}

首先让 block 元素转化为 inline 元素,强制其不换行;然后通过 zoom:1 触发 hasLayout,使其可以设置宽高。修复后的 截图如下:

3)结合现代浏览器

综上,现代浏览器都支持 display:inline-block ,IE6、7 inline 元素也可以达到同样的效果,IE6、7 block 元素需要设置 display:inline; zoom:1; 它们结合在一起便是:

display:inline-block; /* 现代浏览器 +IE6、7 inline 元素 */
*display:inline; /* IE6、7 block 元素 */
*zoom:1;

为了不让支持 CSS2.1 inline-block 的浏览器 重置为 inline,我们针对 IE6、7 做一个 hack。由于现代浏览器也开始支持 zoom 属性,这里只是希望 IE6、7 中生效,所以还是 hack 一下比较合适。至此产生了我们熟悉的兼容各个浏览器的 inline-block 写法。

小结:IE6、7 并不是不支持 inline-block,只是 block 元素需要做一些处理来达到 inline-block 的效果。

2. 到底什么是 inline-block

说了很多,或许很多朋友还不是太明白到底什么是 inline-block?W3C 在 CSS2.1 The ‘display’ property 中描述如下:

This value causes an element to generate an inline-level block container. The inside of an inline-block is formatted as a block box, and the element itself is formatted as an atomic inline-level box.

大致意思就是:inline-block 后的元素创建了一个行级的块容器,该元素内部(内容)被格式化成一个块元素,同时元素本身则被格式化成一个行内元素。

直白一点的意思就是:inline-block 的元素既具有 block 元素可以设置宽高的特性,同时又具有 inline 元素默认不换行的特性。当然不仅仅是这些特性,比如 inline-block 元素也可以设置 vertical-align 属性。简而言之:

inline-block 后的元素就是一个格式化为行内元素的块容器( Block container )

怎么样?听起来还不错吧!

3. inline-block 缘从何起?

前面已经证明了 IE 5.5 开始就支持了 inline-block,那么 IE5.5 是什么时候发布的呢?话说当年网景与 IE 大战,IE5.5 那是何等的风骚……(好吧,此处略去十页)。从维基百科的资料来看,IE5.5 beta1 的发布时间是:1999年12月,最终版本是 2000年7月。那么 W3C 标准中是何时才出现 inline-block 这个值的呢?

CSS1 规范中,「display」的值仅包括: block | inline | list-item | none 。CSS2.1中才添加了 inline-block 属性值。一丝继续舔着手指,用那苦逼的英语水平终于翻到了这份草案:http://www.w3.org/TR/2002/WD-CSS21-20020802/visuren.html#display-prop, 这份草案的日期是 2002年8月2日,纳尼!!!原来我们纠结了半天的 inline-block , IE5.5 至少提前两年就提出来了啊!难道是微软给 W3C 提议后,CSS 2.1才加入的?(不过我看到 W3C 官网有一个关于是否增加 inline-block 的投票)好吧这个问题也许有一天 IE 某个开发者写《 IE回忆录》的时候我们才能了解到其中的内幕。如果找到更早关于 inline-block 的 CSS草案,也麻烦告知一丝一声。好吧,如果你还不相信,打微软官方电话问问吧 800-820-3800(不是 DHC 哦!)。

原来我们一直讨论的 inline-block 在 IE 6、7中和 CSS2.1 中的(现代浏览器所支持的) inline-block 上压根不是一个东东嘛,IE6、7 中的 inline-block 更像是 IE 的私有属性值,他们本身就不具有可比性。简单、绝对的认为 IE6、7 不支持 inline-block 好比一叶障目,看到前面,却看不到后面,太过于片面。诚然,IE6、7 的 hasLayout 给我们带来了很多麻烦,但是不得不承认微软的 IE 在网页多语言文本混排上的先进性,尤其是 CJK 文字和西文的混排,超越其他浏览器至少5年。

总结:

  • IE5.5 后开始支持 inline-block, 但是它所支持的 inline-block 不能等同于 CSS2.1 中的 inline-block,因为 IE5.5 比 CSS2.1 更早提出 inline-block 的概念并作为所谓的私有属性值使用,所以二者表现出来的效果是不完全一致。
  • IE 5.5、6、7 、8(Q)中 block 元素对 inline-block 支持不完整,如果要达到类似的效果,需要先设置为 display:inline,然后使用 zoom:1 触发 hasLayout
  • IE 5.5、6、7 、8(Q)中 inline 元素欲达到 inline-block 的效果只需直接设置此属性值或使用 zoom:1 等均可。

各浏览器对 display 属性的支持情况请参阅:《各浏览器对 ‘display’ 特性值的支持程度不同》

二、inline-block 今生

1. display:inline-block 后的元素为什么会产生水平空隙,这真的是 bug 吗?

这么一个神奇的属性,为何大家一直避而远之呢?这恐怕还得从 inline-block 元素之间产生的水平空隙(间隙)说起吧。

参照 DEMO

  • 现代浏览器中 inline 和 block 元素 display:inline-block 后均会产生水平空隙;
  • IE6、7,IE8(Q)模拟 display:inline-block 后分两种情况:

    IE6、7,IE8(Q)中:inline 元素会产生空隙,block 元素不会产生空隙。

看看 inline 元素默认的表现情况如何?原来默认就有空隙存在!它们是谁?是空白符(white space)!

W3C 9.1 White space 中规定以下元素属于空白符(white space):

  • ASCII 空格 (&#x0020;)
  • ASCII 制表符 (&#x0009;)
  • ASCII 换页符 (&#x000C;)
  • 零宽度空格 (&#x200B;)「这个在闭合浮动中也有运用到」

9.3.2 Controlling line breaks 中进一步阐述:

A line break is defined to be a carriage return (&#x000D;), a line feed (&#x000A;), or a carriage return/line feed pair. All line breaks constitute white space.

For more information about SGML’s specification of line breaks, please consult the notes on line breaks in the appendix.

折行被定义为一个回车符(&#x000D;),一个换行符 line feed (&#x000A;),或者一个回车、换行的组合。所有的折行构成了空白符。

有关 SGML 规范中折行的更多信息,请参阅附录中关于折行的注释。

通常情况下,对于多个连续的空白符(空格,换行符,回车符等),浏览器会将他们合并为一个空白符。CSS 中由 white-space 这个属性来控制:

white-space:normal | pre | nowrap | pre-wrap | pre-line

默认值:normal

  • normal:默认处理方式。
  • pre:用等宽字体显示预先格式化的文本,不合并文字间的空白距离,当文字超出边界时不换行。可查阅 pre 对象
  • nowrap:强制在同一行内显示所有文本,直到文本结束或者遭遇 br 对象。
  • pre-wrap:用等宽字体显示预先格式化的文本,不合并文字间的空白距离,当文字碰到边界时发生换行。
  • pre-line:保持文本的换行,不保留文字间的空白距离,当文字碰到边界时发生换行。

注:IE7及更早浏览器不支持 CSS2.1 新增的 pre-wrap | pre-line。

所以这并不是 inline-block 后产生的 bug,而是因为 inline-block 具有 inline 元素固有的特性。那么为何 IE6、7 block 元素没有产生空隙呢?其实前面也提到了 IE 的 hasLayout,具有独立性,所以产生 hasLayout 的元素之间表现出来互不影响,这也再次表明 IE6、7 中的 inline-block 不能等同于 CSS2.1 中的 inline-block。如果非要说是有 bug, IE6、7 block 元素 inline-block 后不产生空隙才是 bug。

测试表明删除换行符后,inline 元素间的空隙就「消失」了:

2.去掉 inline-block 产生的空隙

为了让各个浏览器表现一致,更好的还原视觉设计搞,很多时候我们需要去掉 inline-block 产生的空隙。

上一节中我们已经知道产生空隙的根本性原因是:

HTML 中的换行符、空格符、制表符等产生了空白符,而这些归根结底都是字符,那么它们的大小都是 受 font-size 来控制的,字体大小直接导致 inline 或者 inline-block 后元素之间空隙的大小,把 inline-block 元素间的空隙认为总是某个固定大小是错误的。

用 GIF 动画的形式来表明对应关系:

很清楚的看到,当 font-size:0 的时候元素间的空隙都为0了,或许到这里你会感到很欣喜了,原来掌握的根本性原因这么简单就搞定了啊!

然,理想是丰满的,现实是骨感的。

大部分浏览器是支持 font-size:0 的。很明显,我们要和 IE 6、7 这两个妖孽进行一番战斗。

font-size:0 的支持情况

1)Chrome

低版本的 chrome 浏览器为了不让文字过小不利于阅读,默认是不支持 font-size:0 的,还好我们有 -webkit-text-size-adjust 这个私有属性来控制,当设为 none 时就支持字体大小为 0 了。我已经记不清楚 chrome 从哪个版本开始支持 font-size:0 了,反正我用 chrome 19 是支持了(有知道的朋友,烦请告诉一丝一声,最好有官方更新说明)。但是,-webkit-text-size-adjust:none; 会直接导致页面文字无法缩放,这对于用户来说显然是不友好的。所以-webkit-text-size-adjust:none; 一定要慎用,确保使用的地方没有大面积的文字。

-webkit-text-size-adjust:none 的使用场景实例参阅:http://vip.etao.com/

2)Safari

Safari 5 依旧不支持 font-size:0 ,不过相信这些浏览器厂商都意识到了这个问题,在 Mac 平台最新的 Safari 6 已经很好的支持 font-size:0 了。

3)Firefox,Opera

经测试,Firefox12,Opera 10 ,这次表现不错,支持 font-size:0 。

4)IE

  • IE8 以上支持 font-size:0;
  • IE6、7 inline 元素 inline-block 后设置 font-size:0 始终有 1px 的空隙。

是不是一下子又开始头疼了?没关系,让我们请出 letter-spacing word-spacing 二位大神。既然空白符也是字符,那么二位大神肯定是可以搞定它们的。

  • letter-spacing : normal | length (检索或设置对象中的文字之间的间隔)
  • word-spacing : normal | length(检索或设置对象中的单词之间插入的空隔)

normal: 默认间隔
length: 用长度值指定间隔,允许为负值。

还等什么,我们赶紧试试吧:

参照 DEMO

  • 第一步:使用 font-size:0经测试发现,chrome、firefox、IE8+、opera,inline 或 block 元素都没有空隙了;
    Safari 5.1.7 由于不支持 font-size:0 ,仍然存在空隙;


    IE6、7、8(Q),inline 元素 inline-block 后始终存在 1px 左右的空隙。
  • 第二步:处理 Safari 不支持 font-size:0 的问题上面已经指出 letter-spacing 是支持负值的,那么这个负值到底取多少合适呢?经过测试得出的结论是:inline-block 产生的空隙与父级元素继承或者设定的 font-family、font-size 有关,通常情况下,12px 大小的 tahoma 字体,inline-block 后元素间产生的空隙(间隙)大约是 5px;
    各个字体详细情况请参阅《inline-block空隙–letter-spacing与字体大小/字体关系数据表》Firefox 中 letter-spacing 负值的绝对值大于空隙大小后,会导致元素整体位置向右偏移;Safari 中 letter-spacing 负值的绝对值大于空隙大小后,内部会发生重叠。
  • 第三步:修复 IE6、7 中始终存在的 1px 空隙
    既然 letter-spacing 已经无能为力了,那就试试 word-spacing 吧,直接设置 word-spacing:-1px。这里需要注意的是,letter-spacing 和 word-spacing 同时使用可能导致冲突,所以我们需要在 IE6、7 中 hack 掉 letter-spacing。最终代码如下:

font-size:0;/* 所有浏览器 */
letter-spacing:-5px;/* Safari 等不支持字体大小为 0 的浏览器 */
*letter-spacing:normal;
word-spacing:-1px;/* IE6、7 */

  • 第四步:子元素重置回正常值
    上述所有操作都是在父元素设置的,那么子元素都会继承这些属性,字体大小为0了,子元素就什么都看不到了,这并不是我们想要的。 同时字符和单词间距我们也要把它重置为默认值。「font-size: 12px; letter-spacing: normal; word-spacing: normal;」
  • 最后:inline-block 更好的复用
    或许你会担心每次我都要去看字体和空隙之间大小的关系吗?其实不然,通常情况下,全局字体都已经在 body 中指定了,根据全局字体设置合适的 letter-spacing 负值即可。如此一来,我们便可以放心大胆的使用 inline-block 了,结合 OOCSS 的思想,可以抽离出两个复用的类,在需要设置 inline-block 元素的父级元素上定义一个「.dib-wrap」,该元素自身定义为「.dib」。这里还有一个问题需要注意的是:由于 inline-block 具有 inline 元素的特性,在垂直方向上很多时候我们并不希望元素以「vertical-align:baseline」方式来呈现,所以在「.dib-wrap」中统一重置为「vertical-align:top」即可。

3. 去除 inline-block 空隙终极解决方案(2012年8月17日更新)

.dib-wrap {
font-size:0;/* 所有浏览器 */
*word-spacing:-1px;/* IE6、7 */
}
.dib-wrap .dib{
font-size: 12px;
letter-spacing: normal;
word-spacing: normal;
vertical-align:top;
}
@media screen and (-webkit-min-device-pixel-ratio:0){
/* firefox 中 letter-spacing 会导致脱离普通流的元素水平位移 */
.dib-wrap{
letter-spacing:-5px;/* Safari 等不支持字体大小为 0 的浏览器, N 根据父级字体调节*/
}
}
.dib {
display: inline-block;
*display:inline;
*zoom:1;
}

其实在 YUI 3 中也全面运用了 inline-block 作为基础布局,YUI 3 是这样解决的:

.yui3-g {
letter-spacing: -0.31em; /* webkit: collapse white-space between units */
*letter-spacing: normal; /* reset IE < 8 */
word-spacing: -0.43em; /* IE < 8 && gecko: collapse white-space between units */
}

.yui3-u {
display: inline-block;
zoom: 1; *display: inline; /* IE < 8: fake inline-block */
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
}

显然,这里纯粹使用了 letter-spacing 和 word-spacing 来控制元素间的空隙,局限性极大,-0.31em 和 -0.43em 只是因为 YUI 3 全局 cssfonts.css 里设置是:「body { font:13px/1.231 arial,helvetica,clean,sans-serif; }」。

当然,如果你坚持使用把 html 写在一行的方式来达到去除 inline-block 空隙的目的,我只能说:一切以牺牲结构来兼容表现的行为都是耍流氓!所以探讨此种方式去除空隙也将是无意义的,不在本文和作者考虑范围之内。

4. 结局——本文产生的一些观点如下:

  • IE5.5 后开始支持 inline-block, 比 CSS2.1 更早提出 inline-block 的概念并作为所谓的私有属性值使用。但是它所支持的 inline-block 不能等同于 CSS2.1 中的 inline-blockIE 5.5、6、7 、8(Q)中 block 元素对 inline-block 支持不完整,因此二者表现出来的效果是不完全一致。
  • 产生 inline-block 空隙的根本性原因是:HTML 中的换行符、空格符、制表符等合并为空白符,字体大小不为 0 的情况下,空白符自然占据一定的宽度,因此产生了元素间的空隙。
  • 慎用 -webkit-text-size-adjust:none,它将会导致页面无法通过缩放来改变字体大小。

三、inline-block 未来

如今,Mac 平台下的 Safari 6 已经支持 font-size:0 了,相信很快 Windows 平台的 Safari 如果发布 5.X 的更新,也会支持字体为 font-size:0 了。等到 IE6、7 灭亡之后,世界就真真儿的美妙了!最后说一点:inline-block 与 float 也是无法直接比较的,请不要再讨论 inline-block 和 float 哪个更好的话题了。inline-block 从 IE5.5 一路走来,存在即是合理,以后有时间在总结一下 inline-block 与 float 的使用场景的区别。

在不改变 CSS 定位机制的前提下,inline-block 应该是首选,而不是以「奇淫技巧」存在的。有感打油诗一首:

网事如烟
CSS 红尘里
inline-block 知多少
你在这头
inline-block 在那头
用与不用
它就在那里
不悲不喜

PS:

  1. 为了更好的排版,本文使用繁体中文引号 「」代替简体中文小蝌蚪引号;
  2. 中英文混排的时候英文首尾各加一个空格;
  3. 以后文章有需要的时候也都将使用 gif 动画配合说明。

测试环境

操作系统版本:  Windows 7 企业版 6.1(内部版本 7600)
浏览器版本: IE6
IE9
Firefox 14.0.1
Chrome 19.0.1084.46
Safari 5.1.7(7534.57.2)
Opera 12.50
最后更新时间:  2012-8-22

original articles: http://ued.taobao.com/blog/2012/08/inline-block/

10 Jan 2013

前端自动化部署

Automating JavaScript builds

sample code: https://github.com/JohnnyMa/sample_code

related articles: http://creynders.wordpress.com/2012/03/14/automating-javascript-builds/#wrapper

06 Jan 2013

Css Inline Block

[转]inline-block comes first.

描述

作为 CSS2 visual formatting model 规范的成员之一,display: inline-block 其实并不是什么新鲜玩意儿,至少比 border-radius 神马的资历要老得多,但是悲剧的是其使用频率 并不十分广泛

我想说,在很多场景中,inline-block 是神器。其实严谨地说,从语义的角度出发,inline-block 在很多时候是必需的 display 方式,而不是一个作为“奇技淫巧”的存在。

表现

还是稍微说一下 inline-block 的表现好了。顾名思义,inline-block 规定让对象呈现出一种容纳于 inline 中的 block box 样式,即,inline-block 元素本身作为 inline 呈现,content 作为 block 呈现。

相应地,有如下肉眼能观察到的 现象

  • 由于 inline-block 的 content 是 block,所以可以设置 width / height——inline 元素是无法设定尺寸的
  • inline-block 接受所有能设定于 inline 的样式,比如 vertical-align
  • 由于对外呈现为 inline 因此 inline-block 的上下 margin 不会和相邻 block 元素的 margin 合并
  • 等等

兼容性

作为早期的 display 属性之一,各大标准浏览器支持良好自不必说(FF2 曾支持不完全,不过这已经是历史了),至于 ie,在 ie8 中有完全的支持,ie7 以下似乎又是悲剧,不过,根据 MSDN 对 display 属性的描述上来看,可以得知 IE 5.5 开始已经支持 inline-block 属性:

The inline-block value is supported as of Internet Explorer 5.5.

只不过需要小 hack 一下:

display: inline;
zoom: 1;

使用场景

在实践中,很多场景下都能动用 inline-block,比较常见的是以下几处。

用来取代 float 布局

假定有如下需求,见图(借用一下蓝色理想的图):

需求

需求

很容易想到对这个 gallery 中的每个 item 定义 float: left,使得它们能够一个接一个地往左漂移。但是 float 这个属性被设计的初衷并不是用来干这些大事情的,它最适用的场景是诸如图文混排的这种小地方,可惜现在很多的责任压在它的肩膀——“我感到压力很大”,float 说。

重新思考,首先不考虑具体的布局实现,观察这个 gallery 是什么。我的观点是,这是一个 逐行呈现 item 的 list。list 中所有的 item 都是平凡的,不会彼此依赖或产生什么副作用,只是简单地一个接着一个地呈现,所谓逐行呈现。那么可以说,这一坨东西是 inline 的。

又考虑到每个 item 不是简单的诸如文本、链接这样的 inline 元素,而是复杂的盒子。那么综合这两点,可以很自然地得出结论——用 inline-block 来呈现这个 list。

vertical-align

y 轴上的对齐问题也很让人头疼,比如垂直居中。

对于垂直居中问题,最直接的想法是借助 display: table-cell 容器,因为表格单元格中的对象都可以设定 vertical-align,可惜 IE7- 再次成为掣肘,再者,table-cell 也 并不十分合乎语义

思考一下,为何官方并没提供一个方法使得 block 元素能够控制 y 轴上的对齐?我的观点是:

  1. 垂直方向的对齐一般需要相邻的参照物,比如定义上下标的 sup sub 标签。而 block 元素占据整行,左右根本没有这种参照物,因此没有 vertical-align 的必要。
  2. 容纳 block 的元素必须也是 block(table-cell 等高级 display 方式先不管),而这个 block 容器默认都是 没有缝隙地 包裹着内部的 block 元素的(除非显式设定包裹容器的 height),既然如此,也就无所谓 block 需要相对于容器在垂直方向定位这种问题了。

虽然上述观点为 W3C 做了一番辩护,但是的确有很多场景需要 block 元素相对于定高容器垂直居中。

那么,由于 inline 元素是可以自然地 vertical-align 的,因此将需要设定垂直居中的对象设定为 inline-block,同时引入一个冗余的兄弟元素同样设定为 inline-block,并且 height: 100%,再设定两者都为 vertical-align: middle。

由于两者对外呈现为 inline,因此能相互在垂直方向居中对齐,同时冗余元素是 100% 的高度,所以我们的目标元素相当于在容器中垂直对齐了。

结论

inline-block comes first!

参考文献

  • http://www.w3.org/TR/CSS2/visuren.html
  • http://msdn.microsoft.com/en-us/library/ms530751(VS.85).aspx

文章出处: http://www.pushiming.com/blog/2010/10/inline-block-comes-first/

07 Nov 2012

Javascript 设计模式

单例 singleton

  
    var singleton = function (fn) {
      var result;
      return function () {
        return result || (result = fn.apply(this, arguments));
      };
    };

    var test = singleton(function() {
      return document.body.appendChild(document.createElement('p'));
    });
  

06 Nov 2012

Skills For Web F2e Developers

从刚开始工作,便一直接触前端开发的工作。

起初觉得前端开发,无非就是做几个网页,加点特效,然后就是几个页面之间的跳转。当然了,可能还需要写点css,js之类的code,等等。

本篇主要是列出一些我曾经学过,以及我想要学习的前端技术。

更新将会一直持续...

01 Nov 2012


Find More >>>