目录
7.动态创建标记
JavaScript也能用来改变网页的结构和内容,本章介绍了通过DOM方法来创建新元素和修改现有元素来改变网页结构
7.1 一些传统方法
Document.write()
document对象的write()方法可以把字符串插入到文档中,这种方法的缺点在于没有把网页的行为和表现分离开来。并且必须在<body>部分使用<script>标签才能调用。
innerHTML属性
innerHTML属性可以用来读写给定元素里的HTML内容。
接下来试着用DOM的视角看一段代码
<div id='testdiv'>
<p>This is <em>my</em> content</p>
</div>
首先根是元素节点div,div下面有一个属性节点——id=“testdiv” 和一个元素节点<p>,而<p>元素节点下面又有子节点,分别是文本节点“This is” 和 “content” 以及元素节点<em>,元素节点em下面有文本节点“my”。
如果对div使用用innerHTML属性,那么将会获取到<p>This is <em>my</em> content</p>。
但这种技术无法区分替换一段HTML内容或者插入一段HTML内容,一旦使用innerHTML属性,那么他的全部内容都会被替换。
这种方式比document.write()更值得推荐,因为他把JavaScript代码从标记中分离出来了,不需要再在body标签部分插入script标签。
7.2 DOM方法
DOM是文档的表示,在浏览器看来DOM节点树才是文档。之前的setAttribute方法便可以用来改变DOM节点树上的某个属性节点。所以一旦学会用DOM去观察文档,就懂得了什么是动态方式创建标记,实际上并不是在创建标记,而是在改变DOM节点树。一个文档就是一个DOM节点树,要想加入内容,就要插入新的节点,要想添加标记到文档,就要插入一些元素节点。
createElement()方法
使用这个方法的语法是
document.createElement(nodename)
比如下面这条语句可以创建一个新的<p>标签
document.createElement(‘p’)
光是创建一个标签并不能影响页面的表现,还需要把新创建出来的元素插入到文档中去。此时创建出来的p元素节点已经存在,但是他还不是任何DOM节点树的组成部分,这种情况成为文档碎片。下一步我们要做的就是把这个元素节点连接到文档的节点树上。
appendChild()方法
这个方法的语法是
parent.appendChild(child)
实例
var testdiv = document.getElementById('testdiv');
var para = document.createElement('p');
testdiv.appendChild(para)
通过这块代码便可以把p元素节点插入到id为testdiv的节点中去了,p节点成为了testdiv的子节点。
createTextNode()方法
之前的createElement()方法只能创建文本节点,当我们想把文本放入p元素内部时候他帮不上忙。当需要创建一个文本节点时候使用createTextNode(()方法来实现。
语法和createElement相似
document.createTextNode(text)
练习一个实例
用DOM方法创建
<p>this is <em>my</em> content</p>
思路: 1.创建一个p元素节点赋给变量para
2.创建一个文本节点赋给变量txt1
3.把txt1追加到para上
4.创建一个em元素节点赋值给empasis
5.创建一个文本节点赋给变量txt2
6.把txt2追加到emphasis上
7.把emphasis追加到para上
8.创建一个文本节点并赋给txt3
9.把txt3追加到para上
代码:
function test() {
var para = document.createElement('p');
var txt1 = document.createTextNode('This is');
para.appendChild(txt1);
var emphasis = document.createElement('em');
var txt2 = document.createTextNode('my');
emphasis.appendChild(txt2);
para.appendChild(emphasis);
var txt3 = document.createTextNode('content');
para.appendChild(txt3);
}
7.3 重回图片库
在已有元素前插入一个新元素
使用DOM提供的InsertBefore()方法,需要提供三个信息
(1)新元素:你想插入的元素
(2)目标元素:想把这个新元素插入到哪个元素之前
(3)父元素:目标元素的父元素
调用语法:
parentElement.insertBefore(newElement,targetElement)
在已有元素后插入一个新元素
很可惜的是DOM并没有提供insertAfter()方法,但是我们可以自己编写一个这样的函数来实现
function insertAfter(newElment, targetElement) {
var parent = targetElement.parentNode;
if (parent.lastChild == targetElement) {
parent.appendChild(newElment);
} else {
parent.insertBefore(newElment, targetElement.nextsibling);
}
}
其中最重要的一步就是检测父元素的最后一个子元素是不是targetElement,如果是,则直接用appendChild方法插入新元素,如果不是则使用目标的元素的nextSibling属性,他的意思是找到目标元素的下一个兄弟元素,在这个兄弟元素之前插入新元素就是在目标元素之后插入新元素。
7.4 Ajax
目前没看懂。
8.充实文档的内容
上一章节学会了利用DOM方法来动态创建标记,这一节将继续在实践中应用这些技术。
- 一个为文档创建“缩列语列表”的函数
- 一个为文档创建“文献来源链接”的函数
- 一个为文档创建“快捷键清单”的函数
8.1 不应该做什么
如果正在使用DOM技术把一些重要的内容添加到网页上,则应该立刻停下里检讨自己的计划和思路,这是在滥用DOM,无法满足渐进增强和平稳退化原则。
8.2 把“不可见”变成“可见”
本章着眼要做的事情就是把一些属性的内容按照自己的想法显示出来,这些属性已经存在于标记之中,利用JavaScript和DOM以另外一种结构呈现他们。
本章将用DOM技术为网页添加一些实用的小部件
- 得到隐藏在属性里的信息
- 创建标记封装这些信息
- 把这些标记插入到文档
8.3 内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Explaining the DOM</title>
<link rel="stylesheet" href="exp.css">
</head>
<body>
<h1>What is the Document Object Model?</h1>
<p>
the <abbr title="World Wide Web Consortium">W3C</abbr> defines the <abbr title="Document Object Model">DOM</abbr> as:
</p>
<blockquote cite="http://www.w3.org/DOM/">
</p>
A platform - and language-neutral interface that will allow prog rams and scripts to dynamically access and update the content,structure and style of document.
</p>
</blockquote>
<p>
It is an <abbr title="Application Programming Interface">API</abbr> that can be used to navigate <abbr title="HyperText Markup Language">HTML</abbr> and <abbr title="Extensible Markup Language">XML</abbr> document.
</p>
</body>
</html>
8.4 显示“缩略语列表”
首先用一个定义列表元素来显示这些<abbr>标签的文本的title属性
这是希望得到的定义列表,接下来将用JavaScript和DOM来实现
<dl>
<dt>W3C</dt>
<dd>World Wide Web</dd>
<dt>DOM</dt>
<dd>Document Object Model</dd>
<dt>API</dt>
<dd>Application Programming Interface</dd>
<dt>HTML</dt>
<dd>HyperText Markup Language</dd>
<dt>XML</dt>
<dd>eXtensible Markup Language</dd>
</dl>
JavaScript实现代码:
function displayAbbr() {
var abbr = document.getElementsByTagName('abbr');
var defs = new Array();
for (var i = 0; i < abbr.length; i++) {
var current_abbr = abbr[i];
var definiton = current_abbr.getAttribute('title');
var key = current_abbr.firstChild.nodeValue;
defs[key] = definiton;
}
var dlist = document.createElement('dl');
for (key in defs) {
var dtitle = document.createElement('dt');
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
var ddesc = document.createElement('dd');
var ddesc_text = document.createTextNode(defs[key]);
ddesc.appendChild(ddesc_text);
dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
}
var header = document.createElement('h2');
var header_txt = document.createTextNode('Abbreviations');
header.appendChild(header_txt);
document.body.appendChild(header);
document.body.appendChild(dlist);
}
window.onload = displayAbbr();
8.5 显示“文献来源链接表”
JavaScript实现代码:
function displaycitation() {
var quotes = document.getElementsByTagName('blockquote');
for (var i = 0; i < quotes.length; i++) {
if (!quotes[i].getAttribute('cite')) {
continue;
} //判断blockquote里是否有cite属性 没有就跳过本次循环
var url = quotes[i].getAttribute('cite'); //把cite属性存入变量url中
var quoteElements = quotes[i].getElementsByTagName('*');
var elem = quoteElements[quoteElements.length - 1];
var link = document.createElement('a');
//找出当前quote里最后一个元素节点
var link_txt = document.createTextNode('source');
link.appendChild(link_txt);
link.setAttribute('href', url); //创建链接
var superscript = document.createElement('sup');
superscript.appendChild(link);
elem.appendChild(superscript); //插入链接
}
}
window.onload = displaycitation();
8.6 显示“快捷键清单”
首先,我们需要在之前的HTML文档上加入一段标记代码
<ul id="navigation">
<li><a href="index.html" accesskey="1">Home</a></li>
<li><a href="search.html" accesskey="4">Search</a></li>
<li><a href="contact.html" accesskey="0">Contact</a></li>
</ul>
有了这串代码后就可以开始编写快捷键清单,和之前的思路基本相同:
function displayAccesskeys() {
var links = document.getElementsByTagName('a');
var akeys = new Array()
for (var i = 0; i < links.length; i++) {
var current_link = links[i];
if (!current_link.getAttribute('accesskey')) continue;
var key = current_link.getAttribute('accesskey');
var txt = current_link.firstChild.nodeValue;
akeys[key] = txt;
}
var ulist = document.createElement('ul');
for (key in akeys) {
var text = akeys[key];
var str = key + ':' + text;
var item = document.createElement('li')
var item_txt = document.createTextNode(str);
item.appendChild(item_txt);
ulist.appendChild(item);
}
var header = document.createElement('h3');
var header_txt = document.createTextNode('Accesskeys');
header.appendChild(header_txt);
document.getElementsByTagName('body')[0].appendChild(header);
document.getElementsByTagName('body')[0].appendChild(ulist);
}
window.onload = displayAccesskeys;
9.CSS-DOM
9.1 三位一体的网页
我们在浏览器看到的网页其实是由一下三层信息构成的一个共同体
- 结构层 (由HTML或XHTML之类的标记语言负责创建)
- 表示层 (由CSS负责完成,CSS描述页面内容如何呈现)
- 行为层 (负责内容应该如何响应事件这一问题,由JavaScript和DOM负责)
分离
选择最适用的工具去解决问题是最基本的原则,在网页设计工作中,应当
- 使用(X)HTML去搭建文档的结构
- 使用CSS去设置文档的呈现效果
- 使用DOM脚本去实现文档的行为
9.2 Style属性
文档中的每个元素都是一个对象,而每个对象都有各种各样的属性。每个元素节点都有一个属性style。style属性包含着元素的样式,查询对象将返回一个对象。样式都存放在这个style对象的属性里
element.style.property
但是如果要获取的属性是类似于这种font-family 中间带有“-”符号的,会被JavaScript解释为减号而报错。当需要引用一个中间带减号的CSS属性时,DOM要求用驼峰命名法,比如把font-family编程fontFamily。
内嵌样式
通过style属性获取样式有很大的局限性,style属性只能返回内嵌样式。也就是只有把CSS style属性插入到标记里,才可以用DOM style属性去查询那些信息
设置样式
style对象的各个属性都是可读写的,我们不仅可以通过某个元素的style属性去获取样式,还可以通过他去更新样式。
可以用赋值操作来更新样式:
element.style.property = value;
style对象的属性值永远是一个字符串。举个例子,正确的写法应该是
element.style.color = ‘black’;
如果省掉了black的引号,JavaScript就会把black解释为一个变量,而这个变量没有声明过,导致代码无法工作。
9.3 何时该用DOM脚本设置样式
在绝大多数场合还是应该用CSS去声明样式。但在使用CSS不方便的场合还是可以利用DOM对文档的样式做一些小的增强。
根据元素在节点树里的位置来设置样式
使用CSS为特定位置的某个元素应用样式并不是一件容易的事情,虽然CSS3中加入了一些位置选择器,但是有很多浏览器并不支持CSS3,而使用DOM却可以很轻松做到。
比如我们要取<h1>后面一个元素节点,就可以用DOM这样完成:
function styleHeaderSiblings() {
var headers = document.getElementsByTagName('h1');
for (var i = 0; i < headers.length; i++) {
elem = getNextElement(this.nextsibling);
}
}
function getNextElement(node) {
if (node.nextsibling.nodeType == 1) {
return node;
}
if (node.nextsibling) {
return getNextElement(node.nextsibling);
}
return null;
}
根据某种条件反复设置某种样式
JavaScript特别擅长处理重复性任务。用一个while或者for循环就可以轻松地遍历一个很长的列表。
首先写一个html文档来建立一个表格。
<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="Washinton">WA</abbr></td>
</tr>
<tr>
<td>June 12th</td>
<td>Sacramento, <abbr title="Califonia">CA</abbr></td>
</tr>
</tbody>
</table>
</body>
加入CSS样式表,让数据可读性更好。
如果此时我们希望表格里的行交替改变背景色,用CSS3 的nth-child方法很好做到,但是如果无法使用CSS3的方法,那么就只能手动为每一行添加class属性,这很麻烦。因此可以用JavaScript编写一个函数来添加效果
function colorAlt() {
var tables = document.getElementsByTagName('table'); //取出所有表格
for (var i = 0; i < tables.length; i++) {
var odd = false;
var 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;
}
}
}
}
window.onload = colorAlt();
响应事件
当前许多浏览器对CSS的伪类选择器支持并不完整,但对DOM有良好的支持。因此在事件发生时用DOM改变HTML元素的样式也是一个不错的选择。
下面这个highlightRows函数就可以替代hover的效果。
function highlightRows() {
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';
}
}
}
9.4 className属性
凡是元素节点都有className属性,我们可以通过className属性和赋值操作符设置一个元素的class属性,而不是直接改变它的样式
element.className = value;
但该技巧有一个不足,就是这会直接替换掉元素原来的class属性。当我们需要需要追加一个class属性的时候采用
element.className = element.className + ‘ value’;
注意此时的value前面有一个空格。