XPath 1.0 in JavaScript
Here is an implementation of XPath 1.0 in JavaScript. It’s not completely tested but it seems to work with the few tests I’ve been doing. It is released under a Creative Commons Licence. Please let me know if there are any bugs.
There is a mailing list for discussing the library. You can subscribe by going to the list information page. The archives are also available.
Download: xpath.js
A simple example of how to use it:
// Create a new parser object
var parser = new XPathParser();
// Parse the XPath expression
var xpath = parser.parse("count(//svg:rect)");
// Create a context for the XPath to be evaluated in
var context = new XPathContext();
context.expressionContextNode = document.documentElement;
// Evaluate the XPath expression
var result = xpath.evaluate(context);
// Display the result
window.alert("There " + (result == 1 ? "is " : "are ") + result
+ "SVG rect elements in the document.");
The XPathContext constructor takes three arguments: a VariableResolver, a NamespaceResolver and a FunctionResolver. If these are not specified then default objects will be used. The default VariableResolver will not resolve any variables. The default NamespaceResolver will resolve namespaces based on the expression’s context node. The default FunctionResolver resolves only the core XPath functions.
Here is an example using custom resolvers:
// MyVariableResolver
MyVariableResolver.prototype = new VariableResolver();
MyVariableResolver.prototype.constructor = MyVariableResolver;
MyVariableResolver.superclass = VariableResolver.prototype;
function MyVariableResolver() {
}
MyVariableResolver.prototype.getVariableWithName = function(ns, ln, c) {
if (ns == null && ln == "doc") {
// Make the $doc variable return a nodeset containing the
// document element.
var ns = new XNodeSet();
ns.add(c.contextNode.ownerDocument.documentElement);
return ns;
}
return null;
};
// MyNamespaceResolver
MyNamespaceResolver.prototype = new NamespaceResolver();
MyNamespaceResolver.prototype.constructor = MyNamespaceResolver;
MyNamespaceResolver.superclass = NamespaceResolver.prototype;
function MyNamespaceResolver() {
}
MyNamespaceResolver.prototype.getNamespace = function(prefix, n) {
// Always resolve the prefix "ex".
if (prefix == "ex") {
return "http://example.org/functions";
}
return this.superclass.getNamespace(prefix, n);
};
// Extension function
var doubleString = function() {
// Extension functions always have the XPathContext as the first
// argument.
var c = arguments[0];
if (arguments.length != 2) {
throw new Error("Function ex:double-string expects (string)");
}
// We take the first real argument to the extension function,
// evaluate it, and get its string value.
var s = arguments[1].evaluate(c).stringValue();
// Then we return a new string object for the doubled string.
return new XString(s + s);
};
var varRes = new MyVariableResolver();
var nsRes = new MyNamespaceResolver();
var funRes = new FunctionResolver();
funRes.addFunction("http://example.org/functions",
"double-string", doubleString);
var parser = new XPathParser();
var context = new XPathContext(varRes, nsRes, funRes);
var xpath = new XPath("ex:double-string($doc/@width)");
var result = xpath.evaluate(context);
Evaluating an XPath expression will return an object of class XBoolean, XNumber, XString or XNodeSet. All four of these classes have methods called booleanValue, numberValue and stringValue to convert them to JavaScript types. The XNodeSet class has a toArray method which will return an array containing the DOM nodes in that nodeset.
Because the XPath data model treats adjacent text nodes and CDATA sections as a single text node, expressions working on documents which have such adjacent text nodes may not evaluate correctly. There is a utility function included to coalesce all adjacent text nodes in a document to avoid this problem.
Utilities.coalesceText(document);
Since revision 18, the script includes DOM 3 XPath support. Including the script will attempt to install the XPathEvaluator functions on the current document (the one named by the global ‘document’ variable), if XPath support is not already available. This is not guaranteed to work in all DOM implementations, since the document object is a host object, and host objects need not store any extra properties. However, it works at least in Internet Explorer, Opera and Mozilla (though Mozilla already has DOM 3 XPath support).
The DOM 3 XPath support can be installed on any document object, with a call to the installDOM3XPathSupport function:
installDOM3XPathSupport(myDoc, new XPathParser());
The function must be given an XPathParser object that will be used for all XPath parsing.
7 April 2008, 1:04am
I am trying to do very basic navigation in an XML file in Javascript. Do I need a such parser or should the msxml4.dll be enough?
What I am trying to do is reach nodes in the XML using selectNodes(“//tag”);
This succeeds with msxml4
Then I try to navigate back up the XML tree using
selectSingleNode(“ancestor”);
which does not work.
I am not sure if I am doing something wrong or if I actually need an additional parser such a yours.
I’d appreciate any help.
30 April 2008, 5:18pm
Meir (sorry for the delay in replying), it depends if you want to be able to navigate the document using XPath in UAs other than IE. If not, then using msxml is fine. If so, then you’ll need to do things differently depending on which UA your page is running on.
Note that nowadays, the three major non-IE browsers support DOM Level 3 XPath, and in IE you can use the msxml functions, so the need for a library such as mine is probably diminished a bit.
The specific problem you’re encountering though is that you need to use
selectSingleNode("ancestor::*")to select the parent node.15 December 2009, 9:05pm
Does it have support for name spaces, especially on Opera and Chrome?
Best regards
17 December 2009, 9:42am
It does have support for namespaces; see the use of NamespaceResolver in the examples above.
To be honest, I haven’t tested this code lately so I don’t know if it works in Opera and Chrome.
12 March 2010, 6:20pm
it is Awesome!!
22 September 2010, 7:51am
I have been using this library successfully for a long time and for the first time I am now dealing with an XML document with namespaces declared.
I have tried to follow the simple examples here but I simply don’t understand how to do it. Are there any other simple examples out there I can refer to?
22 September 2010, 9:57am
Ed: I don’t know of other examples out there. The namespace resolver object you supply must have a
getNamespace(prefix, node)function on it, whereprefixis the namespace prefix to resolve andnodeis a context element node, IIRC. From this function, return the namespace URI the prefix maps to.25 April 2011, 3:36am
it is great. thanks for your hard work.
and there is a type error at line 4180.
case XPathResult.FIRST_UNORDERED_NODE_TYPE:
26 April 2011, 12:41pm
Thanks XY, fixed now.
4 November 2011, 8:42am
Hi Cameron,
I am writing to you to let you know that I am using your xpath.js library in a GWT SVG library of mine and it is working very well. Many thanks for this fine work.
If you are interested, here are a few links to pages where I mention your work:
http://www.vectomatic.org/lib-gwt-svg
http://www.vectomatic.org/lib-gwt-svg/availability-of-lib-gwt-svg-0-5-4-and-other-announcements
as well as SVG samples / games which use your code:
http://www.vectomatic.org/gwt/lib-gwt-svg-samples/lib-gwt-svg-samples.html (see the xpath sample)
http://www.vectomatic.org/gwt/lib-gwt-svg-edu/lib-gwt-svg-edu.html
Many thanks,
Lukas
4 November 2011, 9:35am
Thanks Lukas!
28 December 2011, 7:17am
Hi Cameron,
For completeness, here are some other javascript-based XPath libraries I cam across while evaluating your library:
XPathJS – has namespace support
https://github.com/andrejpavlovic/xpathjs
JavaScript-XPath – fastest
http://coderepos.org/share/wiki/JavaScript-XPath
Llama’s XPath.js – smallest
http://llamalab.com/js/xpath/
Google AJAXSLT – oldest
http://code.google.com/p/ajaxslt/
Thanks for building this library, I’ve found it very useful for my projects.
Cheers,
Andrej
23 January 2012, 6:20am
Hi Cameron
I have found xpath.js works great on node.js. Since many node modules only work on Linux, it seems that xpath.js is invaluable when using node on windows. I think it would be great to make xpath.js a node module and upload it to github. If you do not have time I am happy to do this.
What do you think?
Thanks,
Yaron
18 May 2012, 7:46am
The following expression causes an infinite loop:
“’1′ mod/html’”
To fix, replace:
while ((c = s.charAt(pos++)) != delimiter) {
literal += c;
}
with:
while ((c = s.charAt(pos++)) != delimiter && c != “”) {
literal += c;
}
if (c == “”)
throw new Error(“Unclosed literal: ‘” + literal + “‘”);