XPath in JavaScript
There are 10 different result types, each represented by a constant on the
The simplest results return a single value (Boolean, Node, Number, and String) while the more complex ones return multiple nodes. When called,
This object’s properties contain the result of the evaluation. There is a property for each type of simple result:
To perform an XPath query, you’ll need to use an
You can either create a new instance or use a built-in one. Creating your own means instantiating
In Firefox, Safari, Chrome, and Opera, all instances of
If you load an XML document via
Note that you cannot use
There are two ways to return multiple nodes, via iterator or snapshot. Iterator results are still tied to the document, so any changes made will automatically be reflected in the result set.
Snapshot results, on the other hand, take the results at that point in time and are not affected by further document augmentation. Both result types require you to iterate over the results. For iterator results, you’ll need to use the
For snapshot results, you can use the
In most cases, a snapshot result is preferable to an iterator result because the connection with the document has been severed; every call to
In short, iterator results have the same performance implications as using
If you’re simply using XPath to query an HTML document, then the namespace resolver argument for
XPathResult object. They are XPathResult.ANY_TYPE– Returns the type of data appropriate for the XPath expressionXPathResult.ANY_UNORDERED_NODE_TYPE– Returns a node set of matching nodes, although the order may not match the order of the nodes within the documentXPathResult.BOOLEAN_TYPE– Returns a Boolean valueXPathResult.FIRST_ORDERED_NODE_TYPE– Returns a node set with only one node, which is the first matching node in the documentXPathResult.NUMBER_TYPE– Returns a number valueXPathResult.ORDERED_NODE_ITERATOR_TYPE– Returns a node set of matching nodes in the order in which they appear in the document. This is the most commonly used result type.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE– Returns a node set snapshot, capturing the nodes outside of the document so that any further document modification doesn't affect the result set. The nodes in the result set are in the same order as they appear in the document.XPathResult.STRING_TYPE– Returns a string valueXPathResult.UNORDERED_NODE_ITERATOR_TYPE– Returns a node set of matching nodes, although the order may not match the order of the nodes within the documentXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE– Returns a node set snapshot, capturing the nodes outside of the document so that any further document modification doesn't affect the node set. The nodes in the node set are not necessarily in the same order as they appear in the document.
evaluate() depends wholly
on the result type requested. The simplest results return a single value (Boolean, Node, Number, and String) while the more complex ones return multiple nodes. When called,
evaluate() returns an XPathResult object. This object’s properties contain the result of the evaluation. There is a property for each type of simple result:
booleanValue, singleNodeValue, numberValue, and stringValue. Additionally, there is a resultType property whose value maps to one of the XPathResult constants. This is useful in determining the type of result when using XPathResult.ANY_TYPE. If there is no matching result, evaluate() returns null.To perform an XPath query, you’ll need to use an
XPathEvaluator object. You can either create a new instance or use a built-in one. Creating your own means instantiating
XPathEvaluator (Opera only implemented this as of version 9.5):var evaluator = new XPathEvaluator();
//get first div
var result = evaluator.evaluate("//div", document.documentElement, null,
XPathResult.FIRST_ORDERED_NODE_TYPE, null);
alert("First div ID is " + result.singleNodeValue.id);
In Firefox, Safari, Chrome, and Opera, all instances of
Document also implement the XPathEvaluator interface, which means you can access document.evaluate() if you want to query the HTML page. If you load an XML document via
XMLHttpRequest or another mechanism, the evaluate() method is also available. For example:
//get first div
var result = document.evaluate("//div", document.documentElement, null,
XPathResult.FIRST_ORDERED_NODE_TYPE, null);
alert("First div ID is " + result.singleNodeValue.id);
Note that you cannot use
document.evaluate() outside of document; you can use an instance of XPathEvaluator any document.There are two ways to return multiple nodes, via iterator or snapshot. Iterator results are still tied to the document, so any changes made will automatically be reflected in the result set.
Snapshot results, on the other hand, take the results at that point in time and are not affected by further document augmentation. Both result types require you to iterate over the results. For iterator results, you’ll need to use the
iterateNext() method, which will either return a node or null (this works for both ordered and unordered iterator results)://get all divs - iterator style
var result = document.evaluate("//div", document.documentElement, null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
if (result){
var node = result.iterateNext();
while(node) {
alert(node.id);
node = node.iterateNext();
}
}
For snapshot results, you can use the
snapshotLength property to determine how many results were returned and the snapshotItem() method to retrieve a result in a specific position. Example (this works for both ordered and unordered snapshot results)://get all divs - iterator style
var result = document.evaluate("//div", document.documentElement, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (result){
for (var i=0, len=result.snapshotLength; i < len; i++) {
alert(result.snapshotItem(i).id);
}
}
In most cases, a snapshot result is preferable to an iterator result because the connection with the document has been severed; every call to
iterateNext() re-executes the XPath query on the document
and so is much slower. In short, iterator results have the same performance implications as using
HTMLCollection objects, which also query the document repeatedly.If you’re simply using XPath to query an HTML document, then the namespace resolver argument for
evaluate() will always be null;
if you intend to use XPath on an XML document containing namespaces,
then you’ll need to learn how to create and use namespace resolvers.
Every namespace URI is mapped to a specific prefix defined in the XML
document with the exception of the default namespace, which doesn’t
require a prefix. A namespace resolver performs the mapping between
namespace prefix and namespace URI for the XPath engine. There are two
ways to create namespace resolvers. The first is to create a function
that accepts the namespace prefix as an argument and returns the
appropriate URI. For example:
This approach may work if you already have the prefixes and namespace URIs handy. When the default namespace is going to be resolved, an empty string is passed into the function.
The second approach is to create a namespace resolver using a node that contains namespace information, such as:
The
This approach is more useful when the namespace information is embedded in the XML document, in which case it doesn’t make sense to duplicate that information and too tightly couple the JavaScript to the XML document.
Using either approach, you can easily evaluate XPath expressions on XML documents that have namespaces:
Once again, this information is valid for Firefox, Safari, Chrome, and Opera; Internet Explorer does not natively support DOM Level 3 XPath. It does remain an option in other browsers, though, for super fast DOM querying.
Both comments and pings are currently closed.
function resolver(prefix){
switch(prefix){
case "wrox": return "http://www.wrox.com/";
case "ncz": return "http://www.nczonline.net/";
default: return "http://www.yahoo.com/";
}
}
This approach may work if you already have the prefixes and namespace URIs handy. When the default namespace is going to be resolved, an empty string is passed into the function.
The second approach is to create a namespace resolver using a node that contains namespace information, such as:
<books xmlns:wrox="http://www.wrox.com/" xmlns="http://www.amazon.com/">
<wrox:book>Professional JavaScript</book>
</books>
The
<books> element contains all of the namespace information for this XML snippet. You can pass a reference to this node into the XPathEvaluator object’s createNSResolver() method and get a namespace resolver automatically created:var evaluator = new XPathEvaluator();
var resolver = evaluator.createNSResolver(xmldoc.documentElement);
This approach is more useful when the namespace information is embedded in the XML document, in which case it doesn’t make sense to duplicate that information and too tightly couple the JavaScript to the XML document.
Using either approach, you can easily evaluate XPath expressions on XML documents that have namespaces:
var evaluator = new XPathEvaluator();
var resolver = evaluator.createNSResolver(xmldoc.documentElement);
var result = evaluator.evaluate("wrox:book", xmldoc.documentElement,
resolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (result){
alert(result.singleNodeValue.firstChild.nodeValue);
}
If you don’t provide a namespace resolver when an XPath query is run
against a document that uses namespaces, then an error will occur.Once again, this information is valid for Firefox, Safari, Chrome, and Opera; Internet Explorer does not natively support DOM Level 3 XPath. It does remain an option in other browsers, though, for super fast DOM querying.
Disclaimer: Any viewpoints
and opinions expressed in this article are those of Nicholas C. Zakas
and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
Both comments and pings are currently closed.
Nessun commento:
Posta un commento