客户端javascript的存在使得静态的HTML文档变成了交互式的Web应用。脚本化Web页面内容是javascript的核心目标。
选取文档元素
通过ID选取: var section1 = document.getElementById("ID名称");
通过名字选取元素: var radiobutons = document.getElementsByName("name");
通过标签选取元素: var spans = document.getElementsByTagName("标签名称");
通过CSS类选取元素:
HTML元素的class属性值是一个以空格隔开的列表,可以为空或包含多个标示符。
class属性通常与CSS样式表一起使用,对于组内的所有元素应用相同的样式。
HTML定义了getElementsByClassName()方法,它基于class属性值中的标示符来选取成组的文档元素,返回一个NodeList对象,该方法只需要一个参数,但是该参数字符串可以由多个空格隔开的标示符组成,标示符顺序是无关紧要的
<script type="text/javascript">
//查找其class属性值中包含“warning”的所有元素
var warnings = document.getElementsByClassName("warning");
//查找以“log”命名并且有“error”和“fatal”类的元素
var log = document.getElementById("id");
var fatal = log.getElementsByClassName("fatal error");
</script>
文档结构和遍历
作为节点树的文档
Document对象、它的Element对象和文档中表示文本的Text对象都是Node对象。
Node定义了几个重要的属性:
parenNode:该节点的父节点;
childNode:只读的类数组对象,它是该节点的子节点的实时表示;
firstChild、lastChild:该节点的子节点中的第一个和最后一个。
nextSibling、previoursSibling:该节点的兄弟节点中的前一个和下一个。具有相同父节点的两个节点为兄弟节点。
nodeType:该节点的类型。9代表Document节点,1代表Element节点,3代表Text节点,8代表Comment节点,11代表DocumentFragment节点。
nodevalue:Text节点或者Comment节点的内容。
nodeNmae:元素的表签名,以大写形式表示;
作为元素树的文档
当将主要的兴趣点集中在文档中的元素上而非他们之间的文本上时,可以是用另外一个API,将文档看做是Element对象树,忽略部分文档:Text和Comment节点。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
/**
* 返回元素e的第n层祖先元素,如果不存在则返回null
*/
function parent(e,n){
if(n === undefined) n = 1;
while(n--&& e ) e = e.parentNode;
if(!e ||e.nodeType !== 1) return null;
return e;
}
/**
* 返回元素e的第n个兄弟元素
*/
function sibling(e,n){
while(e && n!==0){//如果e未定义,即刻返回它
if(n > 0 ){//查找后续的兄弟元素
if(e.nextElementSibling) e = e.nextElementSibling;
else{
for(e = e.nextSibling;e && e.nodeType !==1;e = e.nextSibling)
/*空循环*/;
}
n--;
}
else{//查找前面 的兄弟元素
if(e.previousElementSibling) e = e.previousElementSibling;
else{
for(e = e.previousSibling;e && e.nodeType !==1;e = e.previousSibling)
/*空循环*/;
}
n++;
}
}
return e;
}
</script>
</head>
<body>
</body>
</html>
属性
HTML元素由一个标签和一组称为属性(attribut)的名/值对组成。
HTML属性作为Element的属性:
表示HTML文档元素的HTMLElement对象定义了读/写属性,它们映射了元素的HTML属性。
获取和设置非标准HTML属性:
Element类型还定义了getAttribute()和setAttribute()方法来查询和设置非标准的HTML属性。
var image =document.images[0];
var width =parseInt(image.getAttribute("WIDTH"));
image.setAttribute("class","nail");
属性值都被看做字符串。
Element类型还定义了两个相关的方法 hasAttribute()和removeAttribute()方法,检测命名属性是否存在和完全删除属性。
数据集属性:
使用getAttribute和setAttribute来读和写非标准的属性值,文档将不再是合法有效的HTML.
HTML5提供了解决方案,在HTML5中,任意以“date-”为前缀的小写的属性名字都是合法的,这个数据集属性将不会对其元素的变现产生影响。
HTML5还在Element对象上定义了dateset的属性。该属性指代一个对象,他的各个属性对应用与去掉前缀date-的值,因此dateset.x 应该保存date-x属性的值。
创建、插入和删除节点
创建节点
创建新的Element节点可以使用Document对象的createElement()方法,给方法传递元素的标签名。
Text节点用类似的方法创建: var newnode = document.createTextNode("text node content");
另一种创建新文档节点的方法是复制已经存在的节点,每个节点都有一个cloneNode()方法来返回给节点的一个副本。给方法传递参数true也能够递归地复制所有的后代节点。
在除了IE的其他浏览器中,Document对象还定义了一个类似的方法叫ipportNode()。如果给它传递另一个文档的一个节点,它将返回一个适合本文档插入节点的副本。
插入节点
一旦有一个新节点,就可以使用Node的方法appendChild()或者insertBefore()将它插入到文档中。
appendChild()是在需要插入的Element节点上调用的,它插入指定的节点使其成为那个节点的最后一个子节点。
insertBofore()接受两个参数,第一个参数是待插入的节点,第二个参数是已存在的节点,新节点将插入到该节点的前面。该方法应该是在新节点的父节点上调用,方法的第二个参数必须是该父节点的子节点。
//将child节点插入到parent中,使其成为第n个子节点
function insertAt(parent,child,n){
if( n <0 || n > parent.childNodes.length) throw new Erroe("invalid index");
else if( n == parent.childNodes.length) parent.appendChild(child);
else parent.insertBofore(child,parent.childNodes[n]);
}
如果调用appendChild()或insertBefore()将已存在文档中的一个节点再次插入,那个节点将自动从当前的位置删除并在新的位置重新加载。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
//根据指定表格每行第n个单元格的值,对第一个<tbody>中的行进行排序
//如果存在comparator函数则使用它,否则按字母表顺序比较
function sortrows(table,n,comparator){
var tbody = table.tBodies[0];//第一个<tbody>可能是隐式创建的
var rows = tbody.getElementsByTagName("tr");//tbody中的所有行
rows = Array.prototype.slice.call(rows,0);//真实数据中的快照
//基于第n个<td>元素的值对行排序
rows.sort(
function(row1,row2){
var cell1 = row1.getElementsByTagName("td")[n];//获得第N个单元格
var cell2 = row2.getElementsByTagName("td")[n];//两行都是
var val1= cell1.textContent || cell1.innerText;//获得文本内容
var val2 = cell2.textContent || cell2.innerText;//两单元格都是
if (comparetor) return compaaator(val1,val2);
if (val1 < val2) return -1;
else if (val1 > val2) return 1;
else return 0;
};)
//在tbody中按它们的顺序把行添加到最后
//这将自动把它们从当前位置移走,故没有必要先删除它们
//如果<tbody>还包含了除了<tr>的任何其他元素,这些节点将会悬浮到顶部位置
for(var i = 0 ; i< rows.length;i++) tbody.appendChild(rows[i]);
}
</script>
<body>
删除和替换节点
removeChild()方法是从文档树中删除一个节点,该方法不是在待删除的节点上调用,而是在其父节点上调用。在父节点上面调用该方法,将需要删除的子节点作为参数传递给方法,在文档中删除n节点: n.parentNode.removeChild(n);
replaceChild()方法删除一个节点并用一个新的节点取而代之。在父节点上调用该方法,第一个参数为新节点,第二个参数为需要替代的节点。
//用一个新的<b>元素替换n节点,并使n称为该元素的子节点
function embolden(n){
//假如参数为字符串而不是节点,将其当做元素的id
if(typeof n == "string") n = document.getElementById(n);
var parent = n.parentNode;//获得n的父节点
var b = document.createElement("b");//创建一个<b>元素
parent.replaceChild(b,n);
b.appendChild(n);//使n成为b的子节点
}
使用DocumentFragment
DocumentFragment是一种特殊的Node,作为其他节点的一个临时的容器。
创建一个DocumentFragment节点: var frag = docment.createDocumentFragment();
DocumentFragment是独立的,不是任何其他文档的一部分,它的parentNode总是null。
//倒序排列节点n的子节点
function reverse(n){
//创建一个DocumentFragment作为临时的容器
var f = document.createDocumentFragment();
//从后至前循环子节点,将每个子节点移动到文档片段中,n的最后一个节点变成F的第一个子节点,注意,给f添加一个节点,给节点自动地从N中删除
while(n.lastChid) f.appendChild(n.lastChild);
//最后,将f插入到N中
n.appendChild(f);
}
生成目录表
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<script type="text/css">
#TOC{ border:solid black 1px; margin:10px;padding : 10px;}
.TOCEntry{ font-family:sans-serif;}
.TOCEntry a{ text-decoration:none;}
.TOClevel1{font-size:16pt;font-weigth:bold;}
.TOClevel2{font-size:12pt;margin-left: .5in;}
.TOCSectNum:after{content:" : ";}
//这段代码的最后一行表示每个段编号之后都有一个冒号和空格符,要想隐藏段编号:.TOCSectNum{display:none;}
}
</script>
<script type="text/javascript">
/**
* 这个模块注册一个可在页面加载完成后总动运行的匿名函数,当执行这个函数时会去文档中查找id为"TOC"的元素,如果这个元素不存在,则创建它。
*
* 生成的TOC目录应当具有自己的CSS样式,整个目录区域的样式className设置为“TOCEntry”
*为不同层级的目录标题定义不同的样式。
*/
onLoad(function(){//匿名函数定义了一个局部作用域
//查找TOC容器元素
//如果不存在,在文档开头处创建一个
var toc = documment.getElementById("TOC");
if(!toc){
toc = documment.createElement("div");
toc.id = "TOC";
document.body.insertBefore(toc,documment.body.firstChild);
}
//查找所有的标题元素
var headings;
if(document.querySelectorAll)//是否有这个方法
headings = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
else //否则用别的方法
headings = findHeadings(document.body,[]);
//递归遍历document的body,查找标题元素
function findHeadings(root,sects){
for(var c= root.firstChild;c != null;c = c.c.nextSibling){
if(c.nodeType !== 1 ) continue;
if(c.tagName.length ==2 && c.tagName.charAt(0) == "H")
sects.push(c);
else findHeadings(c,sects);
}
return sects;
}
//初始化一个数组来保持跟踪章节号
var sectionNumbers = [0,0,0,0,0,0];
//现在,循环已经找到的标题元素
for(var h = 0;h<headings.length;h++){
var heading = headings[h];
//跳过在TOC容器中的标题元素
if(heading.parentNode == toc) continue;
//判断标题的级别
var level = parsrInt(heading.tagName.charAt(1));
if(isNaN(level) || level < 1 ||level > 6) continue;
//对于该标题级别增加sectionNumbers对应的数字
//重置所有标题比它级别低的数字为零
sectionNumbers[level-1]++;
for(var i = level;i < 6 ;i++) sectionNumbers[i] = 0;
//现在,将所有的标题级别的章节号组合产生一个章节号,如2.3.1
var sectionNmubers = sectionNmbers.slice(0,level).join(".");
//为标题级别增加章节号
//把数字放在<span>中,使得其可以用样式修饰
var span = document.createElement("span");
span.className = "TOCSectNum";
span.innerHtml = sectionNmuber;
heading.insertBefore(span,heading.firstChild);
//用命名的锚点将标题包起来,以便为它增加链接
var anchor = document.createElemet("a");
anchor.name = "TOC"+sectionNumber;
heading.parentNode.insertBefore(anchor,heading);
ahchor.appendChild(heading);
//现在为该节点创建一个链接
var link = document.creatElement("a");
link.href = "#TOC"+sectionNumber;//链接的目标地址
link.innerHTML = heading.innerHTML;//链接文本与实际标题一致
//将链接房子一个div中,div用基于级别名字的样式修饰
var entry = document.createElement("div");
entry.className = "TOCEntry TOCLevel"+ level;
entry.appendChild(link);
//将div添加到TOC容器汇总
toc.apendChild(entry);
}
});
</script>
<body>
</body>
</html>
文档和元素的几何形状和滚动
判定一个元素的精确的几何形状,位置是非常有必要的。
文档坐标和视口坐标
元素的位置是以像素来度量的,向右代表X坐标的增加,向下代表Y坐标的增加。
有两个不同的点作为坐标系的原点:元素的X和Y坐标可以相对于文档的左上角或者相对于在其中显示文档的视口的左上角(能见的界面,如拖动滚动条)。
在顶级窗口和标签页中,“视口”只是实际显示文档内容的浏览器的一部分:不包括浏览器的”外壳“。针对框架页中显示的文档,视口是定义了框架页的<iframe>元素。
如果文档比视口小,或者未出现滚动,则文档的左上角就是视口的左上角,文档和视口坐标系统是同一个,要在两种坐标之间转换,必须加上或者减去滚动的偏移量。
文档坐标比视口坐标更加基础,并且在用户滚动时它们不会发生变化。但是在客户端中使用视口坐标是非常常见的,当使用CSS指定元素的位置时运用了文档坐标,但最简单的查询元素位置的方法返回视口坐标中的位置,为鼠标事件注册事件处理程序函数时,报告的鼠标指针的坐标是视口坐标系中的。
为了在坐标系之间互相转换,需要判定浏览器窗口的滚动条的位置。Window对象的pageXOfset和pageYOffset属性在所有的浏览器中提供这些值,除了IE8及更早的版本以外。
//以一个对象的x和y属性的方式返回滚动条的偏移量
function getScrollOffsets(w){
w = w || window //使用指定的窗口,如果不带参数则使用当前窗口
//出了IE8 及以前版本
if(w.pageXOffset != null) return {x: w.pageXOffset,y:w.pageYOffset};
//对标准模式下的IE
var d = w.document;
if( document.compatMode == "CSS1Compat")
return {x: d.documentElement.scrollLeft, y : d.documentElement.scrollTop};
//对于怪异模式
return {x:d.body.scrollLeft,y:d.body.scrollTop} ;
}
查询窗口的视口尺寸
<script type="text/javascript">
//作为一个对象的w和h属性返回视口的尺寸
function getViewportSize(w){
w = w || wondow;
if(w.innerWidth !=null) return {w:w.innerWidth,h:w.innerHeight};
var d= w.document;
if(d.compatMode == "CSS1Compat") return {w: d.documentElement.clientWidth,h:d.documentElement.clientHeight};
return {w:d.body.clientWidth,h:d.body.clientHeight};
}
</script>
查询元素的几何尺寸
判定一个元素的尺寸和位置最简单的方法是调用它的getBoundingClentRect()方法,返回一个有left,right,top,bottom属性的对象,left和top属性表示元素的左上角的X和Y坐标,right和bottom属性表示的右下角的X和Y坐标。(视口坐标)
为了转换为甚至用户滚动浏览器窗口以后仍然有效的文档坐标,需要加上滚动的偏移量。
var box = e.getBoundingClentRect();
var offsets = getScrollOffsets();
var x= box.left + offsets.x;
var y = box.top + offsets.y;
有的浏览器getBoundingClentRect()方法返回的对象还包含width和height属性,计算元素的width和height:
var box = e.getBoundingClentRect();
var W= box.width || (box.right - box.left);
var H = box.height ||(box.bottom - box.top);
如果想查询内联元素每个独立的矩形,调用getClientRects()方法;
这两个方法返回的矩形对象并不是实时的,在用户滚动或该表浏览器大小的时候不会更新它们。
断定元素在某点
使用Document对象的elementFromPoin()来判断元素的位置,接受参数为 x 和y坐标。(使用视口坐标而非文档坐标)
滚动
<script type="text/javascript">
function gundong(){
var documentHeight = document.documentElement.offsetHright;
var viewportHeight = window.innerHeight;
window.scroll(0,documentHeight-viewportHeight);
}
更多信息
HTML表单
网上摘抄内容
动态文档内容
如果一个<script>有一个defer属性,它就不能包含任何对document.write()的调用。defer属性告诉web浏览器把脚本的调用延迟到文档完全载入才是安全的,一旦发生这种情况,对document.write()在文档正在解析的时候将内容插入其中来说就太迟了。
可以使用write()方法,结合Document对象的open()方法,close()方法,在其他窗口或帧中创建一个全新的文档
function hello(){
var w=window.open();
var d=w.document;
d.open();
d.write("<h1>Hello world!</h1>");
d.close();
}
(经验证,若w中原来已经有内容,则d.open,d.write("good"),d.close()会将原来的内容抹去,写上新内容)
要创建新文档,先要调用Document对象的open()方法,现调用write写,最后用close方法以说明创建过程结束了。若忘记关闭文档,浏览器就不能制止它所显示的文档装载动画。而且浏览器可以将写入的HTML缓存起来,这样在调用方法close()显式结束文档之前,缓存输出不会显示出来。
与close不同,open方法的调用是可选的。若调用一个已经关闭了的文档的write方法,js会隐式打开一个新HTML文档,就像已经调用过open方法一样。这也就解释了在同一文档中从事件句柄调用document.write()时js会打开一个新文档。
方法write可以具有多个参数,参数会依次写入文档,就像它们已经连接在一起一样。
而且Document对象还支持writeln()方法,它会在输出的参数之后附加一个换行符。
2 Document属性
遗留属性:
bgColor:文档背景,对应于已经废弃的标记<body>的bgColor属性
cookie:允许js程序读写HTTP cookie
domain:使处于同一Internet域中的相互信任的web服务器在它们的网页间交互时能协同地放松同源策略安全限制
lastModified:一个字符串,包含文档的修改日期
location:等价于属性URL,已经废弃
referrer:文档的URL,包含把浏览器带到当前文档的链接(eg,因权限验证而发生的跳转可以在登录之后跳转到最初访问的页面)
title:位于文档标记<title>和</title>之间的文本
URL:一个字符串,声明了装载文档的URL。除非发生服务器重定向,否则其等于location.href
referrer存放了一个文档的URL,用户从该文档链接到当前文档,可以使用这一属性来防止站点的深度链接。如果要让所有访问者都通过自己的主页而到达,可以把如下代码放到除主页以外的所有页面顶部来重定向它们。
<script>
if(document.referrer == "" || document.referrer.indexOf("mysite.com") == -1)
window.location = "http://home.mysite.com";
</script>
3 遗留DOM:文档对象集合
文档对象集合属性是遗留DOM的核心,这些属性使得可以访问文档的某些具体元素:
anchors[]:Anchor对象的一个数组,该对象代表文档中的锚(文档中的一个命名的位置,是带name属性的<a>标记)。一个Anchor对象的name属性保存了name属性的值。
applets:Applet对象的数组,该对象表文档中的Java applet
forms[]:Form对象的数组,该对象代表文档中的<form>元素,每个Form对象有一名为elements[]的集合属性
images[]:Image对象的数组,该对象代表<img>元素,其src属性可读写
links[]:Link对象的数组,该对象是代表文档中的超文本链接的Link对象。超文本链接在HTML中用<a>标记创建,偶尔会用客户端图像地图的<area>标记来创建。Link对象的href属性与<a>标记的href属性相对应:保存链接的URL,该对象通过 protocol、hostname和pathname等属性使一个URL的不同部分变得可用。
包含在这些遗留DOM集合中的对象是可以脚本化的,但它们中没有谁会允许改变文档的结构。
命名Document对象:
因基于位置的索引不稳定,小的文档改变文档元素的顺序可能会破坏基于它们的顺序的代码,更健壮的解决方案是为重要元素分配名字,用名字引用这些元素。在遗留DOM中,可使用表单、表单元素、图像、applet和链接的name属性来做到这一点。
设置一个<form>、<img>或<applet>(除了<a>标记之外)的name属性,使得相应的Form、Image或Applet对象(但没有Link或Anchor对象)可以作为文档对象自身的一个有名字的属性被访问。
Document对象上的事件句柄:
HTML不区分大小写,它的属性可用大写,小写或大小写混合的形式,而js的所有事件句柄属性都必须用小写
添加句柄可以如下:
document.myform.onsubmit = validateform;
函数名后没有括号,因为此处不想调用函数,只是把一个引用赋予它。
遗留DOM示例:
http://blog.sina.com.cn/s/blog_912389e5010115o5.html
4 W3C DOM概览
节点:
文档树中不同类型的节点都用节点的特定子接口来表示,每个Node对象都有一个nodeType属性。
DOM树根部的Node是一个Document对象,它的documentElement属性引用了一个Element对象,它代表了文档的根元素,比如HTML文档从<html>标记表示的元素开始。HTML文档中因通常对<body>元素比对<html>元素更感兴趣,可以使用document.body来引用这个元素。
属性:
用Element接口的getAttribute()方法,setAttribute()方法和removeAttribute()方法可以查询、设置并删除一个元素的属性
另一种使用属性的方式是调用getAttributeNode()方法,它将返回一个表示属性和它的值的Attr对象,它是一种节点类型,但不出现在childNodes[]数组中,不是文档树的一部分。DOM标准允许通过Node接口的attributes[]数组访问Attr节点,但微软定义了不兼容的attributes[]数组,要可移植地使用这种功能不可能
DOM HTML API
HTMLDocument是HTML专有的Document接口的子接口,HTMLElement是HTML专有的Element接口的子接口。HTMLElement接口定义了id,style,title,lang,dir和className属性,大部分标记只有这些属性,其他的:
<ul> : HTMLUListElement
<body> : HTMLBodyElement
此外还有其它的一些接口,它们一般还定义了专用的属性和方法,比如HTMLInputElement接口定义了focus()和blur()方法和form属性,HTMLFormElement接口定义了submit()和reset()方法及length属性。
DOM一致性:
Document对象的implementation属性引用一个DOMImplementation对象,它定义了名为hasFeature()的方法,这个方法(如果存在)可以查询一个实现是否支持特定的DOM特性或模块。
eg 判断一个web浏览器是否支持HTML文档的基本1级DOM接口
if(document.implementation && document.implementation.hasFeature &&
document.implementation.hasFeature("html","1.0")){
//则浏览器支持1级核心和HTML接口
}
IE中的DOM一致性
IE6声称支持1级DOM标准的Core接口和HTML接口,但它不支持Node接口定义的节点类型常量,这个问题可以通过用整数直接量代替相应的符号常量来获得兼容性。
但因为使用常量是一个良好的编程习惯,若可使用如下的可移植的方法:
if(!window.Node){
var Node={
ELEMENT_NODE:1
ATTRIBUTE_NODE:2
TEXT_NODE:3
COMMENT_NODE:8
DOCUMENT_NODE:9
DOCUMENT_FRAGMENT_NODE:11
};
}
5 遍历文档
遍历文档节点:
function countTags(n){
var numtags=0;
if(n.nodeType == 1) numtags++;
var children = n.childNodes;
for(var i=0;i<children.length;i++){
numtags += countTags(children[i]);
}
return numtags;
}
除了childNodes属性,Node接口还定义了其他属性:
firstChild和lastChild属性分别引用节点的第一个和最后一个子节点
nextSibling和previousSibling属性引用节点相邻的兄弟节点
获取一个DOM节点下的所有文本
function getText(n){
var strings=[];
getStrings(n,strings);
return strings.join("");
function getStrings(n,strings){
if(n.nodeType == 3) strings.push(n.data);
else if (n.nodeType == 1){
for(var m=n.firstChild;m!=null;m=m.nextSibling)
getStrings(m,strings);
}
}
}
6 在文档中查找元素
用类名或标记名选择HTML元素:
http://blog.sina.com.cn/s/blog_912389e50101160r.html
7 修改文档
DOM核心API的真正威力在于它能用js,下面是一个sortkids()函数
http://blog.sina.com.cn/s/blog_912389e50101161f.html
由于NodeList对象是childNodes属性的值,并且由getElementsByTagName_r()返回,它是“活的”,即对文档的任何改变会立刻反映在NodeList中,而上例用的是数组kids。具体例子如下,结果会马上显示出来
eg 将文档内容转换为大写
function upcase(n){
if(n.nodeType == 3){
n.data = n.data.toUpperCase();
}
else{
var kids = n.childNodes;
for(var i=0;i<kids.length;i++) upcase(kids[i]);
}
}
<ul id="list">
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
</ul>
<button onclick="upcase(document.getElementByIdx_x_x_x_x_x_x_x('list'))">upcase</button>
还可以使用appendData(),insertData(),deleteData()和replaceData()在Text节点中添加,插入,删除和替换文本
DOM API允许文档树中的节点在树中自由移动,下面的函数用表示HTML标记<b>的新元素替换指定的节点,并改变原始节点的父节点使它成为新的<b>节点的子节点。
function enbolden(n){
if(typeof n=="string") n=document.getElementByIdx_x_x_x(n);
var b = document_createElement_x_x_x_x_x_x("b"); //此处有错误
var parent = n.parentNode;
parent.replaceChild(b,n);
b.a(n); //此处有错误
}
修改属性:
一种方法是调用 element.setAttribute()
另一种是表示HTML属性的DOM元素定义了对应于每个标准属性的js属性,element.attributename = value
使用DocumentFragment
DocumentFragment是一种特殊类型的节点,它自身不出现在文档中,只作为连续节点集合的临时容器,并允许将这些节点作为一个对象来操作,当把一个DocumentFragment播放插入文档时,插入的不是DocumentFragment自身,而是它的所有子节点。可以用document_createDocumentFragment()来创建一个DocumentFragment
示例如下:
使用一个DocumentFragment
function reverse(n){ //反转一个节点的子节点
var f = document_createDocumentFragment();
while(n.lastChild) f.a(n.lastChild);
n.a(f);
}
8 给文档添加内容
document_createElement_x_x_x_x_x_x(),document_createTextNode()方法分别创建Elment和Text节点,方法Node.a(),insertBefore(),replaceChild()等
示例如下:
定义了一个log()函数用于记录消息和对象,例子包含一个log.debug()函数,在调试js时,可以作为alert的替代,传递给log()的消息要么是字符串,要么是js对象。当记录字符串的时候,原样显示,记录对象时,显示为属性名和属性值的一个表,log.makeTable()示意使用函数创建HTML表
function log(category,message,object){
//若这个category显式地屏蔽了,则什么都不做
if(log.options[category+"Disabled"]) return;
//找到容器
var id= category+"_log";
var c = document.getElementByIdx_x_x_x_x_x(id);
//若没有容器但这个category需要记录,则创建容器
if(!c && log.options[category+"Enabled"]){
c = document_createElement_x_x_x_x_x("div");
c.id = id;
c.className = "log";
document.body.a(c);
}
//若还是没有容器则忽略消息
if(!c) return;
//若需加时间戳,则加上时间戳
if(log.options.timestamp) message = new Date()+": "+(message?message:"");
//创建一个div元素装载记录词条
var entry = document_createElement_x_x_x_x_x("div");
entry.className = category+"_message";
if(message){
entry.a(document_createTextNode(message));
}
if(object && typeof object == "object"){
entry.a(log.makeTable(object,0));
}
//最终,将词条加入记录容器
c.a(entry);
}
//创建一个表显示特定对象的属性
log.makeTable = function(object,level){
//若已经达到最大递归深度,则返回一个文本节点
if(level > log.options.maxRecursion) return document_createTextNode(object.toString());
//创建即将返回的表
var table = document_createElement_x_x_x_x_x("table");
table.border = 1;
//为表添加name|value|header 表头
var header = document_createElement_x_x_x_x_x("tr");
var headerName = document_createElement_x_x_x_x_x("th");
var headerType = document_createElement_x_x_x_x_x("th");
var headerValue = document_createElement_x_x_x_x_x("th");
headerName.a(document_createTextNode("Name"));
headerType.a(document_createTextNode("Type"));
headerValue.a(document_createTextNode("Value"));
header.a(headerName);
header.a(headerType);
header.a(headerValue);
table.a(header);
//获取对象的属性名并按字母顺序排序
var names = [];
for(var name in object) names.push(name);
names.sort();
//现在遍历这些属性
for(var i=0;i<names.length;i++){
var name,value,type;
name = names[i];
try{
value=object[name];
type=typeof value;
}
catch(e){
value = "<unknown value>";
type = "unknown";
};
//若这个属性会被过滤器过滤掉,则跳过这个属性
if(log.options.filter && !log.options.filter(name,value)) continue;
//决不显示函数源代码:因为这会占用太多的空间
if(type=="function") value="{}";
//创建表单行显示属性名,类型和值
var row = document_createElement_x_x_x_x_x("tr");
row.vAlign = "top";
var rowName = document_createElement_x_x_x_x_x("td");
var rowType = document_createElement_x_x_x_x_x("td");
var rowValue = document_createElement_x_x_x_x_x("td");
rowName.a(document_createTextNode(name));
rowType.a(document_createTextNode(type));
if(type == "object")
rowValue.a(log.makeTable(value,level+1));
else
rowValue.a(document_createTextNode(value));
//将单元加入行中,并将行加入表格
row.a(rowName);
row.a(rowType);
row.a(rowValue);
table.a(row);
}
//最终返回表
return table;
}
//创建空选项对象
log.options = {};
//函数的工具版本
log.debug = function(message,object){log("debug",message,object);};
log.warn = function(message,object){log("warn",message,object);};
</script>
<body>
<script>
function makeLina(h,w){
log.debug("entering Lina");
var lina = {h:h,w:w,weapon:{pat:'nike',elbow:'strong'},character:{gender:'female',feature:"cute"}};
log.debug("new Lina",lina);
log.debug("exiting make Lina");
return lina;
}
</script>
<button onclick="makeLina(172,69)">make Lina</button>
<div id="debug_log" class="log"></div>
</body>
创建节点的方便方法:
可以发现上例中创建文档内容的API很长,下例定义了创建Element的工具函数
它定义了一个名为make()的函数,make函数使用指定的标记名创建一个Element,设置它的属性,并为它添加孩子。属性指定为一个对象的属性,孩子在一个数组中传递。这个数组的元素可能是字符串,被转换为Text节点,或是其他Element对象,通常用嵌套的make()调用来创建。
make有一种灵活的调用语法,允许两种快捷方式
首先,若没有指定属性,属性参数可以省略,并传递孩子参数来代替它
第二,若只有一个孩子,可直接传递而不是放入只有一个元素的数组中
但还可以做的更好,另一个叫做maker的函数,向它传递一个标记名,它会返回一个嵌套的函数,该函数使用指定硬编码的标记名来调用make()
function make(tagname,attributes,children){
//若只有两个参数,attributes参数是一个数组或者字符串,则它实际应该是children参数
if(arguments.length ==2 && (attributes instanceof Array || typeof attributes == "string")){
children = attributes;
attributes = null;
}
//创建元素
var e = document_createElement_x_x_x_x_x(tagname);
//设置属性
if(attributes){
for(var name in attributes) e.setAttribute(name,attributes[name]);
}
//添加孩子
if(children != null){
if (children instanceof Array){
for(var i=0;i<children.length;i++){
var child = children[i];
if(typeof child == "string") child = document_createTextNode(child);
e.a(child); //假定除字符串外的都是节点
}
}
else if (typeof children == "string") e.a(document_createTextNode(children));
else e.a(child);
}
return e;
}
function maker(tag){
return function(attrs,kids){
if (arguments.length == 1) return make(tag,attrs);
else return make(tag,attrs,kids);
}
}
innerHTML属性:
HTML元素的innerHTML属性是该元素的孩子的一个HTML文本字符串(带标签)
innerHTML由微软引入,还有outerHTML,innerText,outerText,火狐不支持它们
9 例子:动态创建的目录
定义了一个maketoc()方法,它定义为onload句柄,文档加载后,它遍历文档,查找文档中h1-6标记,并查找带ID为'toc'的元素,在该元素中创建一个目录,在这个过程中,maketoc()为每个部分的标题添加部分编号,在每个部分的标题添加部分编号,在每个部分之前插入一个指定的锚,然后在每个部分的开始处插入一个返回到TOC的链接
function maketoc(){
//alert(document.getElementByIdx_x_x_x_x_x("toc").getElementsByTagName_r("h1")[0].childNodes[0].data);
//寻找窗口,若找不到,则返回
var container = document.getElementByIdx_x_x_x_x_x('toc');
if(!container) return;
//遍历文档,添加标题h1-6
var sections = [];
findSections(document,sections);
//在窗口元素前插入锚以便我们能链回到它
var anchor = document_createElement_x_x_x_x_x('a'); //创建<a>节点
anchor.name = "TOCtop";
anchor.id = "TOCtop";
container.parentNode.insertBefore(anchor,container);
//初始化存储section号的数组
var sectionNumbers = [0,0,0,0,0,0];
//现在循环遍历找到的section头部元素
for(var s=0;s<sections.length;s++){
var section = sections[s];
//判断是哪一级的h
var level = parseInt(section.tagName.charAt(1));
if (isNaN(level)||level < 1 || level>6) continue;
//为这个级别的h增加section数,并将所有低级别的h数设为0
sectionNumbers[level-1]++;
for(var i=level;i<6;i++) sectionNumbers[i] =0;
//现在用类似2.3.1的形式将h级别结合起来
var sectionNumber = "";
for(i=0;i<level;i++){
sectionNumber += sectionNumbers[i];
if(i<level-1) sectionNumber +='.';
}
//添加section号和一个空格到section头标题,将这个数置于<span>中以便CSS
var frag = document_createDocumentFragment();
var span = document_createElement_x_x_x_x_x("span");
span.className = "TOCSectNum";
span.a(document_createTextNode(sectionNumber));
frag.a(span);
frag.a(document_createTextNode(" "));
section.insertBefore(frag,section.firstChild);
//创建一个锚以标志这个部分的开始
var anchor = document_createElement_x_x_x_x_x("a");
anchor.name = 'TOC'+sectionNumber;
anchor.id = 'TOC'+sectionNumber; //在IE中,产生的锚需要ID
//产生一个指向TOC的链接
var link = document_createElement_x_x_x_x_x("a");
link.href = "#TOCtop";
link.className = "TOCBackLink";
link.a(document_createTextNode(maketoc.backlinkText));
anchor.a(link);
//将锚和链接紧挨着section头插入其前面
section.parentNode.insertBefore(anchor,section);
//现在创建一个指向这个部分的链接
var link = document_createElement_x_x_x_x_x("a");
link.href = "#TOC"+sectionNumber;
link.innerHTML = section.innerHTML;
//将链接放入div中
var entry = document_createElement_x_x_x_x_x("div");
entry.className = "TOCEntry TOCLevel"+ level;
entry.a(link);
//当div加入TOC容器
container.a(entry);
}
//这个方法递归地遍历根为节点n的树,寻找h标记,并将它们添加到sections数组
function findSections(n,sects){
for(var m = n.firstChild;m!=null;m=m.nextSibling){
//跳过任何不是元素的节点
if(m.nodeType != 1) continue;
//跳过容器元素,因它可能有自己的heading
if( m == container) continue;
//由于优化,跳过<p>标签,因为h标签不应出现在段落中,同时还有lists,<pre>标签
if(m.tagName == "P") continue;
//如果我们没有跳过孩子节点,检查它是否是heading,如果是,将其加入数组,否则,递归,由于DOM是基于接口而不是类,因此不能只是用 m instanceof HTMLHeadingElement
if(m.tagName.length==2 && m.tagName.charAt(0) == "H") sects.push(m);
else findSections(m,sects);
}
}
}
//下面是链接回TOC的默认文本
maketoc.backlinkText = "Contents";
//注册maketoc()在文档加载完成时自动运行
if(window.addEventListener) window.addEventListener("load",maketoc,false);
else if (window.attachEvent) window.attachEvent("onload",maketoc);
HTML文档如下:
<div id="toc"><h1>table of contents</h1></div>
10 查询选定的的文本
eg 查询当前选定的文本
function getSelectedText(){
if(window.getSelection){
//这个技巧是最可能标准化的,getSelection() 返回一个Selection对象,which we don't document
return window.getSelection().toString();
}
else if(document.getSelection){
//这是更早更简单的返回一个字符串的技巧
return document.getSelection();
}
else if(document.selection){
//这是IE中的技巧,我们没有document IE的selection属性或者TextRange对象
return document.selection.createRange().text;
}
}
经验证在IE8中也只支持document.selection
可以将上例改造用于搜索
<a href = "javascript:
var q;
if(window.getSelection) q = window.getSelction().toString();
else if(document.getSelection) q = document.getSelection();
else if(document.selection) q = document.selection.createRange().text;
void window.open('http://en.wikipedia.org/wiki/'+q);
">
Look up Selected Text In wikipedia</a>
上例有一个小小的不兼容,如果选定的文本在<input>或<textarea>中,Window和Document对象的getSelection方法不会返回它,它们只能从文档自身的内容中返回选择的文本
火狐中定义了selectionStart和selectionEnd属性可用来查询和设置选择的文本
11 IE 4 DOM
遍历文档:
IE 4不支持childNodes[]数组,但提供了非常相似的children[]数组,IE 4没有Text节点类型,不把文本串看作节点,纯文本的标记只有空的children[]数组,通过innerText属性可访问标记的文本内容
搜索文档元素:
IE4不支持getElementById()和getElementsByTagName_r()方法,而Document对象和所有文档元素都有数组属性all[],它表示文档中所有元素或元素中包含的所有元素。注意,all[]不只表示文档或元素的子节点,它表示所有子孙,无论它们嵌套得多深。
IE4等价于getElementById()的方法是用字符串做all[]的下标,而不用数字,它会返回id属性或name属性具有指定值的元素,还允许把数组下标表示为属性名(没错,和你此时想的一样,但下标不可以是数字)
all数组有一点异常之处,即用tags()方法可以以标记名获取一个元素数组
var lists = document.all.tags("UL");
修改文档:
IE4用相应的HTMLElement对象的属性表示HTML标记的属性,这些对象定义了setAttribute(),getAttribute(),removeAttribute()方法,IE4不支持创建,插入新节点,重定节点父节点,在树中移动节点。
但IE 4中所有HTMLElement对象都定义了innerHTML属性。把这个属性设置为一个HTML文本串可以使用自己需要的内容替换一个元素的内容,但并非所有浏览器都支持这个功能强大的属性
IE4中其他的相关属性和方法有:
outerHTML属性:将用指定的HTML文本串替换一个元素的内容和元素本身
innerText属性和outerText属性与innerHTML和outerHTML属性相似,只是后者将字符串作为纯文本处理,而不作为HTML解析。
insertAdjacentHTML方法和insertAdjacentText方法不管元素内容,而在它附近(在它之前或之后,在内部或外部)插入新的HTML或纯文本内容,这些属性和函数不像innerHTML那么常用,而且火狐没有实现它们