您的位置:首页 > Web前端 > CSS

第十二章:DOM2 和 DOM3(样式)

2017-05-02 16:28 309 查看
DOM2 和 DOM3
样式
访问元素的样式
DOM样式属性和方法

计算的样式

操作样式表
CSSRule

创建规则

删除规则

元素大小
偏移量

客户区大小

滚动大小

确定元素大小

DOM2 和 DOM3

样式

在HTML中定义样式的方式有3种:通过
<link/>
元素包含外部样式表文件,通过
<style>
元素定义嵌入式样式,以及使用style特性定义针对特定元素的样式。DOM Level 2 Style围绕这3个应用样式的机制提供了一套API。可以用下列代码确定浏览器是否支持DOM Level 2 Style。

var supportsDOM2CSS = document.implementation.hasFeature("CSS", "2.0");
var supportsDOM2CSS2 = document.implementation.hasFeature("CSS2", "2.0");


访问元素的样式

任何支持style特性的HTML元素在JavaScript中都有一个对应的style属性。这个style对象是CSSStyleDeclaration的实例,包含这通过HTML的style特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。在style特性中指定的任何CSS属性都将表现为这个style对象的相应属性。对于有短划线的CSS属性名,必须将其转换为驼峰大小写形式。

CSS属性JavaScript属性
background-imagestyle.backgroundImage
colorstyle.color
displaystyle.display
font-familystyle.fontFamily
- 有个属性需要注意,那就是float。在JS中float是保留字,因此不能做属性名(书上原话)。经过前面的阅读可知,使用保留字未必会发生错误,也许最新的浏览器都已经可以使用float了。实验下来结果如下:

<div id="myDiv" style="float: left"></div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
console.log(div.style.float);//IE9、8:left  Chrome:left
console.log(div.style.cssFloat);//IE9:left IE8:undefined Chrome:left
console.log(div.style.styleFloat);//IE9、8:left  Chrome:undefined
</script>


只要取得一个有效的DOM元素的引用,就可以随时通过JavaScript为其设置样式。

<div id="myDiv" style="float: left">1</div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
div.style.backgroundColor = "red";
div.style.width = "100px";//在混杂模式下不加px后缀没关系,在标准模式下会失效。
div.style.height = "200px";
</script>


通过style对象同样可以取得在style特性中指定的样式

<div id="myDiv" style="float: left;background-color: red;">1</div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
alert(div.style.backgroundColor);//red
alert(div.style.float);//left
</script>


如果没有为元素设置style特性,那么style对象中可能包含一些默认的值,但这些值并不能反映该元素的特性。

<!DOCTYPE html>
<html>
<head>
<title>DOM Level 2 IFrame Example</title>
<style>
#myDiv {
background-color: red;
}
</style>
</head>
<body>
<div id="myDiv" style="float: left">1</div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
alert(div.style.backgroundColor);//"" 而不是"red"
</script>
</body>
</html>


DOM样式属性和方法

下面是DOM Level 2 style规范为style对象定义的属性和方法:

cssText:返回style特性中的css代码

length:应用给元素的CSS属性的数量

parentRule:表示CSS信息的CSSRule对象(后续讨论)。

getPropertyCSSValue(propertyName):返回包含给定属性值的CSSValue对象(后续讨论)。

getPropertyPriority(propertyName):如果给定的属性使用了!important设置(提高指定CSS样式规则的应用优先权),则返回”important”,否则返回空字符串。

getPropertyValue(propertyName):返回给定属性的字符串值

item(index):返回给定位置的CSS属性的名称(等同方括号语法,不过返回的是名称而不是

removeProperty(propertyName):删除属性

setProperty(propertyName,value,priority):添加或设置属性,并加上优先权标志(”important”或”“)

来看下面的例子

<div id="myDiv" style="float:left;background-color:  red;">1</div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
//冒号后面的空格会被设置为1个。无论原格式如何,可见内部进行过解析。
console.log(div.style.cssText);//"float: left; background-color: red;"
//重写cssText,会移除原有属性,重新添加属性。
div.style.cssText = "float:right"//省略分号
console.log(div.style.cssText);//"float: right;" 不再保留background-color属性,且结果同样也多了空格和分号

//length的作用主要是拿来遍历
var test_div = document.createElement("div");
test_div.style.cssText = "width: 25px; height: 100px; background-color: red;";
console.log(test_div.style.length);//3
for (var i=0, len=test_div.style.length; i<len; i++) {
console.log(test_div.style[i]);//等价于 test_div.style.item(i)。
// 这里和普通的方括号语法不同地方是,在这里会返回propertyName而不是value。注意此时取到的不是驼峰命名!
}
console.log(test_div.style["width"]);//另外一种方括号用法,等价于test_div.style.getPropertyValue("width")
console.log(test_div.style.getPropertyValue("width"));//同上

//console.log(test_div.style.getPropertyCSSValue("width")); //IE、Chrome 没有这个方法
console.log(test_div.style.parentRule); //null 目前还不知道有什么用
console.log(test_div.style.getPropertyPriority("width"));//""
test_div.style.setProperty("width", "50px", "important");
console.log(test_div.style.cssText);//"width: 50px !important; height: 100px; background-color: red;"
console.log(test_div.style.getPropertyPriority("width"));//"important"
test_div.style.removeProperty("width");
console.log(test_div.style.cssText);//"height: 100px; background-color: red;"
</script>


书上说了一些getPropertyCSSValue(propertyName)getPropertyValue(propertyName)的区别。后者只返回属性值的字符串表示。而前者返回一个包含两个属性的CSSValue对象,这两个属性分别是cssTextcssValueType。其中cssText属性的值和getPropertyValue(propertyName)获得的字符串相同,而cssValueType属性则是一个数值常量。0表示继承,1表示基本的值,2表示值列表,3表示自定义的值。书上说IE 9+、Safari 3+、Chrome支持这个方法?!反正我测试是not a function。这里就不再过多研究了。

计算的样式

所谓计算的样式,就是元素最终的样式!前面提到的关于style对象的一些方法和属性只能够获得style特性下的样式信息,它并不包含那些从其他样式层叠而来并影响当前元素的样式信息。DOM Level 2 Style增强了document.defaultView,提供了getComputedStyle()方法。该方法接收2个参数,第一个参数是需要取得计算样式的元素,第二个参数是一个伪元素字符串(例如”:after”,这个概念涉及其他知识:伪元素)。该方法同样也返回一个CSSStyleDeclaration对象(与style属性的类型相同),我们可以根据这个对象获得计算后的样式信息。

<!DOCTYPE html>
<html>
<head>
<title>Computed Styles Example</title>
<style type="text/css">
#myDiv {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
<script type="text/javascript">
function showComputedStyles(){
var myDiv = document.getElementById("myDiv");
var computedStyle = document.defaultView.getComputedStyle(myDiv, null);
//前者Chrome 后者IE9
console.log(computedStyle.backgroundColor);   //"rgb(255, 0, 0)"
console.log(computedStyle.width);             //"99.9844px" "100px"
console.log(computedStyle.height);            //"199.992px" "200px"
console.log(computedStyle.border);            //"1.5px solid rgb(0, 0, 0)" ""
console.log(computedStyle.borderLeftWidth);   //"1.5px" "1px"
console.log(computedStyle.visibility);    //visible
console.log(computedStyle.float);    //none undefined
}

</script>
</head>
<body>
<div id="myDiv" style="background-color: red; border: 1px solid black"></div>
<input type="button" value="Show Computed Styles" onclick="showComputedStyles()">
<p>(This example won't work in IE < 9.)</p>
</body>
</html>


结果非常地怪异,真是难以理解。在Chrome中怎么蹦出了一个小数(后来过了几天我又试了一下,结果居然正确了(不再显示小数),我这几天刚刚更新了Chrome的版本,难道是最近刚刚修复了这个bug?)?IE9中的border属性怎么没有了?也许以后我可能会知道,但是我现在的确想不出来。不过总的来说有两点,1是会得到该元素最终的样式信息对象(重写会覆盖)。2是没有设置的属性会有默认值(比如float属性,虽然IE中出现了undefined,但是Chrome中是none)

更神奇的是,书上居然说IE不支持getComputedStyle()方法(应该是笔误,是指IE8-不支持,也有可能是因为我看的是盗版的书)。

//替换上面例子中的方法
function showComputedStyles(){
var myDiv = document.getElementById("myDiv");
//IE 中支持currentStyle属性,返回一个CSSStyleDeclaration实例
var computedStyle = myDiv.currentStyle;
alert(computedStyle.backgroundColor);   //"red"
alert(computedStyle.width);             //"100px"
alert(computedStyle.height);            //"200px"
//IE 不返会border样式,因为这个是一个综合属性(主要看浏览器心情)
//border指定了border-left-width、border-top-width等等属性
alert(computedStyle.border);            //undefined IE8之前
alert(computedStyle.border == "");      //IE9之后变成了空字符串
alert(computedStyle.borderLeftWidth);   //"1px"
}


操作样式表

CSSStyleSheet 类型表示的是样式表,包括通过
<link>
元素包含的样式表和在
<style>
元素中定义的样式表,这两个元素本身是由HTMLLinkElementHTMLStyleElement类型表示的。CSSStyleSheet类型只表示样式表,而不管这些样式表在HTML中是如何定义的。HTMLLinkElementHTMLStyleElement类型是允许修改HTML特性,但CSSStyleSheet对象则是一套只读的接口(有一个属性disabled除外)。使用下面的代码可以确定浏览器是否支持DOM 2级样式表

var supportsDOM2StyleSheet = document.implementation.hasFeature("StyleSheets", "2.0");


CSSStyleSheet继承自StyleSheet。从StyleSheet接口继承而来的属性如下:

disabled:表示样式表是否禁用(可读写)。设置为true可以禁用样式表。

href:如果样式表是通过
<link>
元素包含的,则是样式表的URL,否则是null。

media:当前样式表支持的所有媒体类型的集合。这个集合也有length属性和item()方法,也可以通过方括号语法取得集合中的项。如果集合是空列表,表示适用于所有媒体。在IE中,media是一个反映
<link>
<style>
元素中media特性的字符串。

ownerNode:指向拥有当前样式表的节点的指针(一般是
<link>
<style>
元素节点)
13141
。如果样式表是其他样式表通过@import导入的,则这个值为null。IE不支持该属性。

parentStyleSheet:如果当前样式表是通过@import导入的,则这个属性指向导入它的样式表的指针。

title:title属性的值。

type:表示样式表类型的字符串。对CSS样式表而言,这个字符串是”type/css”。

上面都是继承而来的属性和方法,另外CSSStyleSheet类型还支持以下属性和方法:

cssRules:样式表包含的样式规则的集合。IE8-不支持这个属性,但IE都有一个类似的rules属性。

ownerRule:如果样式表是@import导入的,这个属性就是一个指针,指向表示导入的规则;否则为null。IE8-不支持这个属性。

deleteRule(index):删除cssRules集合中指定位置的规则。IE8-不支持这个方法,但IE都支持一个类似的removeRule(index)。

insertRule(rule, index):向cssRules集合中指定的位置插入rule字符串。IE8-不支持这个方法,但IE都支持一个类似的addRule()方法。

应用于文档的所有样式表是通过document.styleSheets集合表示的。同样也有length和item()。

<!DOCTYPE html>
<html>
<head>
<title>Style Sheets Example</title>
<link rel="stylesheet" title="blah" type="text/css" href="stylesheet1.css">
<!--不同浏览器的document.styleSheets返回的样式表有所不同,所有浏览器都会返回rel特性为"stylesheet"的样式表
而IE和Opera(我的Chrome也行,其他没求证过)也包含rel特性为"alternate stylesheet"的样式表-->
<link rel="alternate stylesheet" type="text/css" href="stylesheet2.css">
<style type="text/css">
#myDiv {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
<script type="text/javascript">
function outputStyleSheets(){
var sheet = null;
//这里再强调一下,不使用len而直接写在'i<'后面会影响效率,且如果函数体中包含影响length属性的代码,会出现错误。
for (var i=0, len=document.styleSheets.length; i < len; i++){
sheet = document.styleSheets[i];
alert(sheet.href);//style元素返回null IE8- style元素返回""
}
}

function toggleStyleSheet(){
/*body {
background-color: silver;
}*/
//点击可以禁用/启用样式,立即改变背景颜色。
document.styleSheets[0].disabled = !document.styleSheets[0].disabled;
}
</script>
</head>
<body>
<div id="myDiv"></div>
<input type="button" value="Output Style Sheets" onclick="outputStyleSheets()">
<input type="button" value="Enable/Disable Style Sheet" onclick="toggleStyleSheet()">
</body>
</html>


也可以直接通过元素节点的sheet属性获得CSSStyleSheet对象。IE8-不支持该属性,IE8-仅支持styleSheet属性。IE9+后两个属性都支持。

function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
var link = document.getElementsByTagName("link")[0];
var sheet = getStyleSheet(link);
console.log(sheet.href);
console.log(sheet.media);//MediaList
console.log(sheet.ownerNode == link); //true
console.log(sheet.title);//例子中是blah 不设就是null
console.log(sheet.type);//text/css
console.log(sheet.cssRules);//CSSRuleList
console.log(sheet.cssRules[0].cssText);//body { background-color: silver; }


下面是一个关于CSSRule的例子:

function getCssRule(sheet){
return sheet.cssRules || sheet.rules;
}
var link = document.getElementsByTagName("link")[0];
var sheet = getStyleSheet(link);
var cssRuleList = getCssRule(sheet);
console.log(cssRuleList[0].cssText);//body { background-color: silver; }  IE8- undefined 下面会介绍这个cssText属性
console.log(sheet.ownerRule);//null IE8- undefined
var stylerule = "body { border-color: red;border-style: ridge;}";
if (typeof sheet.insertRule == "function") {
sheet.insertRule(stylerule, 0);//IE9+
} else {
sheet.addRule("body", "border-color: red; border-style: ridge;", 0);//IE都支持
}
console.log(cssRuleList[0].cssText);//body { border-color: red; border-style: ridge; }
if (typeof sheet.deleteRule == "function") {
sheet.deleteRule(1);//IE9+
} else {
sheet.removeRule(1);//IE都支持
}


CSSRule

CSSRule对象表示样式表中的每一条规则(比如body{…}就是一条规则)。实际上,CSSRule是一个供其他类型继承的基类型,其中最常见的就是CSSStyleRule类型,表示样式信息。CSSStyleRule类型包含以下属性:

cssText:返回整条规则对应的文本。书上老说IE不支持,我发现IE9+都支持的。而不同浏览器的返回值可能有小区别(大小写)。

parentRule:如果当前规则是导入规则,这个属性引用的就是导入规则;否则这个值为null。

parentStyleSheet:当前规则所属的样式表。

selectorText:返回当前规则的选择符文本(比如body)。Opera允许修改该属性,其余浏览器该属性只读。

style:一个CSSStyleDeclaration对象(刚开始已经详细介绍过了),可以通过它设置和取得规则中特定的样式值。

type:表示规则类型的常量值。对于样式规则,这个值是1。

其中最常用的属性是cssText、selectorText和style。cssText属性与style.cssText属性类型。但前者包含选择符和花括号,后者只有花括号里面的内容。此外cssText是只读的,而style.cssText可以被重写。

<!DOCTYPE html>
<html>
<head>
<title>CSS Rules Example</title>
<style type="text/css">
div.box {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
<script type="text/javascript">

function getStyleInfo(){
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;//后者适用于IE8-
var rule = rules[0];
alert(rule.selectorText);//div.box
alert(rule.cssText);//div.box{...}
rule.cssText = "div.box { background-color: red;  width: 100px;  height: 200px; }";
alert(rule.cssText);//不变,说明上一句代码无效(cssText只读不可写 Writable为false?)
var cssText = Object.getOwnPropertyDescriptor(rule, "cssText");//undefined 也许DOM对象不能这么用?
//alert(cssText.writable);
alert(rule.style.cssText);//div.box{...}中的...
alert(rule.style.backgroundColor);//blue
alert(rule.style.width);//100px
alert(rule.style.height);//200px
}

//可以使用下列方式重写样式规则
function changeStyleInfo(){
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;
var rule = rules[0];
rule.style.backgroundColor = "red";
}
</script>
</head>
<body>
<div class="box" style="margin-bottom: 10px"></div>
<div class="box"></div>
<input type="button" value="Get Style Info" onclick="getStyleInfo()">
<input type="button" value="Change Style Info" onclick="changeStyleInfo()">
</body>
</html>


创建规则

其实这个前面已经提到过了。使用insertRule(rule, index);可以样式表中添加新规则。而IE8-只支持addRule(selectorText, cssText, index);(据说这个方法最多创建上限4095条)

删除规则

这个前面也提到过了,使用deleteRule(index);IE8-只支持removeRule(index)

元素大小

DOM中没有规定如何确定页面的大小。IE 率先引入了一些属性,目前所有主流浏览器都已经支持这些属性。

偏移量

首先要介绍的是偏移量(offset dimension),包括元素在屏幕上占用的所有可见的空间。元素的可见大小由其高度宽度决定,包括所有内边距滚动条边框大小(不包括外边距)。通过下面四个属性可以取得元素的偏移量。

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;
}
//top同理


要注意这4个属性是只读的。并且每次访问他们都需要重新计算(至于为什么访问值也会计算,想想访问器属性就知道了)。应该尽量避免重复访问这些属性。如果需要重复使用,可以保存在局部变量中,不过要在距离不会修改的前提下。

客户区大小

clientWidth和clientHeight属性表示的是元素内容及内边距的长和宽。



获得页面视口大小,可以通过document.documentElement(即文档节点)的clientWidth和clientHeight属性。IE7之前的版本中得使用document.body的这两个属性去获取。

<!--注意要写在body内部,如果写在head里面document.body为null-->
<script>
console.log(document.documentElement.clientWidth);//杂项下为0 其余正常 但是都有区别
console.log(document.body.clientWidth);//都是正常数字,但是都有区别
</script>


这两个属性也是只读的,每次访问都会重新计算。

滚动大小

最后介绍的是滚动大小(scroll dimension),指的是包含滚动内容的元素的大小。有些元素(例如
<html>
元素)即使没有执行任何代码也能自动地添加滚动条。有些元素则需要设置CSS的overflow属性才能滚动,下面是关于滚动大小的4个属性:

scrollHeight:在没有滚动条的情况下,元素内容的总高度。

scrollWidth:在没有滚动条的情况下,内容的总宽度。

scrollLeft:被隐藏在内容区域左侧的像素数,通过设置这个属性可以改变元素的滚动位置。

scrollTop:同理,被隐藏在内容区域上册的像素数,通过设置这个属性可以改变元素的滚动位置。



scrollHeight和scrollWidth主要用于确定元素内容的实际大小。例如带有垂直滚动条的页面总高度为document.documentElement.scrollHeight。(在IE7之前为document.body.scrollHeight,因为IE7以前的版本将body元素视为浏览器的视口中的元素)

<!DOCTYPE html>
<html>
<head>
<title>Offset Dimensions Example</title>
<meta charset="utf-8">
<style>
.test {
width: 500px;height:500px;overflow: auto
}
.test div{
width: 1000px;height:800px;
background-color: red;
}
</style>
</head>
<body>
<div class="test">
<div>1</div>
</div>
<button onclick="totop()">回到顶部</button>
<button onclick="tobottom()">回到底部</button>
<button onclick="show()">显示信息</button>
<button onclick="showWindow()">显示页面信息</button>
<script>
var div = document.getElementsByClassName("test")[0];
function totop() {
div.scrollTop = 0;
}
function tobottom() {
div.scrollTop = div.scrollHeight - div.clientHeight;
}
function show() {
alert(div.scrollWidth);
alert(div.scrollHeight);
alert(div.scrollLeft);
alert(div.scrollTop);
}
function showWindow() {
//以下结果为Chrome测试结果
//第一个539是body的高度,而768则是整个页面的高度。当我用调试窗口显示在浏览器下方时,clientHeight会变小
//但是scrollHeight没有发生变化
alert(document.documentElement.scrollWidth);//1440
alert(document.documentElement.scrollHeight);//539
alert(document.documentElement.clientWidth);//1440
alert(document.documentElement.clientHeight);//768
}
</script>
</body>
</html>


而对于不包含滚动条的页面,scrollWidth、scrollHeight、clientWidth和clientHeight之间的关系并不十分清晰。我们对同一个页面获取document.documentElement的这些属性,见上面的showWindow()函数。

确定元素大小

主流浏览器为每个元素提供了getBoundingClientRect()方法。这个方法会返回一个矩形对象,包含4个属性:left、top、right、bottom。这个属性给出了元素在页面中相对于视口的位置。IE8及更早版本认为文档的左上角左边是(2,2),而其他浏览器包括IE9则认为是(0,0),可以通过下面的函数:

//"use strict";
//var getBoundingClientRect = function fname(element) {
function getBoundingClientRect(element){
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect){
//if (typeof fname.offset != "number"){
if (typeof arguments.callee.offset != "number"){
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
//fname.offset = -temp.getBoundingClientRect().top - scrollTop;
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}

var rect = element.getBoundingClientRect();
//var offset = fname.offset;
var offset = arguments.callee.offset;

return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset

};
} else {//如果不支持getBoundingClientRect()方法
//这两个方法在前面提到过通过while将offsetLeft累加获得
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
}
}
}


上面的这个方法创建了一个div元素并获取它的坐标信息。然后将offset保存在该方法中,下次调用则直接使用offset变量。

注意到上面用到了arguments.callee属性,该属性在严格模式下是不能使用的。我们可以使用命名函数表达式的写法去完成(直接写成getBoundingClientRect耦合性太高)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript