关于界面的价值观与方法论
78910111213141516

关于JS变量声明的小细节

10January2009

对于从未声明过的对象,如果尝试给它赋值,会隐式的将它声明为全局对象。比如:

(function() {
  s = 'abc';
})();
alert(s); //会弹出 abc

如果尝试读一个未声明的对象,JS会报错。比如:

alert(a); //不会弹出对话框,会报错

这个结论是犀牛书中所写。但是有意思的是,在IE里,如果尝试读一个未声明的对象,并不会报错,当然也不会继续执行JS。这点其实挺要命,不执行也不告诉你为什么。

但这个结论是铁律,所以如下的写法是经常容易犯的错误:

if(!a) {
    //do sth. ...
}

这样写有时候的初衷是:如果变量a不存在,就执行里面的语句。但如果a未定义,在Firefox中会报错;在IE里不会报错,但也不会执行以后的所有语句。

所以该如何探测一个变量是否被定义呢?一个巧妙的办法是:

if(!window.a) {
    //do sth. ...
}

这个方法其实很奇妙,因为读一个未声明的变量,会报错;但读一个未声明的属性,就不会报错。天晓得JS为啥规定这样奇怪的语法。

但是这样的方法只适用与全局变量。若是某个函数里的局部变量,还是用typeof去判断吧:

if(typeof a == 'undefined') {
    //do sth. ...
}

所以,结论就是,养成习惯,不要在变量未定义之前使用它

接下来再看一个例子:

var a = "I'm out";
(function() {
    alert(a);
    var a = "I'm in";
})();

好,提问,运行这段代码,会弹出什么文字?

回答一:弹出“I’m out”。错。JS没有块级作用域,无论var语句在何处,在整个函数体内它都是有定义的。

回答二:弹出“I’m in”。错。请再重新看上面那句话,它只是在整个函数体内有定义,但只有在被赋值之后才会有值。

正确答案是:“undefined”。是不是很晕?自己试试看吧。

总之,再重复一遍之前的结论:不要在变量未定义之前使用它

最后介绍一个我也搞不明白的问题:

在HTML文档里写上这段代码:

<script type="text/javascript">
    window['a'] = 'Hi';
</script>
<script type="text/javascript" src="out.js"></script>
<script type="text/javascript">
    alert(a);
</script>

然后在out.js里写上这句:

 if(false) {
     var a = 'Hello';
 }

然后用FF和IE6分别运行,看看你得到什么。

在FF里会弹出“Hi”,但是在IE6中,会得到“undefined”。

很神奇吧?按语法,无论如何,a都不可能是undefined。但是IE6里就会。

如果把两个语句都写在同一个文件里,就不会有这个情况。
如果把out.js里改成window.a,或者把前一个改成var a,也不会有这个情况。
如果把out.js里的var a移到if语句之外,或是把if的条件改为true,也不会有这个情况。

这个bug其实危害性很大。所以呢,只能给自己定一个好习惯,那就是不要在条件语句中声明变量,虽然这么做,语法上并不禁止。

全局字体最佳实践

28December2008
body {font-family: Arial, sans-serif; }

这是我迄今发现的最好的全局字体解决方案。当然,所谓“最好”,也只是基于我个人的评判标准。所以我还是得分析一下其他的写法有什么缺点,最后再总结这个写法的特点。

body {font-family: "宋体", sans-serif; }

这个写法可能的缺点在于:
1. 宋体在Safari和Vista的IE 7下,看起来很难看。(我稍后把截图补上)
2. 宋体的英文字很难看。
3. 如果在CSS里写中文,你得小心你HTML和CSS的编码是否一致。

body {font-family: SimSun,sans-serif; }

这样写,可以避免上面的第三个问题。但是宋体本身确实很难看。我们希望在不同平台下,都用各自默认的字体。XP是宋体,Vista是微软雅黑,Mac是黑体。这样的话,只能将字体的第一个设置为英文字体,这样遇到中文的时候,浏览器会自动调用默认字体(Vista IE 7的一些版本里貌似默认还是宋体,这个我就无能为力了,交给用户设置的自主权吧)

body {font-family: Tahoma, sans-serif; }

这是一个不错的解决办法。Tahoma其实是一个挺漂亮的字体(我同事喜欢称它“大河马”,哈哈)。但是它其实会带来一些问题:
1. 由Tahoma显示的中文,在IE 6里,下划线会紧紧的贴住中文字,很难看。
2. IE 6下,Tahoma无法正确的设定为13px。它会跟14px一样大。但是其他浏览器没有这个问题。
3. 如果一行里同时出现中文和英文,且这一行里有元素被定义了vertical-align属性,在IE 6、7里会导致文字高低不齐,甚至下划线错位。

body {font-family: Arial, ans-serif; }

上述两个问题,Arial都没有。但是Arial也有缺点:
1. 比Tahoma难看。
2. Tahoma里的第三个问题也同样存在。
不过,这个bug是有个解决办法的,就是将这一行定义zoom:1

所以,如果不嫌难看,定义为Arial是最合适的。如果实在不喜欢,可以将全局定义为Tahoma,然后再将有下划线的(如链接)文字定义为Arial,至少可以缓解一下。

最后,对于全局字体,补充一点:IE里,所有的表单元素都不继承body的字体属性,需要单独设置:

input, label, select, option, textarea, button, fieldset, legend {
    font-family:Tahoma,sans-serif;
}

CSS选择符命名原则

20December2008

坦率的说,我写CSS时,有至少1/4的时间是在纠结选择符该如何命名。随意的命名,会让你陷入尴尬。

举个例子,我们需要在一段文字里将某些词标红。所以为此建了一个.red { color: red; }。结果产品经理觉得,红色太刺眼了,改成橘色比较好吧。这下头大啦,难道写成.red { color: orange; }么?要不然就是把HTML里的每个class="red"改成class="orange"

在过去几年时间里,我经常遇到这样的尴尬。因此作为一些经验之谈,我整理出以下的几个命名原则:

原则一:CSS选择符命名应该体现结构而不是样式。

这句话听起来很奇怪。这样不是违反了结构和样式分离的原则么?恰恰相反,其实正是遵循分离原则。要知道,CSS选择符名真正的用处是在HTML代码里修饰标签。所以它其实应该被看作结构的一部分。因此选择符的名字不可以跟样式有关。

我开头将的那个例子,就是违反这一原则典型。正确的写法,应该是.highlight { color: red; },或者索性就用em { color: red; font-style:normal; }

同理,我尽量避免用带有left、right、颜色、bold等字眼的名字,总而言之,那些可以被作为CSS属性值的名字,都尽量少用作名字。

有时候,我们会遇到另一种情况。有一个很常见的布局:标题放在区块的左上方,而右上方是一个“查看更多”的链接。所以我们会用这样的代码

<h2>
    <span><a href="#">查看更多</a></span>
    精彩贴图
</h2>

自然,会有相应的样式:

h2 span {float:right;}

但是这样做其实很危险。如果我们要求给标题后再加一个副标题(比如“超过1000万精美图片”之类的恶俗标语),就没法再用span了。用strong或是em,其实感觉怪怪的。

原则二:不要轻易将特殊样式直接赋给常用的HTML标签

上面那个例子的问题就在于,轻易的将样式应用在span这种常用的标签上了。h2 span这种选择符,意味着你认为h2里所有的span都要使用右浮动。但是这样的判断显然是很武断的。

另一个例子是,喜欢用ul.top10 li这种选择符。这样用并非不可,但请先确保这个排行榜里只是纯文字。倘若有一天产品经理发现,似乎应该把前三个的信息写得更详细,就意味着你可能需要用到下面这种结构:

<ul class="top">
    <li>
         <div class="product">
            <h6>产品名称</h6>
            <ul>
                  <li>型号:XXX</li>
                  ...
            </ul>
         <div>
    </li>
</ul>

这下又傻眼了吧。只能用.top20 .product li {}去覆盖掉之前的样式。

我比较倾向的写法,是如 ul.top20 li.item这样写。(当然,CSS2准备了更多更精准的后代选择器,就犯不着这个了。等待IE8吧~~)

原则三:设计好一系列的“全局保留字”,以避免命名冲突

一些常用的单词,就不要用作特殊样式的名字。比如,我们会经常用到如下的选择符:

.tab li.current

或者

.nav li.current

以表示当前激活的标签或选项。很多样式都会用到“current”这个单词,为了避免彼此的样式冲突,有一个很简单的办法,就是人为商定,单纯的.current样式不作任何定义。换句话说,current这个名字只能出现在精确的后代选择符中。

类似的还有:
“.active”:和current差不多,有人也喜欢用这个
“.first”:经常在如“ 选项 | 选项 | 选项”的结构中用到
“.last”:在田字格布局中可能会用到
“.hover”:有时候需要用这个样式结合JS实现一些效果
“.text”、“.button”、“.submit”等:在属性选择器普及前,input标签的样式都靠它们了。

原则四:英文要正确
哎,这只能怪计算机是美国人发明的了。有时候会看到一些奇怪的选择符名。我解释一些常用单词的含义:

header,footer
    这两个的意思是指“头部”和“底部”,更确切点讲是“页头”和“页尾”。

heading,footing
    heading的意思是“标题、题目”,<h1>的h就是heading的缩写。
    footing的意思是“注脚”。

所以呢,我的看法是,整个页面的顶部和底部用header和footer,而区块的标题和底部用heading和footing。

caption
    这个词的意思也是“标题”,但是它特指“图片的文字说明”。所以记得用在图片底部的文字。

primary,secondary
    这两个词是“首要”“次要”。它们往往是指同级别的事物,只是重要性不同。比如提交按钮和取消按钮。

main,sub
    这两个词才是上下级关系。如“主菜单”和“子菜单”。

top,middle,bottom
    这里主要是“中”这个词。“上中下”里的“中”是用middle。参见text-align属性。

left,center,right
    “左中右”的“中”是center。参见vertical-align。

此外,还有一些常用的词语:

rating:打分
rank:“第几位”
showcase:展示橱窗,可以用作一行五个这种排布。
category:分类
thumbnail:缩略图
snapshot:截图。这个单词的意思是“快照”,所以一般指原大小。
breadcrumbs、pathway:这两个都可以用来指这个东西:“首页 > 二级页面 > 内容页”
quicklinks:这个可以用作页面右上角的“ 登录 | 帮助 | 客服 ”,也可以指页面底部的“ 关于我们 | 网站地图 ”
tip:提示。可以用作输入框旁边的文字,或是弹出的提示框。