客户端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对象,该方法只需要一个参数,但是该参数字符串可以由多个空格隔开的标示符组成,标示符顺序是无关紧要的
文档结构和遍历
作为节点树的文档
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节点。
Insert title here
属性
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()将已存在文档中的一个节点再次插入,那个节点将自动从当前的位置删除并在新的位置重新加载。
Insert title here
删除和替换节点
removeChild()方法是从文档树中删除一个节点,该方法不是在待删除的节点上调用,而是在其父节点上调用。在父节点上面调用该方法,将需要删除的子节点作为参数传递给方法,在文档中删除n节点: n.parentNode.removeChild(n); replaceChild()方法删除一个节点并用一个新的节点取而代之。在父节点上调用该方法,第一个参数为新节点,第二个参数为需要替代的节点。
//用一个新的元素替换n节点,并使n称为该元素的子节点
function embolden(n){
//假如参数为字符串而不是节点,将其当做元素的id
if(typeof n == "string") n = document.getElementById(n);
var parent = n.parentNode;//获得n的父节点
var b = document.createElement("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);
}
生成目录表
Insert title here
文档和元素的几何形状和滚动
判定一个元素的精确的几何形状,位置是非常有必要的。
文档坐标和视口坐标
元素的位置是以像素来度量的,向右代表X坐标的增加,向下代表Y坐标的增加。
有两个不同的点作为坐标系的原点:元素的X和Y坐标可以相对于文档的左上角或者相对于在其中显示文档的视口的左上角(能见的界面,如拖动滚动条)。
在顶级窗口和标签页中,“视口”只是实际显示文档内容的浏览器的一部分:不包括浏览器的”外壳“。针对框架页中显示的文档,视口是定义了框架页的
如果文档比视口小,或者未出现滚动,则文档的左上角就是视口的左上角,文档和视口坐标系统是同一个,要在两种坐标之间转换,必须加上或者减去滚动的偏移量。
文档坐标比视口坐标更加基础,并且在用户滚动时它们不会发生变化。但是在客户端中使用视口坐标是非常常见的,当使用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} ;
}
查询窗口的视口尺寸
查询元素的几何尺寸
判定一个元素的尺寸和位置最简单的方法是调用它的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坐标。(使用视口坐标而非文档坐标)
滚动
3 遗留DOM:文档对象集合
文档对象集合属性是遗留DOM的核心,这些属性使得可以访问文档的某些具体元素:
applets:Applet对象的数组,该对象表文档中的Java applet
forms[]:Form对象的数组,该对象代表文档中的
images[]:Image对象的数组,该对象代表 元素,其src属性可读写
包含在这些遗留DOM集合中的对象是可以脚本化的,但它们中没有谁会允许改变文档的结构。
命名Document对象:
因基于位置的索引不稳定,小的文档改变文档元素的顺序可能会破坏基于它们的顺序的代码,更健壮的解决方案是为重要元素分配名字,用名字引用这些元素。在遗留DOM中,可使用表单、表单元素、图像、applet和链接的name属性来做到这一点。
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文档中因通常对元素比对元素更感兴趣,可以使用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属性,大部分标记只有这些属性,其他的:
: 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
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
}
}
upcase
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
var name,value,type;
name = names[i];
try{
value=object[name];
type=typeof value;
}
catch(e){
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);};
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;
}
make Lina