原文地址:http://www.stevesouders.com/blog/2013/04/26/i/

我的大部分性能优化工作都集中在JavaScript和CSS上,从早期的Move Scripts to the BottomPut Stylesheets at the Top规则。为了强调这些规则的重要性,我甚至说过,“JS和CSS是页面上最重要的部分”。

几个月后,我意识到这是错误的。图片才是页面上最重要的部分。

我关注JS和CSS的重点也是如何能够更快地下载图片。图片是用户可以直观看到的。他们并不会关注JS和CSS。确实,JS和CSS会影响图片内容的展示,尤其是会影响图片的展示方式(比如图片轮播,CSS背景图和媒体查询)。但是我认为JS和CSS只是展示图片的方式。在页面加载的过程中,应当先让图片和文字先展示,而不是试图保证JS和CSS更快下载完成。

我优化JS和CSS的目的就是让页面尽快渲染。

开始渲染的时间过迟

伴着关注渲染时间的念头,我查询了HTTP Archive来了解我们的页面开始渲染的时间。下面是一些衡量的数值:

  • TTFB,第一个HTML文档数据包到达的时间
  • 开始渲染的时间
  • 页面onload触发的时间

从世界最快的30万个地址的测量值中,我提取出其中的50%和90%部分。如下面展示的一样,在页面加载的前三分之一段时间内,没有任何渲染动作。

表格 1. 页面加载过程中的各个时间点
TTFB 开始渲染 onload
50th percentile 610 ms 2227 ms 6229 ms
90th percentile 1780 ms 5112 ms 15969 ms

预加载

页面等待渲染的时间是整个页面加载时间的三分之一,这个事实让人出乎意料。HTTP Archive上的数据说明,页面花费了32%到36%的时间来等待渲染开始。但是只需要10%的时间来获取第一个字节。因此,浏览器在22%到26%的时间段内,虽然已经接受到了数据,但是却不做任何渲染。在这段时间内浏览器通常都是在下载解析脚本和样式—这两者都会阻碍页面的渲染。

曾经,浏览器在这个时间段内(从接受到第一个字节到渲染开始)是处于空闲状态的。这是因为旧的浏览器中,一个脚本的下载会阻塞其他所有的资源的下载,比如IE6&7。浏览器厂商意识到虽然浏览器需要等待脚本下载执行完成后才能构建DOM,但是没有理由阻塞页面其他资源的并行下载。在2009年的IE8之后,浏览器预加载其他资源的请求。研究表明,预加载让页面加载速度快了20%。今天,所有主流浏览器都支持预加载。在这些浏览器数据中,我展示了每个主流浏览器最早支持预加载的版本。

(顺便说一句,我认为预加载是最有效的性能提升方式。设想现在的浏览器中脚本下载会阻塞其他资源,面对页面上如此庞大的脚本数量,页面的性能会糟糕到哪个程度)

预加载和响应式图片

这时我们又要回到 Jason Grigsby的一条tweet:

我不得不承认一点。我试图推进响应式图片,并且越来越倾向于鼓励开发者来使用JS阻止预加载。

Jason指的“响应式图片”是一项技术,使用脚本来生成图片。通常这个技术用于实现图片对分辨率的适应。一个例子是Picturefill。当你将“预加载”和“响应式图片”合起来思考——预加载会提前加载图片的SRC,但是响应式图片技术通常又没有SRC,或者只是有一个1×1的替代图片。这两项技术之间有冲突。下面有一些权衡:

  • 不要使用响应式图片,这样浏览器就可以利用预加载尽快下载图片,但是图片可能会比需要的尺寸大很多,而且会耗费更长的时间来下载(耗费更多的流量)
  • 使用响应式图片,由于没有利用预加载,则意味着图片会在脚本下载执行后下载,这个时候IMG标签都已经创建好了。

接着Jason在后一条tweet中说:

让我觉得不舒服的是,大部分结论都没有经过测试。只是一些理论,而不是数据。

我并没有数据来比较这两个方式,但是HTTP Archive中开始渲染的时间占总加载时间的三分之一也说明了一些问题。似乎渲染确实被脚本阻塞了,也就是说IMG标签还没有被创建。那么在1/3点后的时间里,IMG标签会被解析,然后JS和CSS才会执行并开始下载需要的图片。

我认为,在页面加载过程中初始化图片请求太晚了,并且相对使用预加载后的效果,页面的渲染时间确实被推迟了。再次声明,我没有数据来对比这两项技术。同时,我也不确定在markup技术的响应式图片中使用预加载会有怎样的改观。(Jason有篇博客文章有相关的内容,The real conflict behind <picture> and @srcset

理想情况下,我们利用markup解决了预加载和响应式图片之间的冲突问题。直到那时,我依然担心这样的技术在开发社区中大量使用会让响应式图片付出预加载失效的代价。我希望浏览器可以增强预加载的效果,那么现在和未来里网站就能够充分利用预加载的功能。

/***** Selector Hacks ******/
/* IE6 and below */
* html #uno { color: red }
/* IE7 */
*:first-child+html #dos { color: red }
/* IE7, FF, Saf, Opera */
html>body #tres { color: red }
/* IE8, FF, Saf, Opera (Everything but IE 6,7) */
html>/**/body #cuatro { color: red }
/* Opera 9.27 and below, safari 2 */
html:first-child #cinco { color: red }
/* Safari 2-3 */
html[xmlns*=""] body:last-child #seis { color: red }
/* safari 3+, chrome 1+, opera9+, ff 3.5+ */
body:nth-of-type(1) #siete { color: red }
/* safari 3+, chrome 1+, opera9+, ff 3.5+ */
body:first-of-type #ocho { color: red }
/* saf3+, chrome1+ */
@media screen and (-webkit-min-device-pixel-ratio:0) {
 #diez { color: red }
}
/* iPhone / mobile webkit */
@media screen and (max-device-width: 480px) {
 #veintiseis { color: red }
}
/* Safari 2 - 3.1 */
html[xmlns*=""]:root #trece { color: red }
/* Safari 2 - 3.1, Opera 9.25 */
*|html[xmlns*=""] #catorce { color: red }
/* Everything but IE6-8 */
:root *> #quince { color: red }
/* IE7 */
*+html #dieciocho { color: red }
/* IE 10+ */
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
 #veintiun { color: red; }
}
/* Firefox only. 1+ */
#veinticuatro, x:-moz-any-link { color: red }
/* Firefox 3.0+ */
#veinticinco, x:-moz-any-link, x:default { color: red }
/* FF 3.5+ */
body:not(:-moz-handler-blocked) #cuarenta { color: red; }
/***** Attribute Hacks ******/
/* IE6 */
#once { _color: blue }
/* IE6, IE7 */
#doce { *color: blue; /* or #color: blue */ }
/* Everything but IE6 */
#diecisiete { color/**/: blue }
/* IE6, IE7, IE8, but also IE9 in some cases :( */
#diecinueve { color: blue\9; }
/* IE7, IE8 */
#veinte { color/*\**/: blue\9; }
/* IE6, IE7 -- acts as an !important */
#veintesiete { color: blue !ie; } /* string after ! can be anything */
/* IE8, IE9 */
#anotherone {color: blue\0/;} /* must go at the END of all rules */
/* IE9, IE10 */
@media screen and (min-width:0\0) {
 #veintidos { color: red}
}
min-height:300px;
//height:300px; /* matches only IE6, IE7 */
selector, x:-IE7 { } /* IE7 only */
selector,{} /* lte ie7 */
.suckyie6.someClass {} /* lte ie6 */
/* Property prefix hacks */
/* IE6 only - any combination of these characters */
_ - £ ¬ ¦
/* IE6/7 only - any combination of these characters */
! $ & * ( ) = % + @ , . / ` [ ] # ~ ? : < > |
/* @media hacks */
/* IE6/7 only (via Keith Clarke) */
@media screen\9 { }
/* IE6/7/8 (via Keith Clarke) */
@media \0screen\,screen\9 {}
/* IE8 (via Keith Clarke) */
@media \0screen { }
/* IE8/9 */
@media screen\0 { }
Posted in CSS.

在HTML5中,引入了两个新的标签,aside和figure,都可以作为主要内容的附加内容存在。

aside简介

最早的时候,aside的定义是用于作为主要内容的附加内容,但是和内容所表达的意思,也就是内容本身是没有任何关系的,常见的比如文章的内部导航链接或者词汇表什么的,就适合用aside来表示。但是比如整个页面的附加内容,类似侧边栏等等,就并不太适合使用aside表示。

不过后来很多开发者对这个定义表示了质疑,之后标准制定者也认同了开发者的质疑,增加aside的含义:aside也可以不在article内部,用于作为整个页面的附加内容。

下面有一个非常典型的例子:

<body>
  <header>
    <h1>My Blog</h1>
  </header>
  <article>
    <h1>My Blog Post</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
    eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
    <aside>
      <!-- 由于aside在article内部,解析器正确的理解方式就是将aside的内容理解为直接和article本身相关
      -->
      <h1>Glossary</h1>
      <dl>
        <dt>Lorem</dt>
        <dd>ipsum dolor sit amet</dd>
      </dl>
    </aside>
  </article>
  <aside>
    <!-- 这里的aside是在article外部。它的内容和页面相关,但是和article没有直接的联系 -->
    <h2>Blogroll</h2>
    <ul>
      <li><a href="#">My Friend</a></li>
      <li><a href="#">My Other Friend</a></li>
      <li><a href="#">My Best Friend</a></li>
    </ul>
  </aside>
</body>

对于aside,最重要的点就是将其和侧边栏sidebar的概念分开,对于这类标签的使用,不应该单纯从视觉的角度来考虑标签的用途,对于标签本身来说,内容的相关性是大于其视觉表现的。

figure和figcaption简介

figure标签用于突出图表,插画,照片或者代码用例,通常和figcaption一起使用。比如可以用来表示视频区块,canvas区块等等。

标准中是这么定义的:

The figure element represents a unit of content, optionally with a caption, that is self-contained, that is typically referenced as a single unit from the main flow of the document, and that can be moved away from the main flow of the document without affecting the document’s meaning.

大致的意思就是:figure是一个内容单元,在整体上是作为主要内容的一个部分,并且可以在不影响整体内容的情况下移动位置。

而figcaption是和figure搭配使用,并且是用于描述整个figure的内容,所以一个figure只会对应一个figcaption。

一个简单的例子:
W9xzh HTML5标签中aside和figure的区别

对应的HTML代码:

<figure>
  <img src="/kookaburra.jpg" alt="Kooaburra">
  <img src="/pelican.jpg" alt="Pelican stood on the beach">
  <img src="/lorikeet.jpg" alt="Cheeky looking Rainbow Lorikeet">
  <figcaption>Australian Birds. From left to right, Kookburra, Pelican and Rainbow Lorikeet. Originals by<a href="http://www.flickr.com/photos/rclark/">Richard Clark</a></figcaption>
</figure>

aside和figure之间的区别

其实看完简介,两个元素之间的区别已经比较明显了:

  • aside和内容有关,但是并不是内容的一部分
  • figure是作为内容的一部分,但是它所在的位置不会影响内容的表达

参考资料

前端的基本知识,之所以称为基本知识,也就应该是作为一个合格的前端开发工程师想都不用想都应该知道的内容,下面简述知识点,顺便也让自己温习下。

DOM结构

dom结构表达了页面上众多元素之间的关系,dom树之间的关系有很多,我们在下面一一描述。

parent —— 父节点

parent表示了节点的直接父节点,可以通过element.parentNode进行访问,如果节点不存在则返回null。

child —— 子节点

child表示的是节点的直接子节点,和父节点不同的是,子节点可能有多个,所以我们在DOM中访问子节点的时候,返回的是子节点列表。

我们可以通过element.childNodes来访问所有节点列表(NodeList),也就是会获取到文本节点等等其他非元素节点,而element.children则会返回所有的元素节点(HTMLCollection),可惜在IE6到8下会把注释节点也获取到。

如果我们希望访问第一个子节点和最后一个子节点,DOM也提供了比较方便的方式直接进行访问。

console.log(list.firstChild); //获取到子节点列表的第一个节点
console.log(list.lastChild); //获取到子节点列表的最后一个节点
console.log(list.firstElementChild); //获取到子节点列表的第一个元素节点
console.log(list.lastElementChild); //获取到子节点列表的最后一个元素节点

demo地址

通常,我们都不太会用到文本节点等等内容,那么我们希望用到的更多是list.firstElementChild这样的方法,不过IE6到IE8还是不支持,所以在获取子节点的时候,还需要检查节点类型是不是为元素节点,确保取到的元素是元素节点。

sibling——相邻关系

sibling表示的就是同一层级下相邻节点的关系,和child节点的获取类似,也分为带element检测和不带检测的两种。

console.log(sister.previousSibling); //获取节点列表的前一个节点
console.log(sister.previousElementSibling); //获取节点列表的前一个元素节点
console.log(brother.nextSibling); //获取节点列表的后一个节点
console.log(brother.nextElementSibling); //获取节点列表的后一个元素节点

demo地址

总结

由上可知,节点之间的关系,可以分为child,parent和sibling三种关系,两个节点之间复杂的关系都可以由这三者组合而成。实际应用的时候,需要特别注意节点和元素节点之间的区别,出于渐进增强的考虑,我们推荐优先使用带element检测的API,如果不支持,再使用普通的API,并自己编写元素节点的判断。

DOM操作

DOM操作包括添加、移除、移动、复制、创建和查找节点,由于DOM操作实际上是JavaScript引擎和DOM模块之间的操作,所以减少相互之间的调用也是性能优化中比较重要的一部分。

添加节点

添加节点的方式有很多,关于如何提高节点添加性能的文章也很多,不赘述。对于相对较大的HTML片段添加,我们主要采用的方法有三种:

  • 直接拼接HTML字符串,使用innerHTML插入
  • 通过先document.createElement创建一个容器,然后往容器里添加内容,最后appendChild
  • 使用document.createDocumentFragment创建一个DOM片段,添加内容后appendChild到DOM中。

三种方式比较类似,可以自行选择适合自己的方式。

移除节点

标准的移除节点的方式是调用Node.removeChild,这个方法会返回被删除的节点。需要注意的是,通过此方式删除的节点还是会存在于内存中,通过使用removeChild返回的节点引用,你还是可以对其进行操作。

还有一种移除节点的方式是Node.innerHTML = ”,不过使用上没有removeChild适应性好,innerHTML在某些情况下还是有些限制。

你也可以使用Node.replaceChild来删除一个节点,如下:

var brother = document.getElementById('brother');
var s = document.createTextNode('');
brother.replaceChild(s, brother.firstElementChild);

不过没有什么特别的地方,怎么用看个人爱好了。

注意点:删除节点之前记得把之前绑定的事件也解绑掉,否则无法回收事件句柄产生的内存。

移动节点

移动节点的最常用的方式是使用appendChild。还有一个方法是insertBefore,使用起来和replaceChild这类API差不多。

var insertedElement = parentElement.insertBefore(newElement, referenceElement);

这两个方法,通过和其他关系属性的组合,可以演变出jQuery中的各个文档处理方法。

  • append和appendTo,直接调用appendChild
  • prepend和prependTo,调用insertBefore,插入到第一个元素节点的前面
  • after和before,insertAfter和insertBefore,从parentNode处调用insertBefore实现

复制节点

原生的复制节点API只有node.cloneNode(deep)。deep用于指定是否克隆子节点,默认情况下的值各个浏览器表现不太一致,建议总是把deep的值写上。

原生的clone方法是不会克隆节点上的事件的,如果deep是false,不会克隆子节点,当然,这个子节点也包括文本节点。

包裹节点

包裹节点并不是DOM原生支持的,但是这个方法还是比较常用,可以说说实现方式。

我们要在节点A的外面包裹一层节点B,那么首先我们将元素B插入到A的前面或者后面,然后找到B元素里最里面的元素,再将A插入到B里面最深处的元素。

查找节点

查找节点是一个很考验性能和算法的操作,特别是在低级浏览器上实现高级选择器。对于原生代码来说,直接调用选择器的优先顺序是getElementById,getElementsByTagName,然后是getElementsByClassName,还有querySelector,最后再用代码来筛选。

对于日常使用的话,只需要记住简单的选择器一定比复杂的选择器性能高就对了。

这是一个简单的性能测试,demo

总结

操作节点的时候,最需要注意的有两点:

clone和replaceChild,还有removeChild等方法都会返回被删掉,替换或者克隆的节点,这个节点实际上并没有被真正删除,依然会存在于内存中。

操作节点,特别是删除节点的时候,注意该节点所绑定的事件,记得在移除前解绑事件,防止内存浪费。

事件模型

事件这个比较复杂, 非三言两语能讲清楚,大致讲讲吧。

事件相关的方法

addEventListener和removeEventListener是标准提供的绑定/解绑事件方法,而attachEvent和detachEvent则是IE提供的方法,之前各自分工,不过到了IE9和IE10里,同时提供了这两个方法,是需要大家注意的。

标准的事件绑定,当绑定多个事件的时候,是先进先出的,而IE下顺序比较混乱,总体来说是后进先出的,解决方案么,就是自己搞个队列来调用这些函数,确保函数的执行顺序。

事件的冒泡和捕获

在标准事件模型下,浏览器会先从最外层的节点开始往触发事件的节点进行事件的捕获,然后在从触发事件的节点对外冒泡事件。而IE678只完成了事件的冒泡。

冒泡和捕获通常用于代理事件,避免多次绑定,对于focus,blur这种无法冒泡的事件,则可以在addEventListener的时候第三个参数设为true来捕获。

创建事件/触发事件

我们可以使用document.createEvent来创建一个事件,然后使用dispatchEvent来触发,比如模拟点击的demo

自定义事件CustomEvent是作为createEvent的一部分存在,可以通过createEvent来创建,也可以直接new window.CustomEvent。

不过由于IE6/7/8不支持相关的API,所以大部分库都选择自己搞一个事件对象来处理自定义事件,实际上这样也比直接调用浏览器的API更加便于管理。

总结

浏览器上的事件处理是比较棘手的一件事,包括mouseover/mouseout的冒泡问题,滚轮事件,表单交互,各种事件因为浏览器遗留问题留下了不少的麻烦,不过能在这些麻烦中处理得当,也是前端开发工程师能力的体现。

XMLHttpRequest

XMLHttpRequest是用于发起HTTP请求的标准对象,我们可以用下面的代码发起一个简单的请求。

var ajax = new window.XMLHttpRequest;
ajax.onreadystatechange = function(){
  if (ajax.readyState==4 && ajax.status >=200 && ajax.status < 300 ){
      alert(ajax.responseText)
  }
}
ajax.open("GET", 'http://www.baidu.com', true);
ajax.send("key=val&key1=val2");

当然,这段代码通常都会报错,因为XMLHttpRequest带有跨域的限制。

关于跨域的话,解决方案有很多:

  • jsonp —— jsonp是最常用的跨域解决方案,通过插入脚本来发送请求,利用了脚本可以请求跨域资源的特性。
  • 服务端设置access-control-allow-origin,通知浏览器允许发送可跨域的请求,不过有兼容性问题
  • window.name跨域
  • server proxy转发

更多可以访问跨域资源共享的10种方式

在IE6下没有XMLHttpRequest,不过IE自己提供了ActiveXObject对象来发起请求,IE7及后续版本则同时支持了ActiveXObject和XMLHttpRequest,不过IE7下的XMLHttpRequest曾出现过安全问题,有可能会被用户禁用,所以建议在实例化XMLHttpRequest的时候try catch一下。

AJAX中会用到的http状态码

  • 200(ok):找到所请求资源,并且没有在半路被菊暴
  • 304(not modified):该资源和上次请求时一样,没有改动。通常情况就是直接使用了缓存文件
  • 401(unauthorized):客户端无权访问该资源,通常就是需要输入用户名和密码
  • 403(forbidden):没有授权,就是说输入的用户名和密码错了
  • 404(not found):一般是请求的uri有错误

xmlhttp对象的readyState值

  • 0(为初始化):对象已经创建了,但还没有调用open方法
  • 1(载入中):open方法已经调用,但请求还没发送
  • 2(已载入):请求已经发送
  • 3(交互中):已经收到部分相应
  • 4(完成):所有数据已经收到了,连接已经关闭(大部分情况下我们只需要检查这个就行)

严格模式和混杂模式

严格模式

严格模式在两个地方有应用,一个是JavaScript的严格模式,还有一个是XHTML 1.0和HTML4的严格模式。

JavaScript严格模式产生的原因在ES5中有说明

The ECMAScript Language recognizes the possibility that some users of the language may wish to restrict their usage of some features available in the language. They might do so in the interests of security, to avoid what they consider to be error-prone features, to get enhanced error checking, or for other reasons of their choosing.

具体可以阅读Javascript 严格模式详解来了解更多。

XHTML1.0和HTML4的严格模式

block嵌套block或inline
a嵌套inline, 不能嵌套a
label不能嵌套label
button不能嵌套a, input, select, textarea, label, button, form, fieldset
form不能嵌套form
pre嵌套inline, 不能嵌套img, object, sup, sub
address嵌套inline
object嵌套block或inline
img, br, input, hr为空标签
必须组合使用
ol-li, ul-li, dl-dt+dd
table-caption+colgroup-col+col+thead+tbody+tfoot-tr-td+th
select-option+optgroup-option
object-param

 混杂模式quirk mode

混杂模式是指浏览器的docType无效或者没有时,就会进入混杂模式。

混杂模式的主要表现是:

  • 盒模型的高和宽包含了padding和边框
  • 行内元素的宽高可以设置
  • 图片padding失效
  • table字体属性继承失效
  • margin:0 auto,对定宽元素的居中失效

混杂模式的bug有这些:

  • w3c css2.1规定:A:hover必须放置在A:link和A:visited之后,否则将隐藏A:hover内定义的相同规则。同理,A:active应在A:hover之后,否则A:active中的相同规则将被隐藏。也就是编写a伪类的正确顺序是LVHA,但是IE6/7/8混杂模式下,写在后面:link的优先级是可以覆盖:hover的。IE6的标准模式下也有这个问题
  • 缺少单位类型的长度属性默认会认为单位是“px”。标准浏览器则会忽略。
  • 行内非替换元素包含一个行内元素,且被包含的行内元素高度大于外层元素高度时,混杂模式的IE6/7/8下外层元素会被内层元素撑开。IE6/7标准模式下也有这个问题。

当然,其他还有很多,不过在业务线上,我们不会刻意去触发quirk模式,所以适当了解下即可。

盒模型

在浏览器中,盒模型是布局的基础,盒模型包含了盒子的高度,内边距(padding),外边距(margin)和边框(border)。

盒模型实际占据的宽度可以这么计算:

height + padding-top + padding-bottom + border-top + border-bottom

主流浏览器在标准模式下,还是基本符合盒模型的计算方法。

没有设置宽度的块级盒子,如果没有触发layout,会尽量让自己填满剩余的空间,即使设置了padding,还是会和padding一起填满父级盒子的宽度。这和设置宽度为100%是不一样的。

xQlQy 前端的基本知识

而绝对定位或者浮动下的盒子,如果没有设置宽高,则会紧紧包裹住盒子的内容。

xPBXC 前端的基本知识

行内元素也是一个盒子,也可以有margin,padding,border。

walOb 前端的基本知识

上面的图片展示了一段包含在行内元素里的文字,我们通过设置background和opacity,我们可以清楚地看到行内元素的padding实际上是有效的,但是它没有占位,所以如果没有增加background的话,看起来和没加padding一样,所以才会有很多人认为padding对于行内元素是无效的。

一个行内元素的margin是生效在第一行和最后一行的,这个应该也不难理解。

margin会存在边距折叠的问题,这实际上也是符合常理的。margin的意思是外边距,表达的意思自然是自己距离其他盒子之间的距离,而如果其他盒子也声明了这个距离,不代表俩者的距离是相加的,应该是取其中较大的一个距离即可,因为这样都满足了两者对于距离的需求。

和盒子相关的还有outline,outline被成为轮廓,可以理解为边框外的边框。MSDN的outline介绍中注明了outline和border的区别:

  • Outlines不会占据空间
  • Outlines不一定是矩形的

具体可以查看demo

块级元素和行内元素

块级元素

  • 在块级格式上下文中,盒子是垂直布局的
  • margin表示了两个盒子之间的间距,所以会存在边距折叠,嵌套和相邻都会存在边距折叠问题。

行内元素

  • 在行内格式上下文中,盒子是水平布局的
  • 水平的margin、padding、border有效,垂直方向padding存在但是无效,border有效,margin无效。
  • 不能指定宽高

块级格式上下文

触发条件:

  • float不为none
  • overflow不为visible
  • display设置为table-cell,table-caption,table,或者inline-block
  • position非static或relative
  • layout为true

特性:

  • 取消边距折叠
  • 不和float box重叠

Layout

触发条件:

  • position: absolute
  • float: left|right
  • display: inline-block (IE6 不支持)
  • width: value ( 非auto,如果是行内元素,则只在quirk模式下生效)
  • height: value ( 非auto,如果是行内元素,则只在quirk模式下生效)
  • zoom: value ( normal,最常用zoom:1)
  • writing-mode: tb-rl

IE 7 :

  • overflow: auto|hidden|scroll
  • overflow-x|-y: auto|hidden|scroll
  • position: fixed
  • min-|max-width: value
  • min-|max-height: value
  • width/height

总结

layout和BFC相关的内容可以阅读:更加直观地了解hasLayout和BFC

元素布局

普通流

普通流就是正常情况下的文档流布局。正常文档流下的元素top/left/button/right,z-index是无效的。

position为relative的元素也是按照普通流布局,不过top/left/button/right,z-index是有效的,并且按照元素原位置进行偏移。

浮动

  • 脱离文档流,紧靠容器或者另外一个不和浮动元素重叠的元素边缘,空间不够就换行。
  • 文档流中line box,inline-box都会围绕float box。
  • top/left/button/right,z-index无效

浮动元素的问题主要来自它与其他元素的重叠,并且还存在双边距的问题。记得清除浮动就行,清除浮动的方式参考BFC和hasLayout。

绝对定位

  • 脱离文档流
  • float失效
  • position:fixed是其子类

绝对定位的一些问题可以参考z-index在IE中的迷惑

IE6/7下对stacking context的产生有bug,具体阅读上面链接。

Android webkit

Webkit是一个开源的浏览器排版和渲染引擎,包含WebCore和JavascriptCore。WebKit有众多的实现(Qt、Gtk, windows, chromium, android, etc)。

Android 4.0平台的Web引擎框架采用了WebKit中的WebCore,javascript引擎则是采用google的V8引擎。Android 4.0的webkit采用了和chromium 12.0.742.130中webkit相同的codebase,webkit版本为534.30。

由于Android系统本身是用Java编写的,那么,webkit与系统相关部分只能用Java编写,同时也可以提供一些Java API提供给应用开发者调用,而由于webkit底层的排版部分与系统关系不是很大,则使用C/C++编写。那么,Android webkit的结构就很清晰了,分为:Java层和C层。两者的通信通过Java Native Interface实现。

qPZ0D Android webkit,webview和chrome的关系

 

WebView

WebView是处于Java层的视图模块,通常在Android Native App中插入的html页面也是构建与WebView之上,包括了页面的浏览,请求的处理。这也就是为什么WebView的出镜率比Android Webkit本身还要高。很多Native App在开发的时候,部分更新率高的模块,都会选择使用WebView来渲染html页面,从而可以方便内容更新。

在C层中也有一个WebView模块,C层中的WebView模块负责初始化并构造WebView对象,然后将其赋值给Java层的WebView。之后两者就可以进行通信了。

Chrome

关于Android chrome,更多就是一些小道消息了。

  • chrome for Android是Chromium的派生。
  • Android自带浏览器虽然与Chrome有一些共享代码,但是两者有非常大的区别,两个开发团队进行了两种完全不同的开发。

未来Android浏览器和chrome for Android之间必然会统一。

参考资料