搜索
首页后端开发XML/RSS教程XML 问题: 超越DOM(轻松使用 DOM 的技巧和诀窍)

文档对象模型(Document Object Model,DOM)是用于操纵 xml 和 HTML 数据的最常用工具之一,然而它的潜力却很少被充分挖掘出来。通过利用 DOM 的优势,并使它更加易用,您将获得一款应用于 XML 应用程序(包括动态 Web 应用程序)的强大工具。

    本期文章介绍了一位客串的专栏作家,同时也是我的朋友和同事 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(&#39;&#39;);
}

 

    快捷方式

    人们常常抱怨 DOM 太过冗长,并且简单的功能也需要编写大量代码。例如,如果您想创建一个包含文本并响应点击按钮的 dc6dce4a544fdca2df29d5ac0ea9906b 元素,代码可能类似于:

清单 5. 创建 dc6dce4a544fdca2df29d5ac0ea9906b 的“漫长之路”

function handle_button() {
    var parent = document.getElementById(&#39;myContainer&#39;);
    var div = document.createElement(&#39;div&#39;);
    div.className = &#39;myDivCSSClass&#39;;
    div.id = &#39;myDivId&#39;;
    div.style.position = &#39;absolute&#39;;
    div.style.left = &#39;300px&#39;;
    div.style.top = &#39;200px&#39;;
    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 == &#39;class&#39;) {
                e.className = attrs[key];
            } else if (key == &#39;id&#39;) {
                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 中的 dc6dce4a544fdca2df29d5ac0ea9906b 元素。注意,attrs 和 style 参数是使用 Javascript 文本对象而给出的。

清单 7. 创建 dc6dce4a544fdca2df29d5ac0ea9906b 的简便方法

function handle_button() {
    var parent = document.getElementById(&#39;myContainer&#39;);
    parent.appendChild(elem(&#39;div&#39;,
      {class: &#39;myDivCSSClass&#39;, id: &#39;myDivId&#39;}
      {position: &#39;absolute&#39;, left: &#39;300px&#39;, top: &#39;200px&#39;},
      &#39;This is the first text of the rest of this code&#39;));
}

    在您想要快速创建大量复杂的 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 的技巧和诀窍)的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
解码RSS:内容提要的XML结构解码RSS:内容提要的XML结构Apr 17, 2025 am 12:09 AM

RSS的XML结构包括:1.XML声明和RSS版本,2.频道(Channel),3.条目(Item)。这些部分构成了RSS文件的基础,允许用户通过解析XML数据来获取和处理内容信息。

如何解析和利用基于XML的RSS提要如何解析和利用基于XML的RSS提要Apr 16, 2025 am 12:05 AM

RSSFEEDSUSEXMLTOSYNDICATECONTENT; PARSINGTHEMINVOLVESLOADINGINGINGINGINSSTRUCTURE,andExtractingData.ApplicationsIncludeBuildBuildingNewSagGregatorSaterNewSagGregatorSator andTrackingPodcastepodcastepisodes。

RSS文档:他们如何提供您喜欢的内容RSS文档:他们如何提供您喜欢的内容Apr 15, 2025 am 12:01 AM

RSS文档的工作原理是通过XML文件发布内容更新,用户通过RSS阅读器订阅并接收通知。1.内容发布者创建并更新RSS文档。2.RSS阅读器定期访问并解析XML文件。3.用户浏览和阅读更新内容。使用示例:订阅TechCrunch的RSS源,只需复制链接到RSS阅读器中即可。

用XML建造供稿:RSS的动手指南用XML建造供稿:RSS的动手指南Apr 14, 2025 am 12:17 AM

使用XML构建RSSfeed的步骤如下:1.创建根元素并设置版本;2.添加channel元素及其基本信息;3.添加条目(item)元素,包括标题、链接和描述;4.转换XML结构为字符串并输出。通过这些步骤,你可以从零开始创建一个有效的RSSfeed,并通过添加额外的元素如发布日期和作者信息来增强其功能。

创建RSS文档:逐步教程创建RSS文档:逐步教程Apr 13, 2025 am 12:10 AM

创建RSS文档的步骤如下:1.使用XML格式编写,根元素为,包含元素。2.在内添加、、等元素描述频道信息。3.添加元素,每个代表一个内容条目,包含、、、等。4.可选地添加和元素,丰富内容。5.确保XML格式正确,使用在线工具验证,优化性能并保持内容更新。

XML在RSS中的作用:联合内容的基础XML在RSS中的作用:联合内容的基础Apr 12, 2025 am 12:17 AM

XML在RSS中的核心作用是提供一种标准化和灵活的数据格式。1.XML的结构和标记语言特性使其适合数据交换和存储。2.RSS利用XML创建标准化格式,方便内容共享。3.XML在RSS中的应用包括定义feed内容的元素,如标题和发布日期。4.优势包括标准化和可扩展性,挑战包括文件冗长和严格语法要求。5.最佳实践包括验证XML有效性、保持简洁、使用CDATA和定期更新。

从XML到可读的内容:揭开RSS feed的神秘面纱从XML到可读的内容:揭开RSS feed的神秘面纱Apr 11, 2025 am 12:03 AM

rssfeedsarexmldocuments usedforcontentAggregation and distribution.totransformthemintoreadableContent:1)parsethethexmlusinglibrarieslibrariesliblarieslikeparserinparserinpython.2)andledifferentifferentrssssssssssssssssssssssssssssssssssssssssssssssersions andpotentionparsingrorS.3)

是否有基于JSON的RSS替代方案?是否有基于JSON的RSS替代方案?Apr 10, 2025 am 09:31 AM

JSONFeed是一种基于JSON的RSS替代方案,其优势在于简洁性和易用性。1)JSONFeed使用JSON格式,易于生成和解析。2)它支持动态生成,适用于现代Web开发。3)使用JSONFeed可以提升内容管理效率和用户体验。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境