getting the xpath of a domNode element in javascript (within Air’s htmlComponent)
March 8, 2011 – 9:22 pmI wanted to store references to certain html nodes and there info in a html document that was loaded in air's htmlcomponent.
It's reaonably easy to inject javascript into any loaded document.
The function I came up with is adapted from another example that I found online, but it is improved so that it takes care of duplicate id's if any are found.
So here al little piece of JavaScript that will get you the xpath of a node in an html doument and a little helder function to check the validity of the generated xpath.
function createXPathFromElement(elm) {
var allNodes = document.getElementsByTagName('*');
for (segs = []; elm && elm.nodeType == 1; elm = elm.parentNode)
{
if (elm.hasAttribute('id')) {
var uniqueIdCount = 0;
for (var n=0;n < allNodes.length;n++) {
if (allNodes[n].hasAttribute('id') && allNodes[n].id == elm.id) uniqueIdCount++;
if (uniqueIdCount > 1) break;
};
if ( uniqueIdCount == 1) {
segs.unshift('id("' + elm.getAttribute('id') + '")');
return segs.join('/');
} else {
segs.unshift(elm.localName.toLowerCase() + '[@id="' + elm.getAttribute('id') + '"]');
}
} else if (elm.hasAttribute('class')) {
segs.unshift(elm.localName.toLowerCase() + '[@class="' + elm.getAttribute('class') + '"]');
} else {
for (i = 1, sib = elm.previousSibling; sib; sib = sib.previousSibling) {
if (sib.localName == elm.localName) i++; };
segs.unshift(elm.localName.toLowerCase() + '[' + i + ']');
};
};
return segs.length ? '/' + segs.join('/') : null;
};
function lookupElementByXPath(path) {
var evaluator = new XPathEvaluator();
var result = evaluator.evaluate(path, document.documentElement, null,XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return result.singleNodeValue;
}
in Air's htmlcomponent I wnated to have the javascript in the code without having to load an exteral js file (not tested) and maybe risking security sandbox errors.
So In the onComplete handler of the htmlComponent I did this to inject the extra javascript in the document: (Make sure to carefully escape double quotes \" and \>\< signs !)
Again the same code, but now inline:
handling the htmlComponent listener: this.addEventListener(Event.COMPLETE, onComplete);
private function onComplete(e:Event):void
{
var container:Object = this.window.document.createElement('div');
this.window.onunload = cleanup;
container.id = 'scriptContainer';
this.window.document.body.appendChild(container);
var script:Object = this.window.document.createElement("script");
script.type = 'text/javascript';
script.innerHTML = " function createXPathFromElement(elm) { \n"
+" var allNodes = document.getElementsByTagName('\*'); \n"
+" for (segs = []; elm && elm.nodeType == 1; elm = elm.parentNode) \n"
+" { \n"
+" if (elm.hasAttribute('id')) { \n"
+" var uniqueIdCount = 0; \n"
+" for (var n=0;n \< allNodes.length;n++) { \n"
+" if (allNodes[n].hasAttribute('id') && allNodes[n].id == elm.id) uniqueIdCount++; \n"
+" if (uniqueIdCount \> 1) break; \n"
+" }; \n"
+" if ( uniqueIdCount == 1) { \n"
+" segs.unshift('id(\"' + elm.getAttribute('id') + '\")'); \n"
+" return segs.join('/'); \n"
+" } else { \n"
+" segs.unshift(elm.localName.toLowerCase() + '[@id=\"' + elm.getAttribute('id') + '\"]'); \n"
+" } \n"
+" } else if (elm.hasAttribute('class')) { \n"
+" segs.unshift(elm.localName.toLowerCase() + '[@class=\"' + elm.getAttribute('class') + '\"]'); \n"
+" } else { \n"
+" for (i = 1, sib = elm.previousSibling; sib; sib = sib.previousSibling) { \n"
+" if (sib.localName == elm.localName) i++; }; \n"
+" segs.unshift(elm.localName.toLowerCase() + '[' + i + ']'); \n"
+" }; \n"
+" }; \n"
+" return segs.length ? '/' + segs.join('/') : null; \n"
+" }; \n"
+" \n"
+" function lookupElementByXPath(path) { \n"
+" var evaluator = new XPathEvaluator(); \n"
+" var result = evaluator.evaluate(path, document.documentElement, null,XPathResult.FIRST_ORDERED_NODE_TYPE, null); \n"
+" return result.singleNodeValue; \n"
+" } \n";
container.appendChild(script);
}
Within the htmlComponent (AS3) you can add code to listen for javascript mouse events and trace / store the xpath like this:
public function onClick(el:Object):String
{
var targ:Object = el.srcElement || el.target;
return this.window.createXPathFromElement(targ);
}









