JS 10.2 DOM 操作技术

10.2 DOM操作技术

因为浏览器的坑,有的时候DOM的操作并不简明;

动态脚本

创建script节点;

function loadScript(url){
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.src = url;
	document.body.appendChild(script);
}

在IE中<script>是一种特殊元素,不允许DOM访问子节点;

// 其他浏览器可以
var script = document.createElement("script");
script.type = "text/javascript";
script.appendChild(document.createTextNode("function sayHi(){alert('hi');}"));
document.body.appendChild(script);

// 兼容IE
var script = document.createElement("script");
script.type = "text/javascript";
script.text = "function sayHi(){alert('hi');}";
document.body.appendChild(script);

// 兼容早期Safari和IE
var script = document.createElement("script");
script.type = "text/javascript";
var code = "function sayHi(){alert('hi');}";
try {
	script.appendChild(document.createTextNode("code"));
} catch (ex){
	script.text = "code";
}
document.body.appendChild(script);

Safari不能支持text但是可以使用文本节点,所以try;

以这种方式加载的代码会在全局作用域中执行,而且当脚本执行后将立即可用;

实际上,这样执行代码与在全局作用域中把相同的字符串传递给eval()是一样的;

动态样式

同样的,css也可以;

function loadStyles(url){
	var link = document.createElement("link");
	link.rel = "stylesheet";
	link.type = "text/css";
	link.href = url;
	var head = document.getElementsByTagName("head")[0];
	head.appendChild(link);
}

加载外部样式文件的过程是异步的,也就是加载样式与执行JavaScript代码的过程没有固定的次序;

一般来说,知不知道样式已经加载完成并不重要;

第13章将讨论利用事件来检测这个过程是否完成;

同样的,IE来了;

var style = document.createElement("style");
style.type = "text/css";
try{
	// 普通浏览器
	style.appendChild(document.createTextNode("body{background-color:red}"));
} catch (ex){
	// IE浏览器
	style.styleSheet.cssText = "body{background-color:red}";
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);

这种方式会实时地向页面中添加样式,因此能够马上看到变化;

如果专门针对IE编写代码,务必小心使用styleSheet.cssText属性;

在重用同一个<style>元素并再次设置这个属性时,有可能会导致浏览器崩溃;

同样,将cssText属性设置为空字符串也可能导致浏览器崩溃;

IE666;

操作表格

显然,使用之前介绍的方式创建一个表格要写死人;

HTML DOM不想你死,于是就有了;

<table>元素添加的属性和方法如下;

1.caption:保存着对<caption>元素(如果有)的指针;

2.tBodies:是一个<tbody>元素的HTMLCollection;

3.tFoot:保存着对<tfoot>元素(如果有)的指针;

4.tHead:保存着对<thead>元素(如果有)的指针;

5.rows:是一个表格中所有行的HTMLCollection;

6.createTHead():创建<thead>元素,将其放到表格中,返回引用;

7.createTFoot():创建<tfoot>元素,将其放到表格中,返回引用;

8.createCaption():创建<caption>元素,将其放到表格中,返回引用;

9.deleteTHead():删除<thead>元素;

10.deleteTFoot():删除<tfoot>元素;  11.deleteCaption():删除<caption>元素;  12.deleteRow(pos):删除指定位置的行;  13.insertRow(pos):向rows集合中的指定位置插入一行;

<tbody>元素添加的属性和方法如下;

1.rows:保存着<tbody>元素中行的HTMLCollection;

2.deleteRow(pos):删除指定位置的行;

3.insertRow(pos):向rows集合中的指定位置插入一行,返回对新插入行的引用;

<tr>元素添加的属性和方法如下;

1.cells:保存着<tr>元素中单元格的HTMLCollection;

2.deleteCell(pos):删除指定位置的单元格;

3.insertCell(pos):向cells集合中的指定位置插入一个单元格,返回对新插入单元格的引用;

例子:

//创建table
var table = document.createElement("table");
table.border = 1;
table.width = "100%";
//创建tbody
var tbody = document.createElement("tbody");
table.appendChild(tbody);
//创建第一行
tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1"));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));
//创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
//将表格添加到文档主体中
document.body.appendChild(table);

这里文本是cell 列,行;

使用NodeList

回顾一下动态集合三兄弟:NodeList,NamedNodeMap,HTMLCollection;

动态的,实时的,忘记的话可能会写出死循环;

var divs = document.getElementsByTagName("div"),
i,
div;
for (i=0; i < divs.length; i++){
div = document.createElement("div");
document.body.appendChild(div);
}

解决方案当然是暂存一下初始length;

var divs = document.getElementsByTagName("div"),
i,
len,
div;
for (i=0, len=divs.length; i < len; i++){
	div = document.createElement("div");
	document.body.appendChild(div);
}

10.3 小结

DOM操作往往是JavaScript程序中开销最大的部分,而因访问NodeList导致的问题为最多;

NodeList对象都是”动态的”,这就意味着每次访问NodeList对象,都会运行一次查询;

有鉴于此,最好的办法就是尽量减少DOM操作;