JavaScript DOM编程艺术 读书笔记
3 DOM
DOM分别指document,object和model。
DOM中包含的节点主要分为三种:元素节点、文本节点和属性节点。DOM的原子是元素节点,标签的名字就是元素的名字,元素可以包含其他的元素。没有被包含在其他元素里的唯一元素是<html>元素,它是树节点的根元素;在XHTML文档里,文本节点总是被包含在元素节点的内部。但并非所有的元素节点都包含有文本节点;属性节点用来对元素做出更具体地描述。
有3种DOM方法可获取元素节点,分别是通过元素ID、通过标签名字和通过类名字来获取。
1) getElementById(id)
2) getElementByTagName(tag)
3) getElementByClassName HTML5 DOM,某些DOM实现里可能还没有
获取属性和方法:object.getAttribute(attribute),object.setAttribute(attribute, value)
function getElementsByClassName(node, classname){ if(node.getElementsByClassName){ return node.getElementsByClassName(classname); } else { var result = new array(); var elements = getElementsByTagName("*"); for(var i=0; i<elements.length; i++){ if(elements[i].clsssName.indexOf(classname) != -1){ result[result.length] = elements[i]; } } return false; } }
4 案例研究
childNodes属性获取任何一个元素的所有子元素,它是一个包含这个元素全部子元素的数组:element.childNodes。通过element.childNodes.length获取element元素包含的元素的个数。由childNodes属性返回的数组包含所有类型的节点,而不仅仅是元素节点。事实上,文档里几乎每一样东西都是一个节点,甚至连空格和换行符都会被解释为节点,而它们也全都包含在childNodes属性所返回的数组当中。
nodeType属性可以查看到节点类型,但其返回值是一个数字而不是“element”或“attribute”那样的英文字符串。nodeType属性共有12种可取值,但其中仅有3种具有实用价值:
1)元素节点的nodeType属性值是1.
2)属性节点的nodeType属性值是2.
3)文本节点的nodeType属性值是3.
如果想改变一个文本节点的值,可以使用DOM提供的node.nodeValue属性,它用来得到(和设置)一个节点的值。访问数组的第一个元素,需要使用node.firstChild属性,与之对应的node.lastChild表示数组的最后一个元素。
function addLoadEvent(func) { var oldonload = window.onload; if(typeof window.onload != ‘function‘){ window.onload = func; } else { window.onload = function(){ oldonload(); func(); } } }
7 动态创建标记
7.1 DOM文档操作
var para = document.createElement("p"); //创建一个p元素节点
把刚创建的元素添加到ID为testdiv的DOM节点树中,成为它的子节点:
var testdiv = document.getElementById("testdiv"); testdiv.appendChild(param);//把这个p元素节点追加到文档中的一个元素节点
创建一个文本节点:
var txt = document.createTextNode("Hello World"); //创建一个文本节点
para.appendChild(txt); //把这个文本节点追加到刚创建的那个p元素节点上
insertBefore()方法,把一个新元素插入到一个现有元素的前面:parentElement.insertBefore(newElement, targetElement)。我们不必搞清楚元素到底是哪个,因为targetElement元素的parentNode属性值就是它。
var gallery = document.getElementById("imagegallery"); gallery.parentNode.insertBefore(placeholder, gallery);
注意:DOM并没有提供insertAfter函数。
利用已有的DOM方法和属性编写的insertAfter函数:
function insertAfter(newElement, targetElement) { var parent = targetElement.parentNode; if(parent.lastChild == targetElement) { parent.appendChild(newElement); } else { parent.insertBefore(newElement, targetElement.nextSibling); } }
7.2 Ajax技术
html代码:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Ajax</title> </head> <body> <div id="new"></div> <script src="addLoadEvent.js"></script> <script src="getHTTPObject.js"></script> <script src="getNewContent.js"></script> </body> </html>
getHTTPObject.js代码:
function getHTTPObject() { if(typeof XMLHttpRequest == "undefined") { XMLHttpRequest = function() { try{ return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch(e){ } try{ return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch(e){ } try{ return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e){ } return false; } } return new XMLHttpRequest(); }
getNewContent.js代码:
function getNewContent() { var request = getHTTPObject(); if(request) { request.open("GET", "example.txt", true); request.onreadystatechange = function() { //相应处理函数 if(request.readyState == 4) { alert("Response Received"); var para = document.createElement("p"); var txt = document.createTextNode(request.responseText); para.appendChild(txt); document.getElementById("new").appendChild(para); } }; request.send(null); //发送请求 } else { alert("Sorry, your browser doesn\‘t support XMLHttpRequest"); } alert("Function Done"); } addLoadEvent(getNewContent);
8 充实文档内容
8.1 为文档创建缩略语列表的函数
temp.html:
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Ajax</title> <style> body { font-family: "Helvetica","Arial",sans-serif; font-size:10pt; } attr{ text-decoration:none; border:0; font-style:normal; } </style> </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.w3c.org/DOM/"> <p> A platform- and language-neutral interface that will allow programs and scripts to dynamically access and update the content, structure and style of documents. </p> </blockquote> <p> It is an <abbr title="Application Programing Interface">API</abbr> that can be used to navigate <abbr title="HyperText Markup Language">HTML</abbr> and <abbr title="Extensible Markup Language">XML</abbr> documents. </p> <script src="addLoadEvent.js"></script> <script src="displayAbbreviations.js"></script> </body> </html>
displayAbbreviations.js:
function displayAbbreviations() { if(!document.getElementsByTagName || !document.createElement || !document.createTextNode) return false; //取得所有的缩略词 var abbreviations = document.getElementsByTagName("abbr"); if(abbreviations.length < 1) return false; //遍历缩略词,把缩略语及其解释保存到defs数组里 var defs = new Array(); for(var i=0; i<abbreviations.length; i++) { var current_abbr = abbreviations[i]; //如果当前元素没有子节点,就立刻开始下一次循环 if(current_abbr.childNodes.length < 1) continue; var definition = current_abbr.getAttribute("title"); var key = current_abbr.lastChild.nodeValue; defs[key] = definition; } //创建定义列表 var dlist = document.createElement("dl"); for(key in defs) { var definition = defs[key]; //创建定义标题 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(definition); ddesc.appendChild(ddesc_text); //添加到定义列表 dlist.appendChild(dtitle); dlist.appendChild(ddesc); } if(dlist.childNodes.length < 1) return false; //创建标题 var header = document.createElement("h2"); var header_text = document.createTextNode("Abbreviations"); header.appendChild(header_text); //把标题添加到页面主体 document.body.appendChild(header); //把定义列表添加到页面主体 document.body.appendChild(dlist); } addLoadEvent(displayAbbreviations);
运行结果:
8.2 显示文献来源链接表
function displayCitation() { if(!document.getElementsByTagName || !document.createElement || !document.createTextNode) return false; //取得所有的引用 var quotes = document.getElementsByTagName("blockquote"); if(quotes.length < 1) return false; //遍历引用 for(var i=0; i<quotes.length; i++) { if(!quotes[i].getAttribute("cite")) continue; //保存cite属性 var url = quotes[i].getAttribute("cite"); //取得引用中的所有元素节点 var quoteChildren = quotes[i].getElementsByTagName("*"); if(quoteChildren.length <1 ) continue; //取得引用中的最后一个元素节点 var elem = quoteChildren[quoteChildren.length-1]; //创建标记 var link = document.createElement("a"); var link_text = document.createTextNode("source"); link.appendChild(link_text); link.setAttribute("href", url); var superscript = document.createElement("sup"); superscript.appendChild(link); //把标记添加到引用中最后一个元素节点 elem.appendChild(superscript); } } addLoadEvent(displayCitation);
运行结果:
9 CSS-DOM
文档的每个元素节点都有一个属性style。style属性包含着元素的样式,查询这个属性将返回一个对象而不是一个简单的字符串。样式都存放在这个style对象的属性里:element.style.property。不仅文档里的每个元素都是一个对象,每个元素都有一个style属性,它们也是一个对象。
当需要引用一个中间带减号的CSS属性时,DOM要求你用驼峰命名法。例如CSS属性font-family变为DOM属性fontFamily。且不管CSS样式属性的名字里有多少个连字符,DOM一律采用驼峰命名法表示它们。
style对象只包含在HTML代码里用style属性声明的样式。在外部样式表和文档的<head>部分里声明的样式不会进入style对象。然而,style对象的各个属性都是可读写的,不仅可以通过某个元素的style属性去获取样式,还可以通过它去更新样式。虽然不应该利用DOM去创建重要的内容,但可以利用DOM对文档的样式做一些小增强。
还可以用element.className = value来设置样式。
10 用javascript实现动画效果
JavaScript函数setTimeout能够让某个函数在经过一段预定的时间之后才开始执行。该函数带有两个参数:第一个参数通常是一个字符串,其内容是将要执行的那个函数的名字;第二个参数是一个数值,它以毫秒为单位设定了需要经过多长时间后才开始执行第一个参数所给出的参数:setTimeout("function", interval); 在绝大多数时候,把这个函数调用赋值给一个变量将是个好主意:variable = setTimeout("function", interval);
如果想取消某个正在排队等待执行的函数,就必须事先像上面这样把setTimeout函数的返回值赋值给一个变量。可以使用clearTimeout函数来取消”等待执行“队列里的某个函数。这个函数需要一个参数---保存着某个setTimeout函数调用返回值的变量。
JavaScript允许我们为元素创建属性:element.property = value; 这很像是在创建一个变量,但区别是这个变量专属于某个特定的元素。
HTML代码:
<p id="message">Hello World!</p>
JS代码:
function moveElement(elementID, final_x, final_y, interval) { if(!document.getElementsByTagName || !document.createElement || !document.createTextNode) return false; if(!document.getElementById(elementID)) return false; var elem = document.getElementById(elementID); if(elem.movement){ clearTimeout(elem.movement); } if(!elem.style.left) elem.style.left = "0px"; if(!elem.style.top) elem.style.top = "0px"; var xpos = parseInt(elem.style.left); var ypos = parseInt(elem.style.top); var dist = 0; if(xpos == final_x && ypos == final_y) { return true; } if(xpos < final_x){ dist = Math.ceil((final_x - xpos)/10); xpos = xpos + dist; } if(xpos > final_x) { dist = Math.ceil((xpos - final_x)/10); xpos = xpos - dist; } if(ypos < final_y){ dist = Math.ceil((final_y - ypos)/10); ypos = ypos + dist; } if(ypos > final_y){ dist = Math.ceil((ypos - final_y)/10); ypos = ypos - dist; } elem.style.left = xpos + "px"; elem.style.top = ypos + "px"; var repeat = "moveElement(‘"+elementID+"‘,"+final_x+","+final_y+","+interval+")"; elem.movement = setTimeout(repeat, interval); }
12 一些整理
12.1 form对象
文档中的每个表单元素都是一个form对象,每个form对象都有一个element.length属性。这个属性返回表番中的包含的表单元素的个数:form.elements.length。这个返回值与childNodes.length不一样,后者返回的是元素中包含的所有节点的个数。而form对象的elements.length属性只关心那些表单的元素,如input、textarea等等。
相应的,表单中的所有字段都保存在form对象的elements属性中。也就是说,form.elements是一个包含所有表单元素的数组。同样,这个属性与childNodes属性也不一样,后者也是一个数组。childNodes数组返回的是所有节点,而elements数组则只返回input、select、textarea以及其他表单字段。
elements数组中的每个表单元素都有自己的一组属性。比如:value属性中保存的就是表单元素的当前值:element.value,等价于element.getAttribute("value");