本期文章引见了一名客串的专栏作家,同时也是我的朋侪和同事 Dethe Elza。Dethe 在运用 XML 举行 Web 运用顺序开辟方面经验丰富,在此,我要谢谢他对我在引见运用 DOM 和 ECMAScript 举行 XML 编程这一方面的协助。请亲昵关注本专栏,以相识 Dethe 的更多专栏文章。
—— David Mertz
DOM 是处置惩罚 XML 和 HTML 的范例 API 之一。由于它占用内存大、速度慢,而且冗杂,所以经常遭到人们的诘问诘责。尽管如此,关于很多运用顺序来讲,它仍然是最好挑选,而且比 XML 的另一个重要 API —— SAX 无疑要简朴很多。DOM 正逐渐出如今一些东西中,比方 Web 浏览器、SVG 浏览器、OpenOffice,等等。
DOM 很好,由于它是一种范例,而且被普遍地完成,同时也内置到其他范例中。作为范例,它对数据的处置惩罚与编程言语无关(这多是长处,也多是瑕玷,但最少使我们处置惩罚数据的体式格局变得一致)。DOM 如今不仅内置于 Web 浏览器,而且也成为很多基于 XML 的范例的一部份。既然它已成为您的东西的一部份,而且也许您偶然还会运用它,我想如今应当充足运用它给我们带来的功用了。
在运用 DOM 一段时候后,您会看到形成了一些形式 —— 您想要反复做的事变。快捷体式格局可以协助您处置惩罚冗杂的 DOM,并建立自诠释的、文雅的代码。这里收集了一些我经常运用的技能和窍门,另有一些 javaScript 示例。
insertAfter 和 PRependChild
第一个窍门就是“没有窍门”。DOM 有两种要领将孩子节点增加到容器节点(经常是一个 Element,也多是一个 Document 或 Document Fragment):appendChild(node) 和 insertBefore(node, referenceNode)。看起来好像缺少了什么。假如我想在一个参考节点背面插进去或许由前新增(prepend)一个子节点(使新节点位于列表中的第一名),我该怎么做呢?很多年以来,我的处理要领是编写以下函数:
清单 1. 插进去和由前新增的毛病要领
function insertAfter(parent, node, referenceNode) { if(referenceNode.nextSibling) { parent.insertBefore(node, referenceNode.nextSibling); } else { parent.appendChild(node); } } function prependChild(parent, node) { if (parent.firstChild) { parent.insertBefore(node, parent.firstChild); } else { parent.appendChild(node); } }
实际上,像清单 1 一样,insertBefore() 函数已被定义为,在参考节点为空时返回到 appendChild()。因而,您可以不运用上面的要领,而运用 清单 2 中的要领,或许跳过它们仅运用内置函数:
清单 2. 插进去和由前新增的准确要领
function insertAfter(parent, node, referenceNode) { parent.insertBefore(node, referenceNode.nextSibling); } function prependChild(parent, node) { parent.insertBefore(node, parent.firstChild); }
假如您方才打仗 DOM 编程,有必要指出的是,虽然您可以使多个指针指向一个节点,但该节点只能存在于 DOM 树中的一个位置。因而,假如您想将它插进去到树中,没必要先将它从树中移除,由于它会自动被移除。当从新将节点排序时,这类机制很轻易,仅需将节点插进去到新位置即可。
依据这类机制,若想交流两个相邻节点(称为 node1 和 node2)的位置,可以运用以下计划之一:
node1.parentNode.insertBefore(node2, node1);
或
node1.parentNode.insertBefore(node1.nextSibling, node1);
还可以运用 DOM 做什么?
Web 页面中大批运用了 DOM。若接见 bookmarklets 站点(参阅 参考资料),您会发明很多有创意的简短剧本,它们可以从新编排页面,提取链接,隐蔽图片或 Flash 广告,等等。
然则,由于 Internet Explorer 没有定义 Node 接口常量(可以用于辨认节点范例),所以您必需确保在脱漏接口常量时,起首为 Web 在 DOM 剧本中定义接口常量。
清单 3. 确保节点被定义
if (!window['Node']) { window.Node = new Object(); Node.ELEMENT_NODE = 1; Node.ATTRIBUTE_NODE = 2; Node.TEXT_NODE = 3; Node.CDATA_SECTION_NODE = 4; Node.ENTITY_REFERENCE_NODE = 5; Node.ENTITY_NODE = 6; Node.PROCESSING_INSTRUCTION_NODE = 7; Node.COMMENT_NODE = 8; Node.DOCUMENT_NODE = 9; Node.DOCUMENT_TYPE_NODE = 10; Node.DOCUMENT_FRAGMENT_NODE = 11; Node.NOTATION_NODE = 12; }
清单 4 展现怎样提取包括在节点中的一切文本节点:
清单 4. 内部文本
function innerText(node) { // is this a text or CDATA node? if (node.nodeType == 3 || node.nodeType == 4) { return node.data; } var i; var returnValue = []; for (i = 0; i < node.childNodes.length; i++) { returnValue.push(innerText(node.childNodes[i])); } return returnValue.join(''); }
快捷体式格局
人们经常埋怨 DOM 太甚冗杂,而且简朴的功用也须要编写大批代码。比方,假如您想建立一个包括文本并相应点击按钮的 <div> 元素,代码可以相似于:
清单 5. 建立 <div> 的“冗长之路”
function handle_button() { var parent = document.getElementById('myContainer'); var div = document.createElement('div'); div.className = 'myDivCSSClass'; div.id = 'myDivId'; div.style.position = 'absolute'; div.style.left = '300px'; div.style.top = '200px'; var text = "This is the first text of the rest of this code"; var textNode = document.createTextNode(text); div.appendChild(textNode); parent.appendChild(div); }
若频仍根据这类体式格局建立节点,键入一切这些代码会使您很快筋疲力竭。必需有更好的处理计划 —— 确切有如许的处理计划!下面这个实用东西可以协助您建立元素、设置元素属性和作风,并增加文簿子节点。除了 name 参数,其他参数都是可选的。
清单 6. 函数 elem() 快捷体式格局
function elem(name, attrs, style, text) { var e = document.createElement(name); if (attrs) { for (key in attrs) { if (key == 'class') { e.className = attrs[key]; } else if (key == 'id') { e.id = attrs[key]; } else { e.setAttribute(key, attrs[key]); } } } if (style) { for (key in style) { e.style[key] = style[key]; } } if (text) { e.appendChild(document.createTextNode(text)); } return e; }
运用该快捷体式格局,您可以以越发简约的要领建立 清单 5 中的 <div> 元素。注重,attrs 和 style 参数是运用 Javascript 文本对象而给出的。
清单 7. 建立 <div> 的轻便要领
function handle_button() { var parent = document.getElementById('myContainer'); parent.appendChild(elem('div', {class: 'myDivCSSClass', id: 'myDivId'} {position: 'absolute', left: '300px', top: '200px'}, 'This is the first text of the rest of this code')); }
在您想要疾速建立大批庞杂的 DHTML 对象时,这类实用东西可以节约您大批的时候。形式在这里就是指,假如您有一种须要频仍建立的特定的 DOM 构造,则运用实用东西来建立它们。这不只减少了您编写的代码量,而且也减少了反复的剪切、粘贴代码(毛病的罪魁祸首),而且在浏览代码时思绪越发清楚。
接下来是什么?
DOM 一般很难告诉您,根据文档的递次,下一个节点是什么。下面有一些实用东西,可以协助您在节点间前后挪动:
清单 8. nextNode 和 prevNode
// return next node in document order function nextNode(node) { if (!node) return null; if (node.firstChild){ return node.firstChild; } else { return nextWide(node); } } // helper function for nextNode() function nextWide(node) { if (!node) return null; if (node.nextSibling) { return node.nextSibling; } else { return nextWide(node.parentNode); } } // return previous node in document order function prevNode(node) { if (!node) return null; if (node.previousSibling) { return previousDeep(node.previousSibling); } return node.parentNode; } // helper function for prevNode() function previousDeep(node) { if (!node) return null; while (node.childNodes.length) { node = node.lastChild; } return node; }
轻松运用 DOM
有时候,您可以想要遍历 DOM,在每一个节点挪用函数或从每一个节点返回一个值。实际上,由于这些主意异常具有普遍性,所以 DOM Level 2 已包括了一个称为 DOM Traversal and Range 的扩大(为迭代 DOM 一切节点定义了对象和 API),它用来为 DOM 中的一切节点运用函数和在 DOM 中挑选一个局限。由于这些函数没有在 Internet Explorer 中定义(最少现在是如许),所以您可以运用 nextNode() 来做一些
相似的事变。
在这里,我们的主意是建立一些简朴、一般的东西,然后以差别的体式格局组装它们来到达预期的结果。假如您很熟悉函数式编程,这看起来会很亲热。Beyond JS 库(参阅 参考资料)将此理念发扬光大。
清单 9. 函数式 DOM 实用东西
// return an Array of all nodes, starting at startNode and // continuing through the rest of the DOM tree function listNodes(startNode) { var list = new Array(); var node = startNode; while(node) { list.push(node); node = nextNode(node); } return list; } // The same as listNodes(), but works backwards from startNode. // Note that this is not the same as running listNodes() and // reversing the list. function listNodesReversed(startNode) { var list = new Array(); var node = startNode; while(node) { list.push(node); node = prevNode(node); } return list; } // apply func to each node in nodeList, return new list of results function map(list, func) { var result_list = new Array(); for (var i = 0; i < list.length; i++) { result_list.push(func(list[i])); } return result_list; } // apply test to each node, return a new list of nodes for which // test(node) returns true function filter(list, test) { var result_list = new Array(); for (var i = 0; i < list.length; i++) { if (test(list[i])) result_list.push(list[i]); } return result_list; }
清单 9 包括了 4 个基础东西。listNodes() 和 listNodesReversed() 函数可以扩大到一个可选的长度,这与 Array 的 slice() 要领结果相似,我把这个作为留给您的演习。另一个须要注重的是,map() 和 filter() 函数是完整通用的,用于处置惩罚任何 列表(不只是节点列表)。如今,我向您展现它们的几种组合体式格局。
清单 10. 运用函数式实用东西
// A list of all the element names in document order function isElement(node) { return node.nodeType == Node.ELEMENT_NODE; } function nodeName(node) { return node.nodeName; } var elementNames = map(filter(listNodes(document),isElement), nodeName); // All the text from the document (ignores CDATA) function isText(node) { return node.nodeType == Node.TEXT_NODE; } function nodeValue(node) { return node.nodeValue; } var allText = map(filter(listNodes(document), isText), nodeValue);
您可以运用这些实用东西来提取 ID、修正款式、找到某种节点并移除,等等。一旦 DOM Traversal and Range API 被普遍完成,您无需起首构建列表,就可以用它们修正 DOM 树。它们不只功用壮大,而且事情体式格局也与我在上面所强调的体式格局相似。
DOM 的风险地带
注重,中心 DOM API 并不能使您将 XML 数据剖析到 DOM,或许将 DOM 序列化为 XML。这些功用都定义在 DOM Level 3 的扩大部份“Load and Save”,但它们还没有被完整完成,因而如今不要斟酌这些。每一个平台(浏览器或其他专业 DOM 运用顺序)有本身在 DOM 和 XML间转换的要领,但跨平台转换不在本文议论局限以内。
DOM 并非非常平安的东西 —— 特别是运用 DOM API 建立不能作为 XML 序列化的树时。相对不要在同一个顺序中夹杂运用 DOM1 非称号空间 API 和 DOM2 称号空间感知的 API(比方,createElement 和 createElementNS)。假如您运用称号空间,请只管在根元素位置声明一切称号空间,而且不要掩盖称号空间前缀,不然状况会异常杂沓。一般来讲,只需根据通例,就不会触发使您堕入贫苦的临界状况。
假如您一向运用 Internet Explorer 的 innerText 和 innerHTML 举行剖析,那末您可以尝尝运用 elem() 函数。经由过程构建相似的一些实用东西,您会获得更多方便,而且继续了跨平台代码的优越性。将这两种要领夹杂运用是异常蹩脚的。
某些 Unicode 字符并没有包括在 XML 中。DOM 的完成使您可以增加它们,但效果是没法序列化。这些字符包括大多数的控制字符和Unicode 代办对(surrogate pair)中的单个字符。只要您试图在文档中包括二进制数据时才会碰到这类状况,但这是另一种转向(gotcha)状况。
结束语
我已引见了 DOM 能做的很多事变,然则 DOM(和 JavaScript)可以做的事变远不止这些。细致研讨、琢磨这些例子,看看是怎样运用它们来处理可以须要客户端剧本、模板或专用 API 的题目。
DOM 有本身的局限性和瑕玷,但同时也具有浩瀚长处:它内置于很多运用顺序中;不管运用 Java 手艺、Python 或 JavaScript,它都以雷同体式格局事情;它异常便于运用 SAX;运用上述的模板,它运用起来既简约又壮大。越来越多的运用顺序最先支撑 DOM,这包括基于 Mozilla的运用顺序、OpenOffice 和 Blast Radius 的 XMetaL。越来越多的范例须要 DOM,并对它加以扩大(比方,SVG),因而 DOM 每时每刻就在您的身旁。运用这类被普遍布置的东西,相对是您的明智之举。
以上就是XML 题目: 逾越DOM(轻松运用 DOM 的技能和窍门)的内容,更多相关内容请关注ki4网(www.ki4.cn)!