Heim  >  Artikel  >  Backend-Entwicklung  >  XML-Angelegenheiten: Jenseits des DOM (Tipps und Tricks für die einfache Verwendung des DOM)

XML-Angelegenheiten: Jenseits des DOM (Tipps und Tricks für die einfache Verwendung des DOM)

黄舟
黄舟Original
2017-02-27 16:30:061362Durchsuche

Das Document Object Model (DOM) ist eines der am häufigsten verwendeten Tools zur Bearbeitung von XML- und HTML-Daten, sein Potenzial wird jedoch selten vollständig ausgeschöpft. Indem Sie die Vorteile des DOM nutzen und es einfacher verwenden, erhalten Sie ein leistungsstarkes Tool für XML-Anwendungen, einschließlich dynamischer Webanwendungen.

Dieser Artikel stellt eine Gastkolumnistin vor, die auch meine Freundin und Kollegin Dethe Elza ist. Dethe verfügt über umfangreiche Erfahrung in der Entwicklung von Webanwendungen mit XML und ich möchte ihm für seine Hilfe danken, die mir dabei geholfen hat, mich in die XML-Programmierung mit DOM und ECMAScript einzuführen. Weitere Informationen von Dethe finden Sie in dieser Kolumne.
—— David Mertz

DOM ist eine der Standard-APIs für die Verarbeitung von XML und HTML. Es wird oft dafür kritisiert, dass es speicherintensiv, langsam und ausführlich ist. Dennoch ist es für viele Anwendungen die beste Wahl und sicherlich viel einfacher als SAX, die andere wichtige API von XML. DOM taucht nach und nach in Tools wie Webbrowsern, SVG-Browsern, OpenOffice und anderen auf.

DOM ist großartig, weil es ein Standard ist und weithin implementiert und in andere Standards integriert ist. Standardmäßig ist der Umgang mit Daten unabhängig von der Programmiersprache (was vielleicht eine Stärke ist, aber zumindest die Art und Weise, wie wir mit Daten umgehen, konsistent macht). Mittlerweile ist das DOM nicht nur in Webbrowsern integriert, sondern auch Bestandteil vieler XML-basierter Spezifikationen. Jetzt, da es Teil Ihres Arsenals ist und Sie es vielleicht immer noch gelegentlich verwenden, ist es wohl an der Zeit, die Vorteile zu nutzen, die es bietet.

Nachdem Sie eine Weile mit dem DOM gearbeitet haben, werden Sie feststellen, dass sich Muster entwickeln – Dinge, die Sie immer wieder tun möchten. Mithilfe von Verknüpfungen können Sie mit langen DOMs arbeiten und selbsterklärenden, eleganten Code erstellen. Hier finden Sie eine Sammlung von Tipps und Tricks, die ich häufig verwende, sowie einige JavaScript-Beispiele.

insertAfter und PRependChild

Der erste Trick ist „Es gibt keinen Trick“. Das DOM verfügt über zwei Methoden zum Hinzufügen untergeordneter Knoten zu einem Containerknoten (normalerweise ein Element, aber auch ein Dokument oder Dokumentfragment): appendChild(node) und insertBefore(node, referenceNode). Es scheint, als würde etwas fehlen. Was passiert, wenn ich nach einem Referenzknoten einen untergeordneten Knoten einfügen oder voranstellen möchte (wodurch der neue Knoten der erste in der Liste wird)? Viele Jahre lang bestand meine Lösung darin, die folgende Funktion zu schreiben:

Listing 1. Falscher Weg zum Einfügen und Hinzufügen nach vorherigem

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);
    }
}


Eigentlich wie in Listing 1: Die Funktion insertBefore() wurde so definiert, dass sie zu appendChild() zurückkehrt, wenn der Referenzknoten leer ist. Anstatt also die obige Methode zu verwenden, können Sie die Methode in Listing 2 verwenden oder sie überspringen und einfach die integrierten Funktionen verwenden:

Listing 2. Korrekte Methode zum Einfügen und Hinzufügen nach vorherigem

function insertAfter(parent, node, referenceNode) {
    parent.insertBefore(node, referenceNode.nextSibling);
}
function prependChild(parent, node) {
    parent.insertBefore(node, parent.firstChild);
}


Wenn Sie neu in der DOM-Programmierung sind, müssen Sie darauf hinweisen, dass Sie zwar mehrere Zeiger haben können, die auf einen Knoten zeigen, der Knoten jedoch nur an einer Stelle in der existieren kann DOM-Baum. Wenn Sie es also in den Baum einfügen möchten, müssen Sie es nicht zuerst aus dem Baum entfernen, da es automatisch entfernt wird. Dieser Mechanismus ist praktisch, wenn Sie Knoten neu anordnen, indem Sie sie einfach an neuen Positionen einfügen.

Wenn Sie nach diesem Mechanismus die Positionen zweier benachbarter Knoten (Knoten1 und Knoten2 genannt) austauschen möchten, können Sie eine der folgenden Lösungen verwenden:

node1.parentNode .insertBefore (node2, node1);
oder
node1.parentNode.insertBefore(node1.nextSibling, node1);

Was kann man sonst noch mit DOM machen?

DOM wird häufig in Webseiten verwendet. Wenn Sie die Bookmarklets-Website besuchen (siehe Verwandte Themen), finden Sie viele kreative Kurzskripte, mit denen Sie Seiten neu anordnen, Links extrahieren, Bilder oder Flash-Anzeigen ausblenden und vieles mehr können.

Da Internet Explorer jedoch keine Knotenschnittstellenkonstanten definiert (die zur Identifizierung von Knotentypen verwendet werden können), müssen Sie sicherstellen, dass Sie, wenn Sie eine Schnittstellenkonstante vermissen, zuerst die Schnittstellenkonstante im DOM-Skript definieren für das Web.

Listing 3. Stellen Sie sicher, dass der Knoten definiert ist

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;
}


Listing 4 zeigt, wie alle im Knoten enthaltenen Textknoten extrahiert werden:

Listing 4. Interner Text

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;);
}


Abkürzung

Leute beschweren sich oft darüber, dass das DOM zu ausführlich ist und einfache Funktionen dies erfordern Es muss viel Code geschrieben werden. Wenn Sie beispielsweise ein dc6dce4a544fdca2df29d5ac0ea9906b-Element erstellen möchten, das Text enthält und auf einen Schaltflächenklick reagiert, könnte der Code wie folgt aussehen:

Listing 5. Der „lange Weg“ zum Erstellen eines 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)情况。


Fazit
Ich habe viele Dinge besprochen, die das DOM kann, aber es gibt noch so viel mehr, was das DOM (und JavaScript) kann. Studieren und erforschen Sie diese Beispiele, um zu sehen, wie sie zur Lösung von Problemen verwendet werden können, die möglicherweise Client-Skripte, Vorlagen oder spezielle APIs erfordern.

DOM hat seine eigenen Einschränkungen und Mängel, aber es hat auch viele Vorteile: Es ist in viele Anwendungen integriert, es funktioniert auf die gleiche Weise, egal ob es Java-Technologie, Python oder JavaScript verwendet, es ist sehr einfach, SAX zu verwenden; Wenn Sie die obige Vorlage verwenden, ist die Verwendung sowohl einfach als auch leistungsstark. Immer mehr Anwendungen beginnen, DOM zu unterstützen, darunter Mozilla-basierte Anwendungen, OpenOffice und XMetaL von Blast Radius. Immer mehr Spezifikationen erfordern und erweitern DOM (z. B. SVG), sodass das DOM immer in Ihrer Nähe ist. Es wäre ratsam, dieses weit verbreitete Tool zu verwenden.

Das Obige ist der Inhalt der XML-Frage: Beyond DOM (Tipps und Tricks zur einfachen Verwendung von DOM) Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn