It's surprising to me that we can use jQuery to query and manipulate XMLDOM cross browsers. Like the example below:

var x = $("<xml><rows><row a='ATTR'>FIRST</row></rows></xml>");
x.find("row").attr("a", "Cool!").text("It rocks");
alert(x.find("row").attr("a") + " " + x.find("row").text());
x.find("rows").append("<mark>Awesome</mark>");//Exception in IE
alert(x.find("mark").text());

As you see, I can find the "row" XML node, change its attribute and innerText, then append a childNode from a XML string... Unfortunately, append(xmlString) will cause exception in IE.

Not wanting to go back to use loosy XMLDOM API, I decide to write a simple XMLDOM extension plugin to solve this problem.  The jquery.xmlext.js extend appendXml(), prependXml(), afterXml(), beforeXml() methods which accept string as argument and work like append(), prepend(), after() and before() on XMLDOM.  The xml() is added to provide cross-browser outerXML dumping.

The $("<xml>...</xml>") method uses XHTML to simulate XML, some details will be lost, like case-sensitivity.  jQuery.parseXml() provides cross-browser string to XMLDOM conversion.

/*
* jQuery XML Extension plugin
* Version 1.1 Beta (30-APR-2009)
* by Jeffrey Lee, http://blog.darkthread.net
*
* Examples:
$(function() {
var x = $("<xml><items><center /></items></xml>");
x.find("center").appendXml("<bottom />").prependXml("<top />");
x.find("center").afterXml("<after />").beforeXml("<before />");
alert(x.xml());
});
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
; (function($) {
    function mnpXml(opCode, xmlStr) {
        return this.each(function() {
            if (typeof xmlStr != "string") return;
            if (!jQuery.isXMLDoc(this)) return;
            var node = $.parseXml(xmlStr).firstChild.cloneNode(true);
            switch (opCode) {
                case "append":
                    this.appendChild(node);
                    break;
                case "prepend":
                    if (this.childNodes.length > 0)
                        this.insertBefore(node, this.firstChild);
                    else
                        this.appendChild(node);
                    break;
                case "after":
                    if (this.nextSibling)
                        this.parentNode.insertBefore(node, this.nextSibling);
                    else
                        this.parentNode.appendChild(node);
                    break;
                case "before":
                    this.parentNode.insertBefore(node, this);
                    break;
            }
        });
    }
    $.fn.extend({
        appendXml: function(s) {
            return mnpXml.call(this, "append", s);
        },
        prependXml: function(s) {
            return mnpXml.call(this, "prepend", s);
        },
        afterXml: function(s) {
            return mnpXml.call(this, "after", s);
        },
        beforeXml: function(s) {
            return mnpXml.call(this, "before", s);
        },
        xml: function() {
            var elem = this[0];
            return elem.xml || (new XMLSerializer()).serializeToString(elem);
        },
        innerXml: function() {
            var s = this.xml();
            var i = s.indexOf(">"), j = s.lastIndexOf("<");
            if (j > i)
                return s.substring(i + 1, j);
            else
                return "";
        }
    });
    $.extend(jQuery, {
        parseXml: function(xmlStr) {
            if (window.ActiveXObject) {
                var xd = new ActiveXObject("Microsoft.XMLDOM");
                xd.async = false;
                xd.loadXML(xmlStr);
                return xd;
            }
            else if (typeof DOMParser != "undefined") {
                var xd = new DOMParser().parseFromString(xmlStr, "text/xml");
                return xd;
            }
            else return null;
        },
        toXml: function(obj, nodeName, useAttr) {
            var x = $($.parseXml("<" + nodeName + " />"));
            var n = x.find(":first");
            for (var p in obj) {
                if (useAttr)
                    n.attr(p, obj[p]);
                else
                    n.appendXml("<" + p + " />").find(p).text(obj[p]);
            }
            return x[0];
        }
    });
})(jQuery);

You can download jquery.xmlext.js to play or test it directly on Mini jQuery Lab with these code:

$.getScript("jquery.xmlext.js", function() {
            var xd = $.parseXml("<items><center /></items>");
            var x = $(xd);
            x.find("center").appendXml("<bottom />").prependXml("<top />");
            x.find("top,bottom").afterXml("<After />").beforeXml("<Before />");
            alert(x.xml());
            var obj = { Name: "Darkthread", Score: 100 };
            xd = $.toXml(obj, "Person");
            alert($(xd).xml());
            xd = $.toXml(obj, "Person", true);
            alert($(xd).xml());
});

 

 【中文摘要】

jQuery可以用來解析XMLDOM,真是件爽快的事,我一直對XMLDOM的那堆API很感冒,就拿簡單的新增Node來說,一下是createElement,再setAttribute,又要appendChild的,頗為煩人。jQuery除了可以讀XmlNode,還可以用來修改Attribute及InnerText。例如以下的程式:

var x = $("<xml><rows><row a='ATTR'>FIRST</row></rows></xml>");
x.find("row").attr("a", "Cool!").text("It rocks");
alert(x.find("row").attr("a") + " " + x.find("row").text());
x.find("rows").append("<mark>Awesome</mark>");//Exception in IE
alert(x.find("mark").text());

很不幸地,以上的寫法在IE裡會敗在append()上。由於不想為了這點小問題回頭去攪和XMLDOM那一堆囉嗦的API,我決定寫一個小Plugin解決這個問題,增加appendXml(), prependXml(), afterXml(), beforeXml()四個API,除了傳入參數限定是XML字串,其餘用法與append(), prepend(), after(), before()相同,另外我還順手加上xml()一併解決跨瀏覽器轉換成XML字串的需求。另外,我發現原先$("<xml>..</xml>")的玩法是用XHTML去模擬XML,會損失一些XML特性,例如: 節點名跟屬性名的大小寫,故還是應回歸正途,用XMLDOM來做,所以我加了一個jQuery.parseXml()。

大家可以下載 jquery.xmlext.js回去玩,或是直接在 Mini jQuery Lab 用以下程式做測試:

$.getScript("jquery.xmlext.js", function() {
            var xd = $.parseXml("<items><center /></items>");
            var x = $(xd);
            x.find("center").appendXml("<bottom />").prependXml("<top />");
            x.find("top,bottom").afterXml("<After />").beforeXml("<Before />");
            alert(x.xml());
            var obj = { Name: "Darkthread", Score: 100 };
            xd = $.toXml(obj, "Person");
            alert($(xd).xml());
            xd = $.toXml(obj, "Person", true);
            alert($(xd).xml());
});

有遇到問題的再跟我說吧!


Comments

# by catbaby

您好, 目前在IE10使用jquery.xmlext.js時, 於this.appendChild(node); 會出現HierarchyRequestError,不知是否有新的解法??

# by Jeffrey

to catbaby, 我用IE10測試沒出錯。放了一個JSBin Demo(http://jsbin.com/OgelUqU/1/edit),你試改看看可否重現你說的問題?

# by Will

ie9 使用时,报错:HierarchyRequestError

Post a comment