开发者

How to use Javascript to find namespaced HTML elements

I am writing something academic where I have namespaced HTML elements like:

<ns:LinkList id="sitesuteis" cssClass="classone">
            <ns:LinkItem id="LI1" href="http://www.ibt.pt/" target="_blank">IBT</ns:LinkItem>
            <ns:LinkItem id="LI2" href="http://http://html5demos.com/t/" target="_blank">HTML5 Demos</ns:LinkItem>
            <ns:LinkItem id="LI3" href="http://diveintohtml5.ep.io/" target="_blank">Dive into HTML5</ns:LinkItem>
            <ns:LinkItem id="LI4" href="http://html5boilerplate.com/" target="_blank">HTML5 Boilerplate</ns:LinkItem&开发者_开发百科gt;
        </ns:LinkList>

Now, in JavaScript I am trying:

var elements = document.getElementsByTagName('ns:LinkItem');
element = elements[0];
console.log(element.getAttribute('id'));
//I get a correct value in all browsers

to get all the ChildNodes in my elements[0]. It works fine in all browsers, except -IE lt 9-

I tried:

var children = element.getElementsByTagName('ns:LinkItem');
console.log(children.length);

and:

var children = Array();
for (i=0; i<element.childNodes.length; i++){
   alert(element.childNodes[i].nodeName);
   if (element.childNodes[i].nodeName=="NS:LINKITEM"){
      children.push(element.childNodes[i]);
   }
}
console.log(children.length);

In both console.logs, I get the correct length (4) in every browser except Internet Explorer 8 or less where I get 0.

According to @Shadow Wizard, in Internet Explorer 8 and below, the canHaveChildren property of the element is false which means dead end, the browser simply doesn't support having child nodes for this tag, same way that <br /> can't have child nodes for example. I have tried it and it is true. If I try:

element.parentNode  

in Internet Explorer 8 or less, I get the div that contains my markup and in the other browsers I get my parent <ns:LinkList>.

I really need a hack for this, and I can't seem to find one.


I believe the following function should work - I've used it in a previous project, and I believe it works effectively in Internet Explorer 6, 7, and 8. I don't have a good way to test in Internet Explorer 9, but I'm guessing it should work properly, as Internet Explorer 9 supports getElementsByTagNameNS. It's pretty straightforward, and relies on core browser methods.

/**
 * Cross-browser implementation for namespaced tags
 * @param {DOM Node} n          Parent node
 * @param {String} tag          Tag name you're trying to retrieve
 * @param {String} [ns]         Namespace prefix
 */
getElementsByTagName = function(n, tag, ns) {
    // map the namespace prefix to the full namespace URIs
    var nsMap = {
        'svg': 'http://www.w3.org/2000/svg'
        // etc - whatever's relevant for your page
    };
    if (!ns) {
        // no namespace - use the standard method
        return n.getElementsByTagName(tag);
    }
    if (n.getElementsByTagNameNS && nsMap[ns]) {
        // function and namespace both exist
        return n.getElementsByTagNameNS(nsMap[ns], tag);
    }
    // no function, get with the colon tag name
    return n.getElementsByTagName(ns + ':' + tag);
};

// get a list of svg:circle elements
getElementsByTagName(document, 'circle', 'svg');

The only pain point here is the requirement to define a mapping from the namespace prefix to the full namespace URI. If you want to make this a more portable function, you could have nsMap be a function argument, rather than something defined in the function body; or you could refer to a namespace map object in the global context.

Here's a fully modularized version, with a slightly tighter version of getElementsByTagName:

var namespaces = (function() {
    var nsMap = {};

    /**
     * Add a new XML namespace with prefix
     * @param {String} prefix       Prefix for new namespace
     * @param {String} uri          Full URI of new namespace
     */
    function addNamespace(prefix, uri) {
        nsMap[prefix] = uri;
    }

    /**
     * Cross-browser implementation for namespaced tags
     * @param {DOM Node} [n]        Parent node
     * @param {String} tag          Tag name you're trying to retrieve
     * @param {String} [ns]         Namespace prefix
     */
    function getElementsByTagName(n, tag, ns) {
        return !ns ?
            n.getElementsByTagName(tag) :
            (n.getElementsByTagNameNS && nsMap[ns]) ?
                n.getElementsByTagNameNS(nsMap[ns], tag) :
                n.getElementsByTagName(ns + ':' + tag);
    }

    return {
        addNamespace: addNamespace,
        getElementsByTagName: getElementsByTagName
    };
}());

// set the svg namespace
namespaces.addNamespace('svg', 'http://www.w3.org/2000/svg');
// get a list of svg:circle elements
namespaces.getElementsByTagName(document, 'circle', 'svg');


In non-Internet Explorer browsers, I would recommend getElementsByTagNameNS to get the elements in a specific namespace.

In Internet Explorer, you can use XPath instead.

jQuery also provides a way to use namespaces; it seems to be covered in "jQuery XML parsing with namespaces".


In Internet Explorer 8 and below, the canHaveChildren property of the element is false which means dead end, the browser simply doesn't support having child nodes for this tag, same way that <br /> can't have child nodes for example.

This has been fixed in Internet Explorer 9 though.


This is a hack, but it might be robust enough for what you need:

function getElementsByTagName(parent, tagName)
{
    if(typeof parent.canHaveChildren === 'undefined' || parent.canHaveChildren)
    {
        return parent.getElementsByTagName(tagName);
    }

    var elements = [];
    var cursor = parent;
    while(cursor.nextSibling && cursor.nextSibling.tagName !== ('/' + parent.tagName))
    {
        if(cursor.tagName === tagName.toUpperCase())
        {
            elements.push(cursor);
        }
        cursor = cursor.nextSibling;
    }
    return elements;
}

function getText(parent)
{
    return parent.innerHTML || parent.nextSibling.data;
}

For example:

var element = document.getElementById('sitesuteis');
var children = getElementsByTagName(element, 'ns:LinkItem');
console.log(children.length);

for(var i = 0; i < children.length; i++)
{
    console.log([getText(children[i]), children[i].getAttribute('href')]);
}


I never had this issue, so this is rather just a proposal or hint.

I found "getElementsByTagName (W3C DOM Core method)" for MSIE namespace "restriction".

I finally found the point in "XPath in JavaScript, Part 3" that would confirm XPath use would be the point:

By default, Internet Explorer’s XPath engine doesn’t work with namespaces (the same as the DOM Level 3 XPath implementation). Namespace information must be specified ahead of time as a property on the XML DOM document object itself. Consider the following XML code:

<books xmlns:wrox="http://www.wrox.com/"
xmlns="http://www.amazon.com/">
    <wrox:book>Professional JavaScript</book> </books>

In order to use XPath queries on this document, you’d first need to define namespace information for the wrox and default namespaces. You can do so via the setProperty() method, passing in "SelectionNamespaces" and a space-separated string of namespace declarations. Example:

xmldoc.setProperty("SelectionNamespaces",
    "xmlns:wrox='http://www.wrox.com/' xmlns='http://www.amazon.com/'"); var book =
xmldoc.documentElement.selectSingleNode("wrox:book");

Note that the namespace declarations are in the same format as they appear in the XML. Unfortunately, there is no automatic way to extract the namespace information from the document for use with XPath queries. Conclusion

Internet Explorer does have XPath support, but it comes with several caveats. First is that XPath queries only work on XML documents, not on HTML documents and therefore can’t be used on document to help find elements on the page. Second, the XPath implementation is very basic and allows only basic return types (nodes and NodeSet objects). Still, if you’re dealing with XML data, XPath remains a fast and convenient way to find specific elements without walking the DOM manually.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜