5

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>
        </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.

5
  • What version(s) of IE? There is som indication that IE does not always return correct information in some versions. Commented Jul 4, 2011 at 13:15
  • if this line correct: var children = element.getElementsByTagName(('xrtml:LinkItem'); you have an extra opening parens, so i just wanted to make sure that's just a typo here and not your code. Commented Jul 4, 2011 at 13:23
  • thanks dtan, that was just a typo, i edited the question Commented Jul 4, 2011 at 13:53
  • 1
    Your example markup doesn't show any children in your LinkItem nodes. Do they actually have children in your actual code? Commented Jul 11, 2011 at 9:53
  • could you post the whole html page? I just tried with my IE8 and it worked... check it: i51.tinypic.com/3449um9.png Commented Jul 12, 2011 at 20:34

5 Answers 5

2

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".

Sign up to request clarification or add additional context in comments.

4 Comments

thank you, my problem is exclusively with IE and i have to do this within the realm of html+javascript. can't do it with xslt
Believe me, XPath is what you need. Take a look at those two links: developer.mozilla.org/en/… and stackoverflow.com/questions/529556/…
@André, what do you mean by term "within the realm of html+javascript" - in IE you have the possibility to use xpath in javascript/jscript (whatever)
Did you get any results/bugs?
1

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

Comments

1

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.

2 Comments

so, isn't there a way to solve this in versions earlier than 9?
Maybe something with DTD but I didn't really look into it.
1

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.

Comments

0

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

2 Comments

looking at the code, seems absolutely perfect. I'm going to try it out today. thanks
@André Alçada Padez: Only if you haven't got nested lists, otherwise it stops on the first closing LinkList tag. This can be fixed by keeping track of opening/closing parent.tagName tags.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.