-
三位一体的网页
-
结构层
网页的结构层由HTML或XHTML之类的标记语言负责创建。标签(tag),也就是那些出现在尖括号里的单词,对网页内容的语义含义做出描述,例如,<p>标签表达了这样一种语义:“这是一个文本段”但这些标签并不包含任何关于内容如何显示的信息。
<p>An example of a paragraph </p>
-
表示层
由css负责完成。css描述页面内容应该如何呈现。你可以定义这样一个css来声明:“文本段应该使用灰色的Arial字体和另外几种scan-serif字体来显示。
p{
color:grey;
font-family:"Arial",sans-serif;
}
-
行为层
负责内容应该如何响应事件这一问题。是js语言和DOM主宰的领域。
分离
- 使用HTML去搭建文档的结构
- 使用CSS去设置文档的呈现
- 使用DOM脚本去实现文档的行为
不过,这三种技术之间存在着一些潜在的重叠区域,用DOM可以改变网页的结构,诸如createElement和appendChild之类的DOM方法允许你动态地创建和添加标记。
在CSS上也有这种技术相互重叠的例子。诸如:hover和focus之类的伪类允许你根据用户触发事件改变元素的呈现效果。改变元素的呈现效果当然是表示层的势力范围,但响应用户触发事件却是行为层的领地。
-
style属性
文档中的每个元素都是一个对象,每个对象又有着各种各样的属性。有一些属性告诉我们元素在节点树上的位置。比如parentNode、netxtSibling、previousSilbling、childNodes、firstChild和lastChild属性,告诉我们各节点之间关系信息。
其他一些属性(比如rodeType和nodeName属性)包含元素本身的信息。比如说对某个元素的rodeName属性进行的查询将然后诸如"p"之类的字符串。
除此之外,文档的每一个元素节点还都有一个属性style。style属性包含着元素的样式,查询这个属性将返回一个对象而不是一个简单的字符串。样式都存放在这个style对象的属性里:
element.style.property
下面是一个内嵌样式的<p>元素的例子:
<p id = "example" style = "color:grey;font-family:'Arial',sans-serif;">
An example of a paragraph
</p>
利用style属性,你可以得到这个<p>标签的样式。
首先,需要从文档里把这个元素找出来。只需把id值传递给getElementById方法,再把这个方法的返回值赋值给变量para,就可以通过para变量引用这个p元素了:
var para = document.getElementById("example");
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "utf-8"/>
<title>Example</title>
</head>
<body>
<p id="example" style="color: grey;font-family: 'Arial',sans-serif;">
An example of paragrah
</p>
<script>
window.onload = function () {
// body...
var para = document.getElementById("example");
alert(typeof para.nodeName);
alert(typeof para.style);
}
</script>
</body>
</html>
-
获取样式
你能够得到para变量所代表的<p>标签的样式。为了查出某个浏览器里的显示颜色,我们需要使用style对象的color属性:
element.style.color
这个元素的style属性的color属性是grey
alert("The color is " + para.style.color);
刚才的代码中还设置了<p>元素的另一个CSS属性font-family。这个属性的获取方式与color属性略有不同。你不能简单地查询style对象的font-family,因为font和family之间的连字符与js语言中的减法操作符相同,js会把它解释为减号。如果像下面这样去访问名为font-family的属性,就会收到一条出错信息:
element.style.font-family
js将把减号前面的内容解释为“元素的style属性的font属性”把减号后面的内容解释为一个名为family的变量,把整个表达式解释为一个减号运算。
减号和加好之类的操作符是保留字符,不允许用在函数或变量的名字里。这同时意味着他们也不能用在方法或属性的名字里。
当你需要引用一个中间带减号的CSS属性时,DOM要求你用驼峰命名法。CSS属性font-family变为DOM属性fontFamily:
element.style.fontFamily
alert("The font family is " + para.style.fontFamily);
不管CSS样式属性的名字里有多少个连字符,DOM一律采用驼峰命名法来表示它们。
也可以用DOM来设置样式
element.style.property = value
-
何时应该用DOM脚本设置样式
在绝大多数情况下,还是应该用css去声明样式。就像你不应该利用DOM去创建重要的内容那样,你也不应该利用DOM为文档设置重要的样式。
不过在CSS不方便的场合,还是可以利用DOM对文档的样式做一些小的增强。
- 根据元素在节点树里的位置来设置样式
通过CSS声明样式的具体做法主要有三种。第一种是为标签元素统一地声明样式,
p{
font-size:1en;
}
第二种是为特定class属性的所有元素同意声明样式
.frneprint
{
font-size:.8cm;
}
第三种是为有独一无二的id属性的元素单独声明样式。
#intro
{
font-size:1.2em;
}
也可以为有类似属性的多个元素声明样式
input[type* = "text"]{
font-size:1.2em;
}
在现代浏览器中,甚至可以根据元素的位置声明样式:
p:first-of-type{
font-size:2em;
font-weight:bold;
}
CSS还无法根据元素之间的想对位置关系找出某个特定的元素,但这对DOM来说却不是什么难题。我们可以利用DOM轻而易举地找出文档中的所有h1元素,然后再同样轻而易举地找出紧跟在每个h1元素后面的那个元素,并把样式添加给它。
function styleHeaderSiblings() {
if(!document.getElementsByTagName) return false;
//用getElementByTagName方法把所有的h1元素找出来
var headers = document.getElementsByTagName("h1");
var elem;
//遍历这个节点集合里所有元素;
for (var i = 0; i < headers.length; i++) {
//把当前h1元素的nextSibling节点作为参数传递给getNextElement函数
//并把这个值赋值给elem变量
elem = getNextElement(headers[i].nextSibling);
elem.style.fontWeight = "bold";
elem.style.fontSize = "1.2em";
}
}
//获取下一个元素节点
function getNextElement(node){
if(node.nodeType == 1)
{
return node;
}
if(node.nextSibling){
return getNextElement(node.nextSibling);
}
return null;
}
-
根据某种条件反复设置某种样式
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "utf-8"/>
<title>Cities</title>
</head>
<body>
<table>
<caption>Itinerary</caption>
<thead>
<tr>
<th>When</th>
<th>Where</th>
</tr>
</thead>
<tbody>
<tr>
<td>June 9th</td>
<td>Portland,<abbr title="Oregon">OR</abbr></td>
</tr>
<tr>
<td>June 10th</td>
<td>Seattle,<abbr title="Washington">CA</abbr></td>
</tr>
<tr>
<td>June 12th</td>
<td>Sacramento, <abbr title="California">CA</abbr></td>
</tr>
</tbody>
</table>
</body>
<script src="example.js"></script>
</html>
body
{
font-family: "Helvetice","Arial",serif;
background-color: #fff;
color: #000;
}
table{
margin: auto;
border: :1px solid #699;
}
caption{
margin: auto;
padding: .2em;
font-size: 1.2em;
font-weight: bold;
}
th{
font-weight: normal;
font-style: italic;
text-align: left;
border: 1px dotted #699;
background-color: #9cc;
color:#000;
}
th,td{
width:10em;
padding: .5em;
}
让表格里的行更可读的常用技巧是交替改变它们的背景颜色从而形成斑马效果,使相邻的两行泾渭分明。通过分别设置奇数行和偶数行样式的办法可实现这种效果。如果浏览器支持CSS3,那就很简单,只需要如下两行样式:
tr:nth-child(odd) {background-color: #ffc;}
tr:nth-child(even) {background-color: #fff;}
js实现此功能:
function stripeTables()
{
if(!document.getElementsByTagName) return false;
//获取文档中所有table元素
var tables = document.getElementsByTagName("table");
//遍历
var odd,rows;
for (var i = 0; i < tables.length; i++) {
odd = false;
rows = tables[i].getElementsByTagName("tr");
for(var j = 0 ;j<rows.length;j++)
{
if(odd == true)
{
rows[j].style.backgroundColor = "#ffc";
odd = false;
}
else
{
odd = true;
}
}
}
}
addLoadEvent(stripeTables);
用displayAbbreviations函数也适用于这个文档。
-
响应事件
实现鼠标悬停加粗
css:
tr:hover{
font-weight: bold;
}
DOM:
function highlightRows() {
if(!document.getElementsByTagName) return false;
var rows = document.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
rows[i].onmouseover = function(){
this.style.fontWeight = "bold";
}
rows[i].onmouseout = function(){
this.style.fontWeight = "normal";
}
}
}
addLoadEvent(highlightRows);
-
className属性
如果你引用外部CSS样式表,并且其中一条针对.intro类的样式声明:
.intro{
font-weight: bold;
font-size: 1.2em;
}
现在只需在styleHeaderSiblings()函数里把紧跟在一级标题之后的那个元素的class属性设置为intro就可以达到同样的目的。
elem.setAttribute("class","intro");
更简单的办法是更新className属性得到一个元素的class属性:
element.className
用className属性和赋值操作符设置一个元素的class属性:
element.className = value
下面是利用className属性编写出来的styleHeaderSiblings函数,它在设置样式时不需要直接与style属性打交道:
function styleHeaderSiblings() {
if(!document.getElementsByTagName) return false;
//用getElementByTagName方法把所有的h1元素找出来
var headers = document.getElementsByTagName("h1");
var elem;
//遍历这个节点集合里所有元素;
for (var i = 0; i < headers.length; i++) {
//把当前h1元素的nextSibling节点作为参数传递给getNextElement函数
//并把这个值赋值给elem变量
elem = getNextElement(headers[i].nextSibling);
elem.className = "intro";
// elem.style.fontWeight = "bold";
// elem.style.fontSize = "1.2em";
}
}
这个技巧有一个不足之处,通过className属性设置某个元素的class属性时将替换(而不是追加)该元素原有的class设置:
在你需要给一个元素追加新的class时,按以下步骤操作:
.odd{
background-color: #ffc;
}
function addClass(element,value) {
//检测className属性的值是否为null
//如果是,把新的class设置值直接赋值给className属性
//如果不是,把一个空格和新的class设置追加到className属性上去。
if(!element.className){
element.className = value;
}
else
{
newClassName = element.className;
newClassName = " ";
newClassName = value;
element.className = newClassName;
}
}
function stripeTables()
{
if(!document.getElementsByTagName) return false;
//获取文档中所有table元素
var tables = document.getElementsByTagName("table");
//遍历
var odd,rows;
for (var i = 0; i < tables.length; i++) {
odd = false;
rows = tables[i].getElementsByTagName("tr");
for(var j = 0 ;j<rows.length;j++)
{
if(odd == true)
{
//rows[j].style.backgroundColor = "#ffc";
addClass(rows[j],"odd");
odd = false;
}
else
{
odd = true;
}
}
}
}
addLoadEvent(stripeTables);
-
对函数进行抽象
把一个非常具体的东西改进为一个较为通用的东西的过程叫做抽象。
将styleHeaderSiblings改写成下面
function styleElementSiblings(tag,theclass){
if(!document.getElementsByTagName) return false;
var elems = document.getElementsByTagName(tag);
var elem;
for (var i = 0; i < elems.length; i++) {
elem = getNextElement(elems[i].nextSibling);
addClass(elem,theclass);
}
}
现在把h1和intro作为参数传递给这个新函数,就可以获得原来的效果。
addloadEvent(function(){
styleElementSiblings("h1","intro");
});