12.2 样式
DOM2级样式模块围绕外联样式,style内嵌,style属性这3中应用样式的机制提供了一套API;
能力检测;
var supportsDOM2CSS = document.implementation.hasFeature("CSS", "2.0");
var supportsDOM2CSS2 = document.implementation.hasFeature("CSS2", "2.0");
访问元素的样式
在标准模式下,所有度量值都必须指定一个度量单位;
在混杂模式下,可以将style.width 设置为”20”,浏览器会假设它是”20px”;
但在标准模式下,将style.width 设置为”20”会导致被忽略——因为没有度量单位;
在实践中,最好始终都指定度量单位;
如果没有为元素设置style特性,那么style对象中可能会包含一些默认的值,
但这些值并不能准确地反映该元素的样式信息;
DOM样式属性和方法
style对象的属性和方法;
1.cssText:如前所述,通过它能够访问到style特性中的CSS代码;
2.length:应用给元素的CSS属性的数量;
3.parentRule:表示CSS信息的CSSRule对象;本节后面将讨论CSSRule类型;
4.getPropertyCSSValue(propertyName):返回包含给定属性值的CSSValue对象;
5.getPropertyPriority(propertyName):如果给定的属性使用了!important设置,则返回”important”;否则,返回空字符串;
6.getPropertyValue(propertyName):返回给定属性的字符串值;
7.item(index):返回给定位置的CSS属性的名称;
8.removeProperty(propertyName):从样式中删除给定属性;
9.setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先权标志(“important”或者一个空字符串);
var prop, value, i, len;
for (i=0, len=myDiv.style.length; i < len; i++){
prop = myDiv.style[i]; //或者 myDiv.style.item(i)
value = myDiv.style.getPropertyValue(prop);
alert(prop + " : " + value);
}
计算的样式
getComputedStyle()接受两个参数:要取得计算样式的元素和一个伪元素字符串(例如”:after”);
如果不需要伪元素信息,第二个参数可以是null;
返回一个CSSStyleDeclaration对象(与style属性的类型相同),其中包含当前元素的所有计算的样式;
所有的意思就是,style只包含自身样式,不包含其他样式表层叠而来并影响到当前元素的样式信息,
而这个方法可以得到所有的;
<!DOCTYPE html>
<html>
<head>
<title>Computed Styles Example</title>
<style type="text/css">
#myDiv {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
</head>
<body>
<div id="myDiv" style="background-color: red; border: 1px solid black"></div>
</body>
</html>
// 获取style
var myDiv = document.getElementById("myDiv");
var computedStyle = document.defaultView.getComputedStyle(myDiv, null);
alert(computedStyle.backgroundColor); // "red"
alert(computedStyle.width); // "100px"
alert(computedStyle.height); // "200px"
alert(computedStyle.border); // 在某些浏览器中是"1px solid black"
浏览器对综合属性的解释不同,
在设置border时, 实际上是设置了四个边的边框宽度,颜色,样式属性;
border-left-width,border-top-color,border-bottom-style,等等;
因此,即使computedStyle.border不会在所有浏览器中都返回值,
但computedStyle.borderLeftWidth会返回值;
浏览器表示值的方式有所区别,
例如,Firefox和Safari会将所有颜色转换成RGB格式(例如红色是rgb(255,0,0));
因此,在使用getComputedStyle()方法时,最好多在几种浏览器中测试一下;
IE不支持此方法,但是有一个类似的概念,currentStyle属性;
var myDiv = document.getElementById("myDiv");
var computedStyle = myDiv.currentStyle;
alert(computedStyle.backgroundColor); //"red"
alert(computedStyle.width); //"100px"
alert(computedStyle.height); //"200px"
alert(computedStyle.border); //undefined
IE也没有返回border样式,因为这是一个综合属性;
所有的计算样式都是只读的;
计算后的样式也包含属于浏览器内部样式表的样式信息,
因此任何具有默认值的CSS属性都会表现在计算后的样式中;
例如,所有浏览器中的visibility属性都有一个默认值,但这个值会因实现而异;
在默认情况下,有的浏览器将visibility属性设置为”visible”,
而有的浏览器则将其设置为”inherit”;
换句话说,不能指望某个CSS属性的默认值在不同浏览器中是相同的;
如果你需要元素具有某个特定的默认值,应该手工在样式表中指定该值;
操作样式表
CSSStyleSheet类型表示的是样式表,包括通过<link>
元素包含的样式表和在<style>
元素中定义的样式表;
CSSStyleSheet继承自StyleSheet,继承的属性有:
disabled,href,media,ownerNode,parentStyleSheet,title,type;
除了disabled属性,其他属性都是只读的;
CSSStyleSheet还支持下面属性和方法;
cssRules,ownerRule,deleteRule(index),insertRule(rule, index);
DOM规定了一个包含CSSStyleSheet对象的属性,名叫sheet;
function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
//取得第一个<link/>元素引入的样式表
var link = document.getElementsByTagName("link")[0];
var sheet = getStylesheet(link);
是的,IE支持的是styleSheet;
getStyleSheet()返回的样式表对象与document.styleSheets集合中的样式表对象相同;
CSS规则
CSSRule对象表示样式表中的每一条规则;
包含属性:cssText,parentRule,parentStyleSheet,selectorText,style,type;
div.box {
background-color: blue;
width: 100px;
height: 200px;
}
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules; //取得规则列表
var rule = rules[0]; //取得第一条规则
alert(rule.selectorText); //"div.box"
alert(rule.style.cssText); //完整的CSS 代码
alert(rule.style.backgroundColor); //"blue"
alert(rule.style.width); //"100px"
alert(rule.style.height); //"200px"
// 可以修改
rule.style.backgroundColor = "red"
以这种方式修改规则会影响页面中适用于该规则的所有元素;
创建规则
insertRule()方法;
sheet.insertRule("body { background-color: silver }", 0); //DOM方法
IE8及更早版本中addRule();
有关这个方法的规定中说,最多可以使用addRule()添加4095条样式规则;
超出这个上限的调用将会导致错误;
兼容时刻;
function insertRule(sheet, selectorText, cssText, position){
if (sheet.insertRule){
sheet.insertRule(selectorText + "{" + cssText + "}", position);
} else if (sheet.addRule){
sheet.addRule(selectorText, cssText, position);
}
}
删除规则
deleteRule(),IE中removeRule();
function deleteRule(sheet, index){
if (sheet.deleteRule){
sheet.deleteRule(index);
} else if (sheet.removeRule){
sheet.removeRule(index);
}
}
与添加规则相似,删除规则也不是实际Web开发中常见的做法;
考虑到删除规则可能会影响CSS层叠的效果,因此请大家慎重使用;
元素大小
本节介绍的属性和方法并不属于”DOM2 级样式”规范,但却与HTML元素的样式息息相关;
DOM中没有规定如何确定页面中元素的大小;
IE为此率先引入了一些属性,以便开发人员使用;
目前,所有主要的浏览器都已经支持这些属性;
偏移量
offsetHeight,offsetWidth,offsetLeft,offsetTop;
其中,offsetLeft和offsetTop属性与包含元素有关,
包含元素的引用保存在offsetParent属性中;
offsetParent属性不一定与parentNode的值相等;
例如,<td>
元素的offsetParent是作为其祖先元素的<table>
元素,
因为<table>
是在DOM层次中距<td>
最近的一个具有大小的元素;
要想知道某个元素在页面上的偏移量,
将这个元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,
如此循环直至根元素,就可以得到一个基本准确的值;
function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
function getElementTop(element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
所有这些偏移量属性都是只读的,而且每次访问它们都需要重新计算;
因此,应该尽量避免重复访问这些属性;
客户区大小
元素的客户区大小(client dimension),指的是元素内容及其内边距所占据的空间大小;
clientWidth,clientHeight;
客户区大小就是元素内部的空间大小,因此滚动条占用的空间不计算在内;
function getViewport() {
if (document.compatMode == "BackCompat") {
return {
// IE7之前的版本
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}
与偏移量相似,客户区大小也是只读的,也是每次访问都要重新计算的;
滚动大小
滚动大小(scroll dimension),指的是包含滚动内容的元素的大小;
有些元素(例如<html>
元素),即使没有执行任何代码也能自动地添加滚动条;
但另外一些元素,则需要通过CSS的overflow属性进行设置才能滚动;
scrollHeight,scrollWidth,scrollLeft,scrollTop;
确定元素大小
getBoundingClientRect();
这个方法返回会一个矩形对象,包含4个属性:left,top,right和bottom;
function getBoundingClientRect(element) {
if (typeof arguments.callee.offset != "number") {
var scrollTop = document.documentElement.scrollTop;
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
}
是的,为了IE;
IE8及更早版本认为文档的左上角坐标是(2,2),而其他浏览器包括IE9则将传统的(0,0)作为起点坐标;
这个函数使用了它自身的属性来确定是否要对坐标进行调整;
第一步是检测属性是否有定义,如果没有就定义一个;
最终的offset会被设置为新元素上坐标的负值,
实际上就是在IE中设置为2,在Firefox和Opera中设置为0;
为此,需要创建一个临时的元素,将其位置设置在(0,0),然后再调用其getBoundingClientRect();
而之所以要减去视口的scrollTop,是为了防止调用这个函数时窗口被滚动了;
这样编写代码,就无需每次调用这个函数都执行两次getBoundingClientRect()了;
接下来,再在传入的元素上调用这个方法并基于新的计算公式创建一个对象;
对于不支持getBoundingClientRect()方法的浏览器,可以;
function getBoundingClientRect(element) {
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect) {
if (typeof arguments.callee.offset != "number") {
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
} else {
var actualLeft = getElementLeft(element);
var actualTop = getElementTop(element);
return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
}
由于这里使用了arguments.callee,所以这个方法不能在严格模式下使用;