事实上,7个范例脚本都用到了某种形式的CSS修改。比如,“表单验证”改变出错的表单域的样式,“XMLHTTP速度测试计”使用动画(其实也就是在很短的时间内多次改变某个样式)来让用户注意到速度的数据(而且,老实讲这算是有些花哨的效果)。“下拉菜单”通过改变样式来显示和隐藏菜单项。这些变化都拥有同样的目的:把用户的注意力吸引到这些元素上。
JavaScript有如下4种修改CSS的方式:
l 修改元素的style属性(element.style.margin='10%');
l 改变元素的class或id(element.className='error'),浏览器将自动应用那些定义在新的class或id上的样式;
l 向文档中写入新的CSS指令(document.write('<style>.accessibility{display: none}</style>');
l 改变整个页面的样式表。
大多数的CSS改变脚本,都采用修改style属性或改变class或id的方式。document.write方法只适合用于某些特定的场合以增强页面的可访问性。最后,我们很少会改变整个样式表,因为并非所有的浏览器都支持这样做,而且通常你也只是想改变某些特定元素的样式。
不管怎么说,我在范例脚本中使用了所有4种方法。我们将在本章中逐个研究这些方法及它们适用的场合。
A style属性
最初也是最广为人知的修改CSS的方式就是通过所有HTML元素都拥有的style属性,并且访问它们的内联样式,style对象对每一个内联的CSS声明都包含一个对应的属性。如果你想设置一个元素的CSS属性margin,使用element.style.margin。如果你想要设置它的CSS属性color,就使用element.style.color。JavaScript属性总是拥有一个和CSS属性相似的名字。
内联样式
记住:HTML元素的style属性让我们得以访问该元素的内联样式。
让我们来回顾一些CSS的理论。CSS提供4种方式来给元素定义样式表。你可以使用内联样式,即直接把你的CSS写在HTML标签的style属性中。
<p>Text</p>
此外,你可以嵌入、链入或引入样式表。不管用何种方法,因为内联样式比其他任何形式的样式更为明确,内联样式能覆盖那些嵌入、链入或引入页面的样式表中定义的样式。因为style属性可以访问这些内联样式,所以它总是能覆盖其他的样式。这是这种方法的巨大优势。
然而,当你尝试读取样式时,可能遭遇问题。看这个例子:
<p>Text</p>
p#test {
margin: 10%;
}
alert(document.getElementById('test').style.margin);
测试段落并没有包含任何内联样式,margin: 10%是被定义在一个嵌入的(或者链入,或者引入的)样式表中,而它是不可能从style属性中读出来的。弹出警告框显示为空。
在下一个例子中,弹出警告框将显示返回结果“10%”,因为margin现在被定义为内联样式:
<p>Text</p>
alert(document.getElementById('test').style.margin);
所以,style属性最适合于设置样式,而要获取它们时就没那么有用了。后面我们会讨论从嵌入页面的、链入的或者引入的样式表中获取样式的方法。
破折号
许多CSS属性的名字包含一个破折号,例如font-size。然而在JavaScript中,破折号表示相减(minus),因此它不能被用在属性名中。这将给出一个错误:
element.style.font-size = '120%';
这是要求浏览器从element.style.font里减去(未定义的)变量size吗?如果是= '120%'代表什么意义呢?作为替代,浏览器期望一个驼峰格式(camelCase)的属性名:
element.style.fontSize = '120%';
一般规则是从CSS属性名中移除所有的破折号,并且破折号后的字符变为大写。这样,margin-left变成了marginLeft,text-decoration变成了textDecoration,而border-left -style变成了borderLeftStyle。
单位
在JavaScript很多数值类型的值需要一个单位,就像它们在CSS中声明时那样。fontSize=120表示什么?120像素、还是120磅或者120%?浏览器可不知道这些,所以它不会做任何反应。为了阐明你的意图,单位是必须的。
以setWidth()函数为例,它是实现“XMLHTTP测速计”的动画效果的核心程序之一:
[XMLHTTP测速计,第70~73行]
function setWidth(width) {
if (width < 0) width = 0;
document.getElementById('meter').style.width = width + 'px';
}
该函数接手一个值,它将改变meter的宽度为这个新值。在经过一个安全检查以确保该值大于0之后,设置元素的style.width为这个新的宽度值。最后加上'px',因为不这样的话,浏览器可能不知道如何解释该数值,结果什么都不做。
不要忘了'px'
忘记在width或height之后附加一个'px'单位是一个常见的CSS修改错误。
在CSS的怪癖模式(quirks mode)里,加上'px'不是必须的,因为浏览器遵循旧的规则,把无单位的值视为像素值。本质上这不是一个问题,但很多Web开发人员因此养成了改变宽度或高度值后遗忘加上单位的习惯,当他们工作在CSS严格模式(strict mode)下时就遭遇到了问题。
获取样式
警告 以下所述内容有浏览器兼容性问题。
正如我们所看到的,style属性不能读取设置在嵌入、链入或引入页面的样式表中的样式。但是因为Web开发人员有时候需要读取这些样式,微软和W3C都提供了访问非内联样式的方式。微软的解决方案只能工作在Explorer下,而W3C标准可以工作在Mozilla和Opera下。
微软的解决方案就是currentStyle属性,它的工作方式像极了style属性,除了两件事情:
l 它可以访问所有样式,不仅仅是内联样式,所以它汇报的是实际应用在元素上的样式;
l 它是只读的,你不能通过它设置样式。
例如:
var x = document.getElementById('test');
alert(x.currentStyle.color);
现在弹出对话框显示元素当前的color样式,而不管它是在什么地方被定义的。
W3C的解决方案是window.getComputedStyle()方法,它以相似但语法更为复杂的方式工作:
var x = document.getElementById('test');
alert(window.getComputedStyle(x,null).color);
getComputedStyle()总是返回一个像素值,尽管原来的样式可能会是50em或11%。
同以前一样,当我们遭遇不兼容的情形时,需要一些代码分支来满足所有浏览器:
function getRealStyle(id,styleName) {
var element = document.getElementById(id);
var realStyle = null;
if (element.currentStyle)
realStyle = element.currentStyle[styleName];
else if (window.getComputedStyle)
realStyle = window.getComputedStyle(element,null)[styleName];
return realStyle;
}
你可以使用这个函数如下:
var textDecStyle = getRealStyle('test','textDecoration');
记住getComputedStyle()将总是返回一个像素值,而currentStyle保留原来定义在CSS中的单位。
简写样式
警告 以下所述内容有浏览器兼容性问题。
不管你是通过style属性获得内联样式,还是通过刚刚讨论的函数获取其他的样式,当你尝试读取简写样式时,都会遇到问题。
看这个边框(border)的定义
<p>Text</p>
因为这是一个内联样式,你期望这行代码可以工作:
alert(document.getElementById('test').style.border);
不幸的是,它不能。不同浏览器在弹出对话框中显示的确切的值是不一致的。
l Explorer 6给出的是 #cc0000 1px solid。
l Mozilla 1.7.12给出的是1px solid rgb(204,0,0)。
l Opera 9给出的是1px solid #cc0000。
l Safari 1.3没有给出任何边框值。
问题出在border是一个简写形式的声明。它暗中包括了不少于12个样式:上(top)、左(left)、下(bottom)和右(right)边框的宽度(width)、风格(style)和颜色(color)。相似地,font声明是font-size、font-family、font-weight和line-height的简写形式,所以它也会展现相似的问题。
rgb()
注意Mozilla使用的特殊的color语法:rgb(204,0,0)。这是传统的#cc0000的有效的替代值。你可以在CSS和JavaScript中任意选择一个语法使用。
浏览器是如何处理这些简写形式的声明呢?上面的例子似乎过于直接;你的直觉应该是期望浏览器返回1px solid #cc0000,确保与内联样式所定义的一致。不幸的是,简写形式的属性比那还复杂的多。
考虑下面的情形:
p {
border: 1px solid #cc0000;
}
<p>Test</p>
alert(document.getElementById('test').style.borderRightColor);
所有浏览器都汇报正确的颜色,尽管内联样式中没有包含border-right-color而是声明了border-color。显然浏览器认为右边框的颜色在设置整个边框颜色时被设置,这也是合逻辑的。
正如你看到的,浏览器必须为这些异常情况制定规则,而且它们已经选择了略有不同的方式去处理简写形式的声明。在缺乏处理简写属性的明确规范的情况下,很难评判哪个浏览器是对还是错。