2009年10月31日星期六

Pixel Perfect 1

花了几个小时小小研究了一下中文字体:

  • 使用的是 Arial 字体(不同字体下的结果不同,即便同为中文)
  • 列分别为12px文字、12px链接、12px加粗文字、12px加粗链接、14px文字、14px链接、14px加粗文字、14px加粗链接
  • 行分别为IE6、IE7、IE8、FF3.5

点击图片看大图

结论:

Arail 12px 14px文字
ie6/7 高11px占14px
上0下3px
下划线在13px处
高13px占16px
上0下3px
下划线在15px处
ie8 高11px占15px
上0下4px
下划线在12px处
高13px占16px
上0下3px
下划线在14px处
ff3.5 字11px高15px
上2px下2px
下划线在14px处
高13px占16px
上1px下2px
下划线在16px处
2009年10月25日星期日

[翻译]OOCSS FAQ

在OOCSS中怎么定义“对象”?

对象类似JAVA中的类,保持着OO的特征。

一个CSS对象由4部分组成:

  1. 可能是一个或多个DOM节点的HTML
  2. 由wrapper节点的class名开始的CSS样式声明
  3. 类似于背景图片和显示用的sprites组件以及
  4. JavaScript行为,监听或者与对象关联的方法

这可能令人费解,因为每个CSS class不是其自身必要的对象,但可以是一个wrapper class的一个部件。比如:

<div class="mod">
        <div class="inner">
                <div class="hd">Block Head</div>
                <div class="bd">Block Body</div>
                <div class="ft">Block Foot</div>
        </div>
</div>

对象是一个class为mod的模块。包括4个部件节点(不能独立于模块外,包括2个区块,inner和body,和两个可选择的区块,head和foot)

OOCSS如何提升性能?

OOCSS具有双倍的性能优势:

  1. 高度重用的CSS代码,只需要很少的CSS代码,意味着:
    • 更小的文件,从而更快的传输
    • CSS代码在站点页面中调用的比重增大则有希望被复用或被浏览器缓存
  2. 就浏览器而言更少的重绘和布局计算
    • 单个页面,CSS规则复用的越多,渲染引擎花在“computed values”的计算时间越少
    • 手动增加的"extending"类,重写更少的规则,这再一次意味着引擎做很少去应用规则

要用ID来对内容写样式吗?

当你在同一页面(或者同一站点同时缓存良好)复用一个对象时,这是性能的“免费赠品”。用ID来写样式在同一页面中只能使用一次。@cgriego (twitter) 拿它与singletons比较过,我认为非常精确。可能有些情况下你要用ID定义样式,比如非常特殊的 header menus,此时你可以在用ID来沙箱(译注:动名词)特殊元素并确保此处的代码不会影响站点的其它地方。选择ID而非class前要三思,随着站点的发展,真的很难预料其他人会怎么处理依据你的CSS所构建的HTML。如有选择的余地,尽可能的考虑扩展性。

我正在考虑移除模板head, body, foot中的ID。某些人或许有多个主区域。站点的多个header 和 footer更难以猜测,但我敢打赌肯定有设计师会这样想,所以ID很可能会消失(不太顺,看原文:Someone could have multiple main content areas. Multiple site headers and footers are more difficult to imagine, but I bet there is a designer who can dream up something like that, so the IDs are very likely to disappear.)。

另一方面,ID hooks are great for linking。放在HTML中,不过别用它们来写样式。

设计师可以写OOCSS吗?

是的,设计师出于本能理解对象,比多数人当前书写CSS的方式要形象 — layers of exceptions (想一下,一个老太太吞了一只苍蝇)。事实上,他们爱上OOCSS的原因有两个:

  1. 这使他们能快速创建复杂高点击量的站点。他们不需纠结于不理解的结构除非有足够的能力并充足的了解语法
  2. 学CSS时,他们不需创建丑陋的 “hello world!” 站点。设计师在非常在意的是他们的工作看起来很漂亮。如果必需做一些丑陋的东东,即便是学习之由,他们很快就会有挫败感并灰常的郁闷。OO-CSS 使得他们的工作在每个学习阶段都看起来很漂亮

设计师是聪明D。我们要给他们信任。他们会讲一种不同的,非工程师的语言,但是极客的语言经常以一种丑陋的方式来拒绝人。我们能做的更好。

我是个前端架构师,如何向团队传授OOCSS?

作为架构师,你应该写结构对象;圆角如何创建,为角或其他特性放置表象元素,并处理浏览器差异。新手为这些模块写皮肤(borders, colors, background images, 等等)。

我用OO-CSS方式创建了大批站点(千级的页面,百万级的访问者)。正确的完成后,很好扩展,这意味着新手将处理的个别元件可预见性很强。代码审阅很容易,因为有可接受的方法明确的规则来扩展对象。这种回馈使新开发者快速生产。

我在FullSIX(一个法国的网络营销机构)管理一个前端开发团队,是我工作过的最有才能的。某些时候我们的成功意味着我们将会有更多难以把控的工作。雇佣前端专家非常困难(没有这种学校!),所以我开始对一些对写代码有兴趣的设计师(很少或没有经验)推行一个内部实习项目,一个月就可以作为团队的初级成员工作。

  • 第一周:学习语义并根据现有的CSS创建HTML。学习创建网页:不需要更多的CSS,HTML语法,多个class,验证,语义,介绍代码审阅等
  • 第二周:创建简单的内容对象(标题,列表等),持续一周。学习CSS语法,怎么扩展对象,颜色,字体百分比,等等
  • 第三周:创建区块的皮肤。边框,颜色,背景图片,基本的布局,sprites。让他们同一个回答过n个问题的资深开发者一起工作,使他们少走弯路,他也应是很好的代码审阅者。
  • 第四周:他们已经是团队的皮肤制作成员了。

他们的代码在一个客户的网站上,同资深开发者写的一样好,或许更好因为他们还未学到一些坏习惯:)

入门:如何使用这些文件?

3个文件,libraries.css (reset 及 fonts 取自 yui), grids.css 及 template.css 已完成,其它的还非常不稳定。

  1. 打开template.html并存为新文件
  2. 通过扩展相应的对象来改写列的数量及宽度。站点中只需一个模板,即使你有不同列的页面,因为列也是对象。可以把它们当作任意的区域,可能会有0 ~ n 个左列。查阅模板文档可了解更多
  3. 用栅格来分割内容区域为小的区块。查阅栅格文档了解更多
  4. 添加内容。提示:这也应OO

如何部署在站点上?

注意CSS文件在不断改进中,我会依据接到的反馈进行改变。

我把CSS文件分为了模块,比如栅格和模板。在使用时移除不必要的注释并减少HTTP请求,否则站点会超级慢。这意味着要合并CSS文件为一个稍大的文件。我通过嵌套的注释来组织CSS。最后,作为上线/部署的一部分,用CSS压缩器来移除注释.

可以修改文件,或者用我自己的样式重写吗?

我不会修改grids, template, 或者 libraries。大量测试表明这些已恰到好处。如果要自定义,考虑下面的扩展基本对象

粉红不是我要的颜色!怎么处理content.css?

获取你会想要修改content.css。去吧,改颜色,字体大小,大小写。只需注意这个文件在快速发展,同时我还没有任何文档来说明如何正确的处理。我会这么做,我保证。

我需要不只6种标题(h1~h6),如何增加?

如果需要不只6中标题样式,通过添加一个新class来扩展标题对象。

.category{font-size:108%; font-weight:normal; font-style: normal; text-transform:uppercase; color: #333;}

不要这样做:

#mySaleModule h2, #mySaleModule .h2{font-size:108%; font-weight:normal; font-style: normal; text-transform:uppercase; color: #333;}

如何扩展对象?

如果要扩展对象,比如一个160px的左列,而非默认值,你可以再列上增加额外的class。

<div class="leftCol gMail"> ... </div>

如果默认值和扩展的列宽或者页面不适合你的站点,你可以扩展列来实现自定义的宽度。

myColumn 扩展列对象来实现自定义列宽。

.myColumn{width:400px;}

HTML

<div class="leftCol myColumn"> ... </div>

不要通过重写我的class来实现,而应扩展此框架提供的对象。我提供了列,标题及其他对象。你可以通过增加另外的class(只指定与我的基本对象的不同点)来扩展这些对象。相对而言此处混合比较好。

不要这样做(因为更新我的框架时会有些麻烦):

.leftCol{… 此处自定义CSS …}

没有用到的样式。我的站点没有160px的gmail式的列,可以移除吗?

当然。移除对象或扩展对象非常合理。只需注意在站点发展时,很难预料到其他人用你的CSS创建的什么样的HTML。过早优化很危险。

为什么有一个单独的模板?

在OOCSS中,一个重要的目标是所有的页面创建自一个模板。这简化了CMS开发,因为有一个单独的起始点所有页面可以制作为其他的页面。CMS的用户不会落入已创建的页面不能变种为不同的页面类型的陷阱。OO模板的另一个目标是每一个section(列,header等)控制自己。实际上,这意味着如果要在模板上增加一个左列,只需在HTML中增加此列。你从来不想这样写CSS吧,为了使子元素满足表现而DOM树需要很多改变。对CMS开发来说DOM循环相当的昂贵。

这有语义吗?我要终止像 .formYellow 或 tinyBlueH2 的class命名吗?

OOCSS可以写得有语义也可无语义,取决于你创建模块时用 errorMod 而非 bigRedModule。我选择class名优一些原则(排名不分先后):

  • 短 - 每一字节计算起来,所以尽可能保持class短
  • 清晰 - 可预料的行为/样式要显而易见
  • 语义化 - 对象什么高过看起来像什么。如何用在站点中?
  • 通用 - 名称应该适用于多数站点。过于特殊的名称减少了适用场合或导致语义化的class以非语义化的方式使用
  • 屏幕 - 移动阅读器或打印样式或许有不同的视图,会重写默认的屏幕视图,所以当有冲突时class为屏幕而特殊定义(Different views might be provided by mobile or print stylesheets, however they override the default screen view, so the classes chosen are screen specific when there was a conflict)。这简化了开发。

其它的框架如何?这只能同YUI一起使用吗?

在大量框架的生态系统中,YUI是专业性及可扩展性的一例。我同他们进行了对比,因为我不断受到他们代码和文档的影响。OOCSS不是一个真正意义上的框架,尽管我创建了很多例子,而是一种书写可扩展,健壮,可维护性强的CSS的一种方式。也许最好的比喻是一个新的语言。最终,它是未知的JavaScript库(it is JavaScript library agnostic),我希望贡献代码回YUI及其他框架。

CSS框架太超过!我需要从头开始编写所有代码吗?

每需要一个随机数字都要写一个数字class吗?

CSS is hard, not because it is broken , but because it is a legitimate technology requiring expertise to architect correctly. 为每个站点重复发明轮子非常的愚蠢。

源文件中右列在主列之前,会影响可访问性,SEO吗?

我早期工作过的站点有更标准的模板结构,body上有一个极好的class,依靠这个模板显示或隐藏左右列。自定义CMS的用户要创建一个3列布局的页面时非常沮丧,发现需要两列,找到它们不得不一个个从头开始,因为有多个模板/起始点。你或许注意到主列是个流体列,在左右两列渲染后自适应剩余的空间。

我更喜欢标签排序胜过视觉排序(因为如果标签顺序改变会变得非常怪异),但是我也只想要一个模板。在web开发中经常遇到的是,两个重要的目标发生了冲突,我做判断如何解决。这种情况下标签排序满足视觉顺序除了右列。在当前的代码中,只需在HTML增加一个左或右列,没有别处昂贵的DOM变化。

屏幕阅读器用户有两个选择:

  1. 跳过链接
  2. 导航链接经常为一个链接列表或嵌套的链接列表形式。这非常有趣,因为这允许屏幕阅读者通过屏幕阅读器的特殊操作设置跳过整个列表。

我的意见是对于跳过菜单这两种方式足够了。

每个人似乎都有一个回复观点:SEO,它们都不同,甚至相反。:)基于这个思潮,我倾向于:尤其对含有一定合理数量链接的导航菜单,不用太过在意。曾几何时,我看到过导航链接被索引在搜索结果的内容部分,不过是在几年前了。搜索爬虫非常智能,我已经准备好想当然的认为如果我以语义化,干净的方式标记内容,并非填充一坨垃圾链接,爬虫能区分的出来。

把导航菜单标记为列表,允许屏幕阅读器用户跳过,并提供“跳至内容”链接,对可访问性提供了双倍的备用措施。

为什么用了_ hack?我能把这些代码放在单独的文件中吗?或者添加IE专有的class?

很显然,首先考虑的是尽可能少和长远。

  1. 添加一个单独的样式表奖增加一个额外的HTTP请求,增加整体文件的大小,这早已是浏览器性能的对立点
  2. 我喜欢把一个对象的代码放在一个地方。我认为这有助于减少hack的数量,尤其是当项目随时间而发展
  3. IE6-的开发工具非常原始,这使得hack和普通代码放在一起更加重要。我想能尽快找到一个可以使用属性的IE bug。同样,我认为这有助于减少hack
  4. 规则表明浏览器理解不了的属性会被忽略。对IE6-使用_早已众所周知,可以合理的预料好的浏览器将会忽略这个属性
  5. 使用条件注释意味着每个HTML页面必须包含一个IE专用样式。某一天(我不能等了!)当IE6的市场份额下降至像IE5那样低时,我将去除这些代码,但最后我要做的是触及百级或千级的HTML页面。我宁愿只有依赖CSS hack的CSS,而不是把它放在HTML中。不幸的是,恕我直言,IE6兼容性的末日比我们期望的要更加长远,因为IE中的quirksmode会回落到IE5.5的模式

我认为我的选择有助于减少hack的总体数量,提升性能,并只有忽略不计的风险。话说回来,如果觉得代码中的_令你作呕,你完全可以移至单独的文件。

为了添加表象效果比如边框而使用空 <b> 标签容器对象,会给屏幕阅读器用户带来问题吗?

不会,幸运的是屏幕阅读器会忽略空的b标签。使用这个表象标签(b是加粗)来实现表象具有优势。这个标签可以通过服务器端脚本包含,以至于有一天所有的CSS圆角和阴影都支持了,可以关闭脚本并拥有漂亮,干净,语义化的HTML。

OOCSS声称避免位置依赖的样式。是否意味着我不能使用类似 .myModule .title 的派生选择器?

不使用派生选择器没什么阻碍,只是把它们放在更高的DOM树而已。避免在body或页面中最外层的div放置wide-net class或id,然后对对象产生的变量写大量的样式。这不能复用,同时会使页面渲染变慢。相反,通过直接在对象上添加一个class来增加一个多余的“local”变量(并对其子元素写派生样式)。

一个好的经验是一个容器主体(body of a container)内的任意元素显然是一个单独的对象。

这无可厚非,因为UL显然是一个单独的对象:

#sidebar ul { ... }

因此,carousel显然不是body对象的一部分:

body.browseProducts .carousel 

这是适当的层叠应用,因为子节点真正的是较大的父对象的一部分。b(角)和inner显然隶属于moudle,它们不能独立存在:

.myModule { ... }
.myModule b { ... }
.myModule .inner { ... }

如何贡献?

最好的方法是涉足其中,开始使用代码(libraries, grids, fonts)并提交 bug 报告及功能需求。 我建了一个 OOCSS google group 来进行超过140个twitter字符的讨论。

译注:

OOCSS的官方站点为 http://oocss.org/,有一些例子及下载等。

2009年10月24日星期六

IE 6&7 z-index bug

很老的 bug了,详细描述看下面几篇文章吧:

  1. position:relative/absolute无法冲破的等级
  2. 对《无法冲破的等级》一文的补充
  3. 补遗《无法冲破的等级》

文中给出的方法简单明了,不过有时页面比较复杂,只有通过用JS遍历 position:relative 元素并改变其 z-index 值来解决了。有了 JS 框架则更加简单。《Fixing IE z-index》分别给出了 jQuery 和 MooTools 版本,下面是 jQuery 版本的代码:

$(function() {
       var zIndexNumber = 1000;
       // Put your target element(s) in the selector below!
       $("div").each(function() {
               $(this).css('zIndex', zIndexNumber);
               zIndexNumber -= 10;
       });
});

DEMO 在此

2009年10月21日星期三

[转发]跨国公司招聘

未验证,自己碰运气吧,好像不错( ^_^ )

http://www.carryquote.com/

Job Description

Job Title: Web Developer

Job Location: Shenzhen

Position Requirements/Qualification:

  • University degree or college diploma
  • Fluent knowledge of xHTML, CSS and JavaScript
  • Fluent knowledge of classic ASP (VBScript), specifically integration with MS SQL Server via ADO
  • Knowledge of AJAX is a plus
  • Good knowledge on making websites
  • Able to speak and write English
  • Immediately availability would be preferred

Goblin

Tel 0755-82714350

E-MAIL shuang.zhang@51job.com


2009年10月3日星期六

Web 开发哲学

from Object Oriented CSS

[翻译]Object Oriented CSS

什么是面向对象的 CSS

框架?工具?哲学?

Object-oriented CSS is a coding paradigm that styles "objects" or "modules"—nestable chunks of HTML that define a section of a webpage—with robust, reusable styles.

很像语言的进化

令 CSS 更强大

有什么不同?

ul{...}
ul li{...}
ul li a(②但是,结构在这里){①直至现在,我们只关心这里}

CSS 大约 2005

意大利面条

CSS 大约 2008

稍微好一点

而不是使我们的代码变好

我们筑了大栅栏

性能呢?

公认的毒药中心

网站变慢

文件大小和 HTTP 请求未作优化

CSS 大约 2009

面向对象的CSS

性能的最佳实践、可扩展性、和最重要的-容易使用

  1. 创建对象而不是页面或模块
  2. 在内容对象上设置好的公用默认值
  3. 抽象重用元素
  4. 分离结构和皮肤(为两个 class)
  5. 分离容器和内容(开放的编辑区)
  6. 使用继承
  7. 对看起来 OO 的应用多个 class

9条最佳实践

  1. 组件库:可重用和冗余
  2. 一致性:避免位置依赖(location-dependent)的样式
  3. 抽象化
  4. 优化图像和 sprites
  5. 灵活
  6. 学会爱栅格
  7. 避免非标准的浏览器字体
  8. 避免高度对齐(alignment)
  9. 谨慎卖弄你的技术(choose your bling carefully)

9个陷阱

  1. 位置依赖的样式
  2. 避免指定一个 class 的标签
  3. 避免用 ID 定义主内容区域内的样式
  4. 避免不规则背景上阴影和圆角
  5. 不要拼合所有图片(触发只有少数几个页面)
  6. 避免高度对齐
  7. 文字就是文字,不要做成图片
  8. 冗余
  9. 避免过早优化

创建组件库

可重用的“乐高积木”

重用元素使得它们

性能“免费”

组件就像乐高积木

组合并匹配来创建不同的和有趣的页面

从组件库创建 HTML

新的页面一般不需要额外的 CSS

标题

根据你需要的语义来完成你想要的表现

列表

必须对页面中的所有模块可用

SIDE-WIDE LOGES

  • 标题
  • 列表(比如 action list, external link list, product list, 或 feature list)
  • 模块 headers 和 footers
  • 栅格
  • 按钮
  • 圆角 boxes
  • Tabs, Carousel, toggle blocks

避免重复

在不能增加价值的元件上浪费性能资源

近似相同的模块

h3 和 h5 太相似了

经验法则:

如果一个页面中的两个模块看起来太相似,它们在一个站点中太相似,选择一个!

例子

Yahoo! 个人财经

2+不同的 tab 风格。能用相同的图像吗?

3个元件的轮廓太相似了。选择一个。

模块宽度、背景色或背景图片的改变是个很好的模块复用的例子。

避免位置依赖(location-dependent)的样式

沙盒比意大利面条好,不过引起了性能问题

避免什么?

运行区域

不时...

返回直径

破坏

在 CSS 中我们一直这么做

破坏

不好的:

#weatherModule h3{color:red;}
#tabs h3{color:blue;}
  • h3 的全局颜色未定义,将导致
    • 在新模块中没有定义样式
    • 开发者被迫为相同的样式写更多 CSS

推荐:

h3{color:black;}
#weatherModule h3{color:red;}
#tabs h3{color:blue;}
  • 定义了全局颜色(更好!)
  • Weather 和 tabs 覆盖了缺省的 h3
    • h3 的3种样式在同一模块中不能并存
    • 缺省样式不能用在 weather 和 tabs 除非有更高的优先级
  • Weather 和 tabs 的 h3 永远不能在其他模块中使用

一致性

写更多规则去重写之前的疯狂规则。

比如标题在任意模块的表现是可预见的。

用这个来代替

h1, .h1{...}
h2, .h2{...}
h3, .h3{...}
h4, .h4{...}
h5, .h5{...}
h6, .h6{...}
  • 定义全局值
  • 遵循语义(同时允许灵活的视觉)

需要超过6个标题?

真的吗?没有重复?没有相似的?

仍然需要更多标题?

.category{...}
.section{...}
.product{...}
.prediction{...}
  • 通过对象本身的 class 扩展标题对象
  • 避免使用继承来改变嵌套对象的表现

抽象

复用代码段

重复编码

是抽象不同水准的语义失败所导致的

分离:

  • 容器和内容
  • 结构和皮肤
  • 轮廓和背景
  • 对象和混合物

分离容器和内容

定义每个对象的界限

开放的编辑区

图像或 flash

混合与匹配

容器和内容对象达到高性能设计

分离轮廓和背景

内部透明!

MAKING IT LOOK FAB

需要小心选择像素

考虑 PNG8 来渐进增强

陷阱

可变的或渐变背景

提防圆角后的可变或渐变背景

分离结构和皮肤

两个单独的 class

示例:模块

结构

Sloves borwser bugs, positions presentational elems, and generally does the heavy lifting of CSS

皮肤

弄漂亮些。

目标是非常明确的皮肤,复杂的被结构对象和跨网站共享所吸收(The goal is very predictable skins, complexity is absorbed by the structure object and shared across the site)。

/* ----- simple (extends mod) ----- */
.simple .inner{
  border:1px solid gray;
  -moz-border-radius:7px;
  -webkit-border-radius:7px;
  border-radius:7px;
}
.simple b{
  *background-image:url(skin/mod/corners.png);
}

什么属于皮肤?

皮肤的每个值应该是确定的,容易预测并测量。

  • 颜色
  • 边框
  • 图像

灵活性

可延长的高度和宽度

固定尺寸

Limit the ways in which a module can be resued

学着爱上栅格

坚信其会很美

传授 OO CSS

给设计师和工程师

自然改进

从简单到复杂的任务

一个真实案例

Gonzalo Cordero Yahoo! 前端开发工程师

需要考虑的设计因素

  • 跨浏览器兼容
  • 多语言支持
  • 可访问性
  • 按时完成全部布局和功能

困惑

WEB 妥协

  • 为什么“简单的东东”变复杂?
  • 为什么要妥协?
  • 为什么我们不能依固定的规则和结构?像讲台上那样?

OOCSS SAVES THE DAY

  • 单个简单的标签结构
  • 跨浏览器支持(甚至 IE 5.5!)
  • 非常少的 CSS
  • 可剥离的,容易应用在多个设计上

译注

这不仅仅是 OOCSS!

本文内容来源于:

ytzong 翻译整理,算是把 Yahoo! 女性能工程师 Nicole Sullivan 给“榨干”了,嘿嘿。 另外,之前的《驯服CSS选择器》也是她的作品,富含 OOCSS 的思想。

2009年10月2日星期五

[翻译]加速 JavaScript:处理 DOM

当制作 RIA(Rich Internet Application) 时,我们使用 JavaScript 来改变或增加元素。这是靠 DOM (Document Object Model 文档对象模型) 来完成的,这是如何影响 app 的速度呢?

处理 DOM 会使浏览器 reflow (浏览器决定如何显示东东的进程)。直接操作 DOM,改变元素的 CSS 样式,改变浏览器窗口的大小会触发 reflow。访问元素的布局属性(如 offsetHeight 和 offsetWidth) 会触发 reflow。由于每次 reflow 需要时间,所有我们能减少 reflow 越多,app 越快。

处理 DOM 不仅操作页面中存在的元素还包括创建的元素。下面四个部分覆盖了 DOM 操作和 DOM generation,减少浏览器中 reflow 触发的次数。

CSS class 切换 DOM 操作

这个部分我们改变一个元素和它后代的多个样式属性,只触发一次回流。

问题

我们写一个函数来改变一个元素中的所有链接的 className 属性。可以通过简单遍历每个 a 并更新他们的 className 属性。

function selectAnchor(element) {
  element.style.fontWeight = 'bold';
  element.style.textDecoration = 'none';
  element.style.color = '#000';
}

解决方法

我们可以增加一个设置所有样式属性的 class 来解决这个问题。通过给元素增加这个class,现在我们只触发一次浏览器 reflow。同时分离了表现和行为(译注:这个在我之前的《body标签class属性的妙用(Google Reader前端简单分析)》中也有提及)

.selectedAnchor {
  font-weight: bold;
  text-decoration: none;
  color: #000;
}

function selectAnchor(element) {
  element.className = 'selectedAnchor';
}

Out-of-the-flow DOM 操作

这个部分我们创建多个元素并把它们插入 DOM 只触发一次 reflow。使用了叫做 DocumentFragment 的东东。我们在 DOM 外创建一个 DocumentFragment (所以是 out-of-the-flow)。之后创建并添加进多个元素。最后,我们移动该 DocumentFragment  中的所有元素到 DOM,这只触发一次 reflow。

问题

让我们写一个改变一个元素中所遇链接的 className 属性的函数。通过简单的遍历每个 a 并更新他们的 href 属性。问题在于,每个 a 会触发一次 reflow。

function updateAllAnchors(element, anchorClass) {
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
}

解决方法

解决这个问题,可以从 DOM 中移除元素,更新所有链接,之后插入回元素到原始位置。为此,我们写一个可复用的函数,不仅从 DOM中移除元素,并且返回一个将会插入回元素至元素位置的函数。

/**
 * Remove an element and provide a function that inserts it into its original position
 * @param element {Element} The element to be temporarily removed
 * @return {Function} A function that inserts the element into its original position
 **/
function removeToInsertLater(element) {
  var parentNode = element.parentNode;
  var nextSibling = element.nextSibling;
  parentNode.removeChild(element);
  return function() {
    if (nextSibling) {
      parentNode.insertBefore(element, nextSibling);
    } else {
      parentNode.appendChild(element);
    }
  };
}

现在我们用这个函数去更新一个元素中的所有链接,这 out-of-the-flow,并且在移除元素时和插入元素时只触发一次 reflow。

function updateAllAnchors(element, anchorClass) {
  var insertFunction = removeToInsertLater(element);
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
  insertFunction();
}

单个元素的 DOM Generation

这个部分我们创建并添加一个元素至 DOM 中,仅触发一次 reflow。创建元素后,在新元素添加至 DOM 前做好所有的改变。

问题

写一个添加新a 元素至父元素的函数。这个函数让你为该链接提供 class 和 文本。我们可以创建元素,添加进 DOM,之后设置这些属性。这触发3次 reflow。

function addAnchor(parentElement, anchorText, anchorClass) {
  var element = document.createElement('a');
  parentElement.appendChild(element);
  element.innerHTML = anchorText;
  element.className = anchorClass;
}

解决方法

最后插入这个子节点至 DOM。这触发一次 reflow。

function addAnchor(parentElement, anchorText, anchorClass) {
  var element = document.createElement('a');
  element.innerHTML = anchorText;
  element.className = anchorClass;
  parentElement.appendChild(element);
}

尽管如此,如果我们要添加大量的链接至一个元素时这有一些问题。通过这个方法,每添加一个链接触发一次 reflow。下一部分解决这个问题。

DocumentFragment DOM Generation

这个部分,我们创建多个元素并把它们插入 DOM 触发一次 reflow。使用了一个叫做 DocumentFragment 的东东。我们在 DOM 外创建一个 DocumentFragment (所以是 out-of-the-flow)。之后创建并添加多个元素进来。最后,把所有 DocumentFragment 中的元素移至 DOM 并只触发一次 reflow。

问题

写一个添加10个连接至一个元素的函数。如果只是简单的添加每个新链接至这个元素,会触发10次 reflow。

function addAnchors(element) {
  var anchor;
  for (var i = 0; i < 10; i ++) {
    anchor = document.createElement('a');
    anchor.innerHTML = 'test';
    element.appendChild(anchor);
  }
}

解决方法

为了解决这个问题,我们创建一个 DocumentFragment 并添加每个新链接至此。当我们使用appendChild 添加 DocumentFragment 至元素时,所有 DocumentFragment 的子节点 实际上添加进了这个元素中。这只触发一次 reflow。

function addAnchors(element) {
  var anchor, fragment = document.createDocumentFragment();
  for (var i = 0; i < 10; i ++) {
    anchor = document.createElement('a');
    anchor.innerHTML = 'test';
    fragment.appendChild(anchor);
  }
  element.appendChild(fragment);
}

[翻译]用Page Speed Activity捕获并分析浏览器渲染

安装

Page Speed 是一个 Firebug/Firefox 扩展,安装地址:http://code.google.com/speed/page-speed/download.html

背景:逐步呈现

快速的网页逐步呈现。即随浏览器的加载而逐步显示其内容。一个逐步呈现的页面给浏览者页面在加载的视觉回馈,并尽快给给用户请求信息。Google Yahoo 都建议逐步呈现页面,比如把 CSS 写在页面 head 中。

对多绝大多数页面来说,有一些很好的实践来优化逐步呈现。一个快速的页面应该先给用户呈现可见的内容(译注:浏览器第一屏),随后呈现视线外的内容(即当前滚动区域外的内容)。一个快速的页面会在加载和渲染重量级的资源(如图片和视频)之前加载和渲染轻量级的资源(如文字)。

一些众所周知的技术会禁止逐步呈现。使用大量表格,甚至用来布局,在一些浏览器中会使逐步呈现失效。应用样式表在页面后方,即便这些样式表在开始的页面加载中使用不到,也会阻止逐步呈现。

用Page Speed Activity捕获浏览器渲染

很难决定一个页面是否要进行逐步呈现优化。大多数页面对肉眼来说呈现太快,以至于意识不到个别的渲染事件(尤其是在高速网络连接下),而且看不到屏幕区域外的内容是否呈现了。

幸运的是,Firefox 3.5 支持捕获浏览器渲染事件。Page Speed Activity 面板用这个功能可为页面呈现过程录制一个“幻灯片”。幻灯片的每一单元显示了哪一个屏幕区域被渲染(黄色),哪些区域在屏幕外(灰色)用户看不到。

启用渲染快照

由于捕获渲染快照会增加一些开销并拖慢浏览器,Page Speed Activity 屏幕快照默认是禁用的。启用渲染快照,确保Activity 下拉菜单中的 "Paint Snapshots (slow)" 被选中。

Activity 面板中的渲染快照

一旦渲染快照启用,就开始用 Activity 面板录制。Activity 面板将在时间线上显示网络,缓存,和 JavaScript 事件。渲染快照幻灯片出在 Activity 面板右侧。渲染快照会按捕获顺序绘制,最早的快照在顶部。拖动右侧的滚动条可看到所有的快照。

快照回放示例

译注:请到这里看演示

在这个例子中,我们可以看到以渲染快照慢动作回放的 Google 搜索结果页面的逐步呈现。这些快照用 modem 连接捕获。

用 Page Speed Activity 回放这个例子的渲染快照,请点击 Play Paint Events按钮。

在快照中,用户可见的区域为白色,用户不看见的区域(当前浏览器滚动区域之外)用灰色遮罩。每个快照用黄色遮罩显示重绘区域。

请注意,可见区域的文本内容首先呈现,之后是屏幕外的文本内容。通过先呈现屏幕内的可见区域,用户尽早获取了尽可能多的有用信息。

文本内容呈现后,图像内容接着呈现。推迟图像内容呈现直到文本内容下载完成并呈现完成,这使得浏览器尽早的显示文本内容,再一次尽早给用户尽可能多的有用信息。

在页面标签中给所有图像指定宽度和高度属性,浏览器不需在图像加载后 reflow 页面。尽管没有严格的与逐步呈现有关,指定图像的宽高会带来更好的用户体验,在页面加载时页面内容不会在附近移动。

最后,注意图像自身的逐渐呈现。允许用户在全部图像完成加载前看到图像内容。现代浏览器在 HTML 中用 <img> 标签逐步呈现图像。相反,许多浏览器中用 CSS 的background-image 属性不会逐步呈现。为了逐步呈现图像,用 HTML 的 <img> 标签来代替 CSS 的 background-image 属性。


总结

使用 Page Speed Activity 面板的渲染快照功能,我们可以清晰的观察页面的逐步呈现行为。这使得网页开发者可以为逐步呈现而优化页面,这是快速网页非常重要的特性之一。

延伸阅读

用 HTML 5 来提升性能

Google 网站管理员 Jens Meiert 发表了一篇《Using HTML 5 for performance improvements》,大部分内容在《Reducing the file size of HTML documents》(中文版)中都有提及,新增的部分为async (和 defer),以下为该段的翻译:

asyncdefer 使用在 script 元素上。

要解释为什么这些属性会提升性能,最好是看一下未使用时发生了什么 -- 各自的脚本在用户代理(浏览器,下同)继续解析页面前将被提取并直接执行,有时这个行为是我们想要的,有时不是。

新的 async 属性允许各自的脚本在可用时异步执行。HTML 4 中已经包含 defer 属性,指出 defer “对用户代理提供一个暗示:脚本不产生任何文档内容(比如,在 Javascript 中没有 document.write ),这样,用户代理会继续解析并渲染”。

如果不使用 async 属性而只使用 defer,脚本在页面解析完成时已经执行。即使指定了 async 属性,也可同时指定defer 属性。这使得那些旧的只理解 defer 的浏览器来回退 defer 行为而非默认的同步阻塞行为。

2009年10月1日星期四

[翻译]驯服CSS选择器--健壮我们的样式表

CSS 文件的大小和所引起的 HTTP 的请求数

是 CSS 性能的最关键因素

回流(reflow)和渲染时间

(非常!)没那么重要

副本(duplication)比陈旧的规则(stale rules)更糟糕

因为我们有工具处理后者

定义缺省值

不要在每处都重复编码

不好的:

#weatherModule h3{color:red;}
#tabs h3{color:blue;}

推荐:

h1, .h1{...}
h2, .h2{...}
h3, .h3{...}
h4, .h4{...}
h5, .h5{...}
h6, .h6{...}

用单独的 class 来定义结构

不要在每处都重复编码

使用 class

而不是元素选择器

不好的:

div.error{...}

推荐:

.error{绝大多数代码写在这里}
div.error{单独定义}
p.error{单独定义}
em.error{单独定义}

避免使用元素选择器

初始化除外

不好的:

div{...}
ul{...}
p{...}

推荐:

.error{...}
.section{...}
.products{...}

给规则同样的权重

使用级联去重写先前的规则

不好的:

.myModule .inner b{...}
.myModule2 b{...}

推荐:

.myModule b{...}
.myModule2 b{...}

保守的使用 hack

不好的:

.mod .hd{...}
.ie .mod .hd{...}
.weatherMod .hd{...}

推荐:

.mod .hd{color:red;_zoom:1;}
.weatherMod .hd{...}

译注:此点来自The Cascade, Grids, Headings, and Selectors from an OOCSS Perspective, Ajax Experience 2009第96P,为作者在 Ajax Experience 2009 上所做的补充。

避免指定位置

应用 class 在你想要改变的对象上

不好的:

.sidebar ul{...}
.header ul{...}

推荐:

.mainNav{...}
.subNav{...}

避免太过特别的 class

它们是都有语义的,而且有限制

不好的:

.ducatiMonster620{...}
.nicolesDucatiMonster620{...}

推荐:

.vehicle{...}
.motorcycle{...}

避免单独的定义

id 在每个页面只能使用一次

不好的:

#myUniqueIdentifier{...}
#myUniqueIdentifier2{...}

混合使用

避免重复编码

封装

不要直接访问对象的子节点

不好的:

.inner{...}
.tr{...}
.bl{...}

推荐:

.weatherMod .inner{...}
.weatherMod .tr{...}
.weatherMod .bl{...}