Maison > Article > développement back-end > XML Matters : Au-delà du DOM (Conseils et astuces pour utiliser facilement le DOM)
Le Document Object Model (DOM) est l'un des outils les plus couramment utilisés pour manipuler les données XML et HTML, mais son potentiel est rarement pleinement exploité. En tirant parti du DOM et en le rendant plus facile à utiliser, vous obtenez un outil puissant pour les applications XML, y compris les applications Web dynamiques.
Cet article présente une chroniqueuse invitée et mon amie et collègue Dethe Elza. Dethe possède une vaste expérience dans le développement d'applications Web utilisant XML, et je tiens à le remercier pour son aide en m'initiant à la programmation XML utilisant DOM et ECMAScript. Restez à l’écoute de cette chronique pour en savoir plus sur Dethe.
—— David Mertz
DOM est l'une des API standards pour le traitement du XML et du HTML. Il est souvent critiqué pour être gourmand en mémoire, lent et verbeux. Pourtant, c'est le meilleur choix pour de nombreuses applications, et il est certainement beaucoup plus simple que l'autre API majeure de XML, SAX. Le DOM apparaît progressivement dans des outils tels que les navigateurs Web, les navigateurs SVG, OpenOffice et autres.
DOM est génial car il s'agit d'un standard largement implémenté et intégré à d'autres standards. En tant que norme, sa gestion des données est indépendante du langage de programmation (ce qui peut ou non être un point fort, mais au moins cela rend la façon dont nous traitons les données cohérente). Le DOM est désormais non seulement intégré aux navigateurs Web, mais fait également partie de nombreuses spécifications basées sur XML. Maintenant qu'il fait partie de votre arsenal, et que peut-être vous l'utilisez encore occasionnellement, je pense qu'il est temps de profiter pleinement de ce qu'il nous apporte.
Après avoir travaillé avec le DOM pendant un certain temps, vous verrez des modèles se développer – des choses que vous voulez faire encore et encore. Les raccourcis vous aident à travailler avec des DOM longs et à créer un code élégant et explicite. Voici une collection de trucs et astuces que j'utilise fréquemment, ainsi que quelques exemples javaScript.
insertAfter et PRependChild
La première astuce est "il n'y a pas d'astuce". Le DOM dispose de deux méthodes pour ajouter des nœuds enfants à un nœud conteneur (généralement un élément, mais également un document ou un fragment de document) : appendChild(node) et insertBefore(node, referenceNode). Il semble qu'il manque quelque chose. Que se passe-t-il si je souhaite insérer ou ajouter un nœud enfant après un nœud de référence (en faisant du nouveau nœud le premier de la liste) ? Pendant de nombreuses années, ma solution était d'écrire la fonction suivante :
Listing 1. Mauvaise façon d'insérer et d'ajouter par précédent
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); } }
En fait, comme Comme dans Listing 1, la fonction insertBefore() a été définie pour revenir à appendChild() lorsque le nœud de référence est vide. Ainsi, au lieu d'utiliser la méthode ci-dessus, vous pouvez utiliser la méthode du listing 2, ou les ignorer et simplement utiliser les fonctions intégrées :
Listing 2. Manière correcte d'insérer et d'ajouter par le précédent
function insertAfter(parent, node, referenceNode) { parent.insertBefore(node, referenceNode.nextSibling); } function prependChild(parent, node) { parent.insertBefore(node, parent.firstChild); }
Si vous êtes nouveau dans la programmation DOM, il est nécessaire de souligner que même si vous pouvez avoir plusieurs pointeurs pointant vers un nœud, le nœud ne peut exister qu'à un seul endroit dans le Arbre DOM. Donc, si vous souhaitez l'insérer dans l'arborescence, il n'est pas nécessaire de le supprimer d'abord de l'arborescence car il sera supprimé automatiquement. Ce mécanisme est pratique lors de la réorganisation des nœuds en les insérant simplement dans leurs nouvelles positions.
Selon ce mécanisme, si vous souhaitez échanger les positions de deux nœuds adjacents (appelés node1 et node2), vous pouvez utiliser l'une des solutions suivantes :
node1.parentNode .insertBefore (node2, node1);
ou
node1.parentNode.insertBefore(node1.nextSibling, node1);
Que pouvez-vous faire d'autre avec DOM ?
DOM est largement utilisé dans les pages Web. Si vous visitez le site des bookmarklets (voir Rubriques connexes), vous trouverez de nombreux scripts courts et créatifs permettant de réorganiser les pages, d'extraire des liens, de masquer des images ou des publicités Flash, etc.
Cependant, comme Internet Explorer ne définit pas de constantes d'interface de nœud (qui peuvent être utilisées pour identifier les types de nœuds), vous devez vous assurer que si vous manquez une constante d'interface, vous définissez d'abord la constante d'interface dans le script DOM. pour le web.
Listing 3. Assurez-vous que le nœud est défini
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; }
Le listing 4 montre comment extraire tous les nœuds de texte contenus dans le nœud :
Liste 4. Texte interne
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(''); }
Raccourci
Les gens se plaignent souvent que le DOM est trop verbeux et que des fonctions simples ont besoin à écrire Beaucoup de code. Par exemple, si vous souhaitez créer un élément dc6dce4a544fdca2df29d5ac0ea9906b contenant du texte et répondant à un clic sur un bouton, le code pourrait ressembler à :
Listing 5. Le "long chemin" pour créer un dc6dce4a544fdca2df29d5ac0ea9906b
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 中的 dc6dce4a544fdca2df29d5ac0ea9906b 元素。注意,attrs 和 style 参数是使用 Javascript 文本对象而给出的。
清单 7. 创建 dc6dce4a544fdca2df29d5ac0ea9906b 的简便方法
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)情况。
Conclusion
J'ai couvert beaucoup de choses que le DOM peut faire, mais il y a tellement plus que le DOM (et JavaScript) peut faire. Étudiez et explorez ces exemples pour voir comment ils peuvent être utilisés pour résoudre des problèmes pouvant nécessiter des scripts clients, des modèles ou des API spécialisées.
DOM a ses propres limites et défauts, mais il présente également de nombreux avantages : il est intégré à de nombreuses applications ; il fonctionne de la même manière qu'il s'agisse de la technologie Java, de Python ou de JavaScript ; il est très simple d'utiliser SAX ; en utilisant le modèle ci-dessus, il est à la fois simple et puissant à utiliser. Un nombre croissant d'applications commencent à prendre en charge DOM, notamment les applications basées sur Mozilla, OpenOffice et XMetaL de Blast Radius. De plus en plus de spécifications nécessitent et étendent le DOM (par exemple SVG), de sorte que le DOM soit toujours autour de vous. Utiliser cet outil largement déployé est une décision judicieuse.
Ce qui précède est le contenu de la question XML : au-delà du DOM (Conseils et astuces pour utiliser facilement DOM). Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !