Using xpath to restore a DOM range in JavaScript
I'm trying to grasp the best approach to saving (serializing) and restoring (deserializing) a DOM selection or range. There is a post here on stringifying a range object, but it frankly doesn't really work.
The situation is an interactive text book using the Webkit view of Adobe Air. I have static (built-in) html content that the user can highlight and bookmark (annotate). This mechanism all works, but I need to be able to store and restore these annotations. I would rather not store a modified version of the DOM, but rather use the static version then reapply the users annotations that I am storing in a SQLite DB along with other needed metadata. DOM manipulation is pretty new for me, and so far my attempts to serialize a DOM::range have failed. What I realized though is that I really seem to only need the start and end containers and the start and end offsets. Then I can recreate the range with document.createRange().
What I could use guidance on is the best approach to serializing the start and end containers. My first thought was xpath, but so far my attempts have come up short. Looking at the Mozilla docs开发者_运维问答 for DOM::Range seems pretty straight forward, but creating a reliable xpath to restore a range isn't quite clicking for me.
HTML
<div>
<div id="container" style="background-color: red;">
<p id="paraText">Text</p>
</div>
</div>
JavaScript
function serialize(node) {
if (typeof XMLSerializer != "undefined") { // Firefox, etc.
return (new XMLSerializer()).serializeToString(node);
}
else if (node.xml) { // IE
return node.xml;
}
};
function parseXMLString(xml) {
if (typeof DOMParser != "undefined") { // Firefox, etc.
var dp = new DOMParser();
return dp.parseFromString(xml, "application/xml");
}
else if (typeof ActiveXObject != "undefined") { // IE
var doc = XML.newDocument();
doc.loadXML(xml);
return doc;
}
};
var contextNode = document.getElementById('container');
var xmlString = serialize(contextNode);
var doc = parseXMLString(xmlString);
// Get elements from document using XPath
var xpathResult = doc.evaluate('//.', doc.firstChild, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
// Insert elements back into document (I used replace in order to show that the document is actually changed)
contextNode.parentNode.replaceChild(xpathResult.singleNodeValue.firstChild, contextNode);
As it happens, I have written a solution for this for my Rangy library. The Serializer module uses XPath-like syntax to serialize and deserialize ranges and selections (demo).
I've also written a highlighter module that isn't yet released publicly. I'll tidy it and put up a demo shortly.
UPDATE
Highlighter demo is now available. After some more testing, it will be released in the next version of Rangy.
One major problem with Rangy library's Serialiazer module is that it is not crossbrowser consistent such as the selections stored on one browser cannot be restored on a different family of browser (Gecko & IE)
精彩评论