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

XML 问题: 超越DOM(轻松使用 DOM 的技巧和诀窍)

Feb 27, 2017 pm 04:30 PM

文档对象模型(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 太过冗长,并且简单的功能也需要编写大量代码。例如,如果您想创建一个包含文本并响应点击按钮的

元素,代码可能类似于:

清单 5. 创建

的“漫长之路”

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 中的

元素。注意,attrs 和 style 参数是使用 Javascript 文本对象而给出的。

清单 7. 创建

的简便方法

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

热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.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

能否用PowerPoint打开XML文件 能否用PowerPoint打开XML文件 Feb 19, 2024 pm 09:06 PM

XML文件可以用PPT打开吗?XML,即可扩展标记语言(ExtensibleMarkupLanguage),是一种被广泛应用于数据交换和数据存储的通用标记语言。与HTML相比,XML更加灵活,能够定义自己的标签和数据结构,使得数据的存储和交换更加方便和统一。而PPT,即PowerPoint,是微软公司开发的一种用于创建演示文稿的软件。它提供了图文并茂的方

使用Python实现XML数据的筛选和排序 使用Python实现XML数据的筛选和排序 Aug 07, 2023 pm 04:17 PM

使用Python实现XML数据的筛选和排序引言:XML是一种常用的数据交换格式,它以标签和属性的形式存储数据。在处理XML数据时,我们经常需要对数据进行筛选和排序。Python提供了许多有用的工具和库来处理XML数据,本文将介绍如何使用Python实现XML数据的筛选和排序。读取XML文件在开始之前,我们需要先读取XML文件。Python有许多XML处理库,

使用Python实现XML数据的合并和去重 使用Python实现XML数据的合并和去重 Aug 07, 2023 am 11:33 AM

使用Python实现XML数据的合并和去重XML(eXtensibleMarkupLanguage)是一种用于存储和传输数据的标记语言。在处理XML数据时,有时候我们需要将多个XML文件合并成一个,或者去除重复的数据。本文将介绍如何使用Python实现XML数据的合并和去重的方法,并给出相应的代码示例。一、XML数据合并当我们有多个XML文件,需要将其合

Python中的XML数据转换为CSV格式 Python中的XML数据转换为CSV格式 Aug 11, 2023 pm 07:41 PM

Python中的XML数据转换为CSV格式XML(ExtensibleMarkupLanguage)是一种可扩展标记语言,常用于数据的存储和传输。而CSV(CommaSeparatedValues)则是一种以逗号分隔的文本文件格式,常用于数据的导入和导出。在处理数据时,有时需要将XML数据转换为CSV格式以便于分析和处理。Python作为一种功能强大

使用PHP将XML数据导入数据库 使用PHP将XML数据导入数据库 Aug 07, 2023 am 09:58 AM

使用PHP将XML数据导入数据库引言:在开发中,我们经常需要将外部数据导入到数据库中进行进一步的处理和分析。而XML作为一种常用的数据交换格式,也经常被用来存储和传输结构化数据。本文将介绍如何使用PHP将XML数据导入数据库。步骤一:解析XML文件首先,我们需要解析XML文件,提取出需要的数据。PHP提供了几种解析XML的方式,其中最常用的是使用Simple

Python实现XML和JSON之间的转换 Python实现XML和JSON之间的转换 Aug 07, 2023 pm 07:10 PM

Python实现XML和JSON之间的转换导语:在日常的开发过程中,我们常常需要将数据在不同的格式之间进行转换。XML和JSON是常见的数据交换格式,在Python中,我们可以使用各种库来实现XML和JSON之间的相互转换。本文将介绍几种常用的方法,并附带代码示例。一、XML转JSON在Python中,我们可以使用xml.etree.ElementTree模

使用Python处理XML中的错误和异常 使用Python处理XML中的错误和异常 Aug 08, 2023 pm 12:25 PM

使用Python处理XML中的错误和异常XML是一种常用的数据格式,用于存储和表示结构化的数据。当我们使用Python处理XML时,有时可能会遇到一些错误和异常。在本篇文章中,我将介绍如何使用Python来处理XML中的错误和异常,并提供一些示例代码供参考。使用try-except语句捕获XML解析错误当我们使用Python解析XML时,有时候可能会遇到一些

Python解析XML中的特殊字符和转义序列 Python解析XML中的特殊字符和转义序列 Aug 08, 2023 pm 12:46 PM

Python解析XML中的特殊字符和转义序列XML(eXtensibleMarkupLanguage)是一种常用的数据交换格式,用于在不同系统之间传输和存储数据。在处理XML文件时,经常会遇到包含特殊字符和转义序列的情况,这可能会导致解析错误或者误解数据。因此,在使用Python解析XML文件时,我们需要了解如何处理这些特殊字符和转义序列。一、特殊字符和

See all articles