XML Namespace Selectors for jQuery
I hit my first real roadbump with jQuery yesterday, a missing feature that really made me stop and stare in puzzlement: jQuery doesn't support xml-namespace selectors. Since I'm trying to parse WebDAV response bodies, and such documents make extensive use of namespaces, it's quite the issue for me. Or rather, it was quite the issue – read on if you're interested in the details, or just download my solution if you're impatient.
Oh sure, jQuery supports prefix selectors just fine. If your document contains an element <D:response>, you can quite safely query for that element by name as long as you remember to backslash-escape the colon:
$(doc).find("D\\:response")
This works as long as you can guarantee that a single prefix is used for the target namespace, and that it's used uniformly throughout the document. But the XML Namespaces standard, as well as the WebDAV standard, make it very clear that you can't rely on this in general. The node name prefix is a purely syntactic construct, while its actual namespace is a semantic property that can be specified in several different ways.
Fortunately, the CSS Level 3 standard provides a very clear syntax and semantics for namespace-aware queries. After declaring 'D' to be the proper WebDAV namespace URI, the CSS-3 equivalent to the above prefix query would be:
$(doc).find("D|response")
Unfortunately, this syntax is largely still a pipe-dream. Not only is it missing from jQuery, but I couldn't find another implementation to help get me off the ground. The only thing for it was to implement it myself – so about six hours later, after studying the internals of the Sizzle selector engine and adventuring from getElementsByTagNameNS through to XPath's namespace-uri() and local-name() functions, I have a solution that I'm happy to show to the world: jquery.xmlns.js.
This plugin extends the jQuery tag and attribute query functions to allow an optional namespace selector. In the simplest case you declare a namespace prefix in the $.xmlns object, then just query for it as normal:
$.xmlns["D"] = "DAV:"; $(doc).find("D|response").each(...);
Sadly, the namespace declarations need to be global since the underlying jQuery/Sizzle selector machinery is stateless. If you're anything like me, such globals will offend your delicate programming sensibilities. To perform a namespace-based query and automatically clean up when you're done, you can use the xmlns query method as follows:
$(doc).xmlns({D:"DAV:"},function() { // The 'D' namespace is declared within this function return this.find("D|response").each(...); // and removed again when the function exits });
It's also possible to specify a default namespace, which will be used when no explicit selector is given. Just pass a string rather than a mapping object, like so:
$(doc).xmlns("DAV:",function() { // This only searches within the 'DAV:' namespace return this.find("response").each(...); });
Namespaced attribute selectors are also supported, although there are some restrictions as spelled out here. The following would search for elements with a 'href' attribute in any namespace:
$(doc).find("[*|href]");
Of course, there are plenty of caveats here. I've tested this on Firefox and IE7 and it meets my expectations, but I haven't explored any of the bizarre corner cases that I'm sure are lurking out there. I also haven't done any performance testing, although I have tried hard not to slow down the common case where a namespace selector is not specified. I'm far from an expert here – any bug reports, success reports or general suggestions most welcome!
Now, to actually get on with the job I was supposed to be doing yesterday...
Comments
Re: XML Namespace Selectors for jQuery
really nice work.
Re: XML Namespace Selectors for jQuery
Cool, just what I searched to enable a jQuery XInclude plugin! Have you thought of submitting this to the jQuery developers? It should be in the core.
Re: XML Namespace Selectors for jQuery
Found a small bug:
Line 245: s/ens/e_ns/ (to say it in Perl)
Re: XML Namespace Selectors for jQuery
Thanks Boldy, I've uploaded a corrected version.
Re: XML Namespace Selectors for jQuery
Great idea, but I am having a bit of a problem using it.
My usage: $.xmlns.DWC = "DWC:" alert($("DWC|error").size() + " == " + $("dwc\:error").size())
and I get 0 == 4 (there are 4 dwc:error elements)
The library itself seems also to cause problems when using $().is() validation
I am testing an element with $(".className").is("A[href]"). It returns false, when jquery.xmlns.js library is loaded and true when I remove it.
What am I doing wrong?
Re: XML Namespace Selectors for jQuery
Some common causes of breakage when using this code:
make sure your page is being served with an XML mimetype; your browser wont process namespaces in text/html documents make sure that "DWC:" is the full URL specifying the namespace, as used in the xmlns:dwc=".." declaration.I'll look into the "is" trouble when I get a chance.
Thank-you!
Hi Ryan,
Just wanted to say thank-you for your work on this.
I'm going to give your script a try. It looks like it's what I've been looking for.
Re: XML Namespace Selectors for jQuery
First of all, rfk (Ryan I suppose), thanks for the plug-in, I needed it.
I use it on jquery-1.4.1 and it doesn't work :) I had the same problem as nxt (the one where he gets 0 == 4).
What I did was patching the Sizzle engine in jQuery and it worked (I still need a lot of testing to do and I'm not sure now jQuery would run OK without jquery.xmlns).
I patched line 2808 in jquery-1.4.1.js to:
ATTR: /[s*((?:[wu00c0-uFFFF-]|\.)*|{1}){0,1}((?:[wu00c0-uFFFF-]|\.)+)s*(?:(S?=)s*(['"])(.?)4|)s*]/,
And line 2809 to:
TAG: /^((?:[wu00c0-uFFFF*-]|\.)*|{1})?((?:[wu00c0-uFFFF*-]|\.)+)/,
When I figure a way to do it more elegantly, I'll write again. All the best
Re: XML Namespace Selectors for jQuery
Ok. An update to my last post. Since it's ugly to patch jQuery itself in order fot it to work with jquery.xmlns, I patched jquery.xmlns to work with jQuery-1.4.1. The problem was that in jQuery-1.4.1 used the Expr.leftMatch property in Sizzle.find to match the elements. This was derived from Expr.match while initializing jQuery. jquery.xmlns redifines $.expr.match.TAG and $.expr.match.ATTR which is OK for jQuery prior to 1.4 (I suppose. I haven't really tested it), but is not enough for newer versions. So, my final solution is, to add the following lines in jquery.xmlns:
// After predefining of $.expr.match.TAG if($.expr.leftMatch) // jQuery-1.4.1 Compliance $.expr.leftMatch[ 'TAG' ] = new RegExp( /(^(?:.|r|n)*?)/.source + $.expr.match[ 'TAG' ].source.replace(/\(d+)/g, function(all, num){return "\" + (num - 0 + 1);}));
And
// After predefining $.expr.match.ATTR if($.expr.leftMatch) // jQuery-1.4.1 Compliance $.expr.leftMatch['ATTR'] = new RegExp( /(^(?:.|r|n)*?)/.source + $.expr.match['ATTR'].source.replace(/\(d+)/g, function(all, num){return "\" + (num - 0 + 1);}));
Re
Thare’s not another way to reach good grade than to compose the homework help referring to this good post but that is, also, easy to buy the tv essay paper at the custom essay writing service.
Re: XML Namespace Selectors for jQuery
Thanks for looking into this Milko, unfortunately I haven't had a chance to play with the new jQuery release. I'll try to put together a new version incorporating your changes and put it through its paces.
Re: XML Namespace Selectors for jQuery
good job,
but i find this error
"en_s is not defined" - Line 245
when i tried this:
$.xmlns['D']='wp:'; $wo.children("D|post_id")
Re: XML Namespace Selectors for jQuery
Hi, again
^ I had a similar problem. I didn't search for code issues and I don't know if it's a bug or I did something wrong.
Anyway, for me it worked when I specified the URI assigned to the namespace prefix.
That is using 'http://www.uriofthewpnamespace.com/' instead of 'wp:'
Re:
Every body acknowledges that today's life seems to be not cheap, but some people need cash for different issues and not every one earns big sums cash. So to get good business loans or auto loan would be a proper way out.
Add a comment: