script的src和link的href属性值为空的陷阱

2010-8-2 update:经过测试发现,但script的src和link的href属性值为#的时候,也会发生同样的事情:给服务器发送一个HTTP请求,并把当前页面作为返回值。浏览器的异同与下面的结论相同。通过js动态生成的script和link元素也一样。

————-============————-============————

有些时候,我们在写HTML页面的过程中,通过script标签来外联外部的js文件,可是不小心给src属性赋值为空,当然有时候也会在link元素中带上了空属性值的href属性,我们想当然的认为这个没多大的影响,浏览器也会按照你认为的方式来解析它们。但是这个想当然估计错误了。先看几个测试页面:《HTML5 Doctype下href=”"》,《HTML5 Doctype下src=”"》,《XHTML1.0 Doctype下href=”"》。

使用firebug来查看DOM节点树的link和script节点,你就会发现怪异的事情发生了:link和script节点的内容居然是当前html页面的内容!!确实不可思议,就连容错机制最好的XHTML1.0的DTD声明也无济于事,这恐怕就是浏览器渲染页面的问题了。script节点src属性为空的时候就更恐怖了,会造成了语法错误。可更不可思议的是,出现这个问题的浏览器居然不是IE。经过多个浏览器的测试,IE和Opera都能很好的处理这个问题,可是Firefox、Chrome、Safari居然出现了

对于这个初步总结为:当link标签的href属性为空、script标签的src属性为空的时候,浏览器渲染的时候会把当前页面的URL作为它们的属性值,从而把页面的内容加载进来作为它们的值。动态修改它们的href或者src属性值为空的时候,也将会出现这个问题:《动态修改href为空》。

因此,在我们平时写HTML页面,或者动态生成这两个标签的时候,尽量避免陷入了上面出现的陷阱,好好对待浏览器,编写符合规范的HTML标签语法和嵌套规则,多余的就别多此一举去倒弄,它回报给你的将是更少的开发时间和精力。

当某天你测试上面给出的测试样例并没有发现这个问题的时候,那就是浏览器修复了这个bug了。

@font-face的性能测试

对@font-face的作用,也是今晚在看这篇博文《@font-face and performance》的时候,才知道使用它可以自定义显示字体。我想在浏览器中加入@font-face的功能是处于一定的情况的,比如使用特殊的字体来显示页面某部分的字体,从而避免采用图片的方式来显示特定字体。出发点是很好的,但是……

解析@font-face的机制在不同的浏览器下是不同的。

对于@font-face更多的叙述,在上面提到的那篇博文中有了详细的描述。但是对于它提出的说“IE doesn’t render anything in the page until the font file is done downloading if there is a SCRIPT tag above the @font-face declaration.”,说的是当在声明@font-face的前面如果存在script标签的时候,IE下不会渲染任何东西,页面一片空白,直到@font-face指定的字体加载完为止,这点经过测试,确实如此。

在声明@font-face的标签之前没有script标签的时候IE下并没有出现阻塞页面渲染的情况,但是如果前面有script标签的时候,不管是否是紧跟这script标签,只要在@font-face标签前面有script标签,就阻塞页面渲染:《没有script标签》,《紧跟script标签》,《不紧跟script标签》。

同时,我测试了@font-face是否会阻塞页面其他资源加载,测试发现并没有这个问题:《是否阻塞资源加载的测试》,而且在字体加载下来之前,忙指示器会一直处于等待状态。下面是各种浏览器对@font-face的测试比较结果,结果分为三类:IE,Firefox、Opera,Safari、Chrome。

  1. 在Firefox、Opera中,并不会阻塞页面其他资源的加载,也不会影响页面渲染,而且在字体加载下来之前,对想应用该字体的内容会使用默认的字体显示,直到字体加载下来之后再渲染为指定的字体。因此,这两个浏览器测试效果最佳。
  2. 在Safari、Chrome中,并不会阻塞页面其他资源加载,也不会影响页面渲染,但是在字体加载下来之前,对应用该字体的内容将会首先空白显示,但是内容尺寸是存在的,直到字体加载完成,才渲染字体样式。
  3. 在IE下就分不同的情况了,如上所述,前面是否有script标签的情况。

对于@font-face的杯具的性能问题,如果没有必要,就尽量避免使用。如果一定要使用的话,可以使用使用lazy load的方式来加载,例如博文中给出的例子:

function lazyload() {
  var sRule1 =
	"@font-face {" +
	"  font-family: 'Yanone';" +
	"  src: url('/bin/resource.cgi?type=font&sleep=6');" +
	"  src: local('Yanone'), " +
        "url('/bin/resource.cgi?type=font&sleep=6') " +
        "format('truetype');" +
        "}";

  var style1 = document.styleSheets[0];
  if ( "function" === typeof(style1.insertRule) ) {
        // Firefox, Safari, Chrome
	style1.insertRule(sRule1, 0);
  }else if ( "string" === typeof(style1.cssText) ) {
	// IE
	style1.cssText = sRule1;
  }
}

将上面的lazyload函数添加到页面的onload事件中去执行,这样就保证了页面的正常渲染并且实现特定字体样式的功能:《lazy load测试》。这个主要是解决IE下@font-face前面有script标签的问题,在其他浏览器下显示的时候,也都还是会在应用该字体的内容中产生空白闪烁、或者是默认字体和指定字体之间的切换闪烁。也都还是在一定程度上影响了用户体验,感觉有些不爽。“Tradeoff is anywhere!!

上面的三个测试都是使用内联CSS的方式,考虑到使用外联CSS样式表的情况,又对此做了一番测试,发现了不同的状况:

  1. 当link放在head时,IE6、8(没有IE7,测试不了)都是死心眼,偏执要阻塞整个页面的加载,在字体下载下来之前整个页面一片空白,其他浏览器跟内联CSS一样的显示:《外联CSS测试(在head内)》。
  2. 当link放到body中时,IE8还是阻塞整个页面的渲染,可是IE6就不同了,IE6会阻塞整个页面样式的渲染,但是不会阻塞link前面的内容的显示,而是阻塞了其后面的内容的显示了,直到字体加载下来之后才开始渲染页面样式和显示link后面的内容。其他浏览器跟内联CSS一样的显示:
    外联CSS测试(在body最底部)》,《外联CSS测试(在body中部)
  3. 当link前面带有script标签的时候,因为外联在head头部的时候在IE各版本都阻塞了,所以这个条件link在页面的任何位置都无所谓,主要测试link前面带有script标签,那就把link放在body中来测试。这下IE6也杯具了,阻塞了整个页面的渲染,IE8照常阻止整个页面渲染,其他浏览器同上。《外联CSS测试(在body中部),前跟script标签》,将link放到body最底部也一样阻塞整个页面的渲染:《外联CSS测试(在body最底部),前跟script标签》,其他浏览器同上。

对于页面中同时有script和link、style标签是很正常的事情,但是当样式中有@font-face的时候,情况就复杂了。更多的测试条件大家可以自行测试。有错误的地方,请不吝留言交流。

重构一个简单的导航

昨晚在浏览Delicious网站的时候,又看到了这种样式的导航样式:

之前一直很想做这样的一个导航,可是苦思都不知道良方,还一直认为这需要几个背景图片,着实复杂;还有hover效果,更是会难些,而且有兼容性问题。之后在FF和IE6下测试了下Delicious的兼容性效果,它在ie6下的样式,使用了普通的背景样式,没有上面所看到的折角效果。

之后看了下它的背景图片,一下子豁然开朗了,关键在于使用负margin来实现即可。下面是我实现的样式截图:

而且在兼容性和代码方面都有所改善。在IE6下也可以实现折角的效果,但是没有hover效果。而在Delicious里是在超链接a标签下嵌套了span标签,在a:hover伪类下来处理span的背景变换。而我直接在li标签中使用:hover来处理鼠标hover的背景切换,代码更加简洁了点,并且通过hack来解决ie6下兼容性问题(Updates:因为把:hover写在了li标签上,所以可以直接去掉hack来兼容ie6的css样式,也能做到稳步退化的效果)。

具体的CSS样式和背景图片可以浏览《折角导航栏效果》页面,使用firebug等工具查看页面源码。

iPhone网站开发点滴

这几天接到北京提出的iphone网站开发项目,看到视觉稿,也确实够晕的。仔细想想,需要使用到很多css3圆角和背景技术,从这点来说,玩玩css3,也确实不错。






上面列出的部分视觉稿中,按钮的样式、导航的渐变背景,以及带有icon前缀修饰的按钮,渐变背景和圆角都是用了css3的-khtml-border-radius,background:-webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#d8d8d9))。css3,玩起来还是不错的,在这里我不想讨论css3的使用技巧,只是想把在做的过程中遇到的一些问题,亮出来晒晒。

  1. HTML的head标签内需要显示声明meta属性:<meta name=”viewport” content=”width=device-width; minimum-scale=1.0; maximum-scale=1″ />,minimum-scale和maximum-scale主要作用是控制内容的显示比例。
  2. iphone在使用css3的时候,要通过-khtml为前缀的方式,-webkit的方式不管用。比如:-khtml-border-radius
  3. iphone不支持css的inline-block属性
  4. iphone没有:hover状态,不过可以通过iphone的ontouchstart和ontouchend两个js事件来模拟触屏特效,比如上面展示了搜索结果展示的list和按钮样式变化的情形,我都是用js来实现的。不过这也有个不可避免的问题,就是iphone默认有这么个触屏效果,虽然可以在ontouchstart和ontouchend事件中通过return false可是实现,但是这取消了链接或者按钮默认的功能,这个需要衡量。(javascript示例代码如下)
  5. iphone在横屏和竖屏的情况下,字体会发生变化。这可以通过在html中用css的属性:-webkit-text-size-adjust:none;声明一下,就可以防止横竖屏切换的时候字体发生的变化,导致其他样式连锁变化。
  6. iphone支持大多数的css3属性:我使用到的就有:last-child,:first-child,nth-child(n),:last-of-type,:first-of-type等等,通过只用这些css属性,可是节省很多麻烦。
  7. iphone同时也好支持几个class类做判断的功能,比如:<div class=”test1 test2″></div>,编写这样的css规则用于判断div是有两个class类:.test1.test2{background:#f00;}。只有div的class中同时包含了test1和test2这两个类,上面的css规则才生效。

(全文…)

IE一像素border的问题

今天在做页面的时候,有一个tab组件的需求,需要切换样式:

tab在切换的时候如上面图片所示,左右一个像素的border,上面2像素,下面一个白色像素来覆盖边框。这个在FF等非IE内核的浏览器下显示没有问题,可是在IE浏览器下《问题页面》:

在IE下,激活tab右下角总是多出了一像素高度,为着这个IE下的一像素问题,快崩溃了。最初还以为是IE bug,测试来测试去,最后发现是IE渲染border的方式跟FF等浏览器不同。按原理来说,应该是top、bottom覆盖left、right的border颜色(FF等浏览器正是这么做的),可是IE却不是,它是按照逆时针,类似于是拼接似的来渲染border。这个问题只有在一个像素宽的border的时候才会出现,当大于一像素的时候,问题不存在。

更新的测试页面

编写HTML页面的点点感受

进入口碑也已经有三四个月的时间了,感觉在工作过程中收获最大的是在编写HTML页面上,从视觉稿到网页DEMO,没办法,基本都在做这样的工作。从最初的wap整站、杭州首页、bug需求到广州首页、上海首页、店铺大全等页面改版,到当下的口碑卡频道首页改版的DEMO的编写。心情从最初的烦躁、枯燥的调试到轻松自如,到当下的信手拈来、游刃有余,摆脱了IE6、7 Fuck bug的束缚,每次尝试到不用写一个hack来完成整个页面布局的滋味,记忆犹新。

在公司同事中学到了不少的经验,也总结出来了自己的一套开发步骤:

  • 整体分析页面布局
  • 归纳统一、分类,切图(CSS Sprites)
  • 拆分模块、规划HTML结构、规划并编写CSS
  • 组合成页面(得益于YUI的页面布局生成器组件)

第一步:整体分析页面布局。
当拿到视觉稿的时候,不是着手去想怎么编写HTML、CSS代码,而是先从整体上分析页面的布局,观察页面各个模块的间距差别、划分header,body,footer、CSS Sprites、切图事宜、页面色调、背景如何设置等等,之后再细化到每个模块的布局,从外到内,逐步细化。可以说,这一步是非常关键的,它给了下面将要进行的代码编写一个整体的框架布局。

第二步:归纳统一、分类,切图(CSS Sprites)
整体分析过后,就是归类、组合模块的时候了。这一步还是的需要从整体来看,分析页面哪些模块的样式相似(常见的就是ul列表,以及一些图文编排),包括颜色、样式、修饰、背景、宽度、padding、margin、border等等。将相似的模块的样式归类,这为接下来编写CSS代码大有好处,可以最大化的精简CSS代码。

第三步:拆分模块、规划HTML结构、规划并编写CSS
在前期工作都做了之后,这步就改到了动手写代码的时候了,我喜欢将每个模块或者列拆分开来编写,这样做的好处是我可以先不管整体布局,从HTML语意化的角度架构HTML结构,此时HTML代码较少,在编写CSS以及查看HTML代码的时候不用花费太多时间了(痛恨HTML代码多了,上下拉滚动条,累人!)

第四步:组合成页面。
在每个模块都编写好之后,借助页面布局生成器(幸亏有了它,效率第一啊!缺点是HTML结构复杂,-_-),将每个拆分的模块整合起来,同时也清理CSS文件的代码,整合CSS代码也有一些些技巧的:在CSS Sprites里设置HTML元素的背景的时候,把全部的元素都写在一处:

/* background url */
.rhs-bd,.business-join h1,.yk-mod-content .business-join p a,.business-open-flow ul,.business-case-list ul li,
.kb-bank-toolbar ul,.kba-consumer h1,.kba-consumer h1,.kba-consumer ul li,.kba-new-join ul li,
.recommend-shop-list,.hot-sign,.search-merchant-bd{background:url(ka_bg.png) no-repeat;}

这样相应的元素只需要设置background-position就OK了,代码整洁,结构清晰,重构维护成本小。还有一点就是慎用margin,在IE6、7下margin会引发很多问题,所以在编写CSS和规划HTML布局的时候,需要考虑到需要使用margin还是使用padding,各有各的好处,margin能保持HTML结构比较简洁,而padding原理是在当前HTML元素的子元素中设置padding来实现布局(当然也可以在当前元素下,但是它会改变宽度和高度,需要重新调整宽高)。所以我的经验是:在模块的HTML和CSS编写方面,都会尽量使用padding,特别是涉及到float的时候,使用padding会避免很多问题。有句话说的好:既然明知道这样做会引发bug,那就尽量不这样做。

上面的开发步骤因人而已,偶是喜欢并且习惯这样的一个思索开发过程,正在体验它的乐趣。《页面DEMO请点击