4

I am a beginner to Xpath and was wondering if there is any way to get the xpath of an element in javascript/jquery. I need an absolute way to identify an element and I knw Xpath is used for this,but can't figure how.

The scenario is that I have a jquery reference of an element. I want its xpath to store in a database on mouse click. How do I get the Xpath of an HTML Element once I have a jquery reference. I need to be able to translate the Xpath into an absolute element later

function clickTrack(event){
offset=event.pageX;
var xpath=getXpath(this);//I need the xpath here
data={'xpath':xpath,'offset':offset};

}
SoWhat
  • 5,564
  • 2
  • 28
  • 59

4 Answers4

8

You can extract this functionality from an XPath tool I once wrote:

http://webkitchen.cz/lab/opera/xpath-tool/xpath-tool.js


Edit: here you go:

function getXPath(node) {
    var comp, comps = [];
    var parent = null;
    var xpath = '';
    var getPos = function(node) {
        var position = 1, curNode;
        if (node.nodeType == Node.ATTRIBUTE_NODE) {
            return null;
        }
        for (curNode = node.previousSibling; curNode; curNode = curNode.previousSibling) {
            if (curNode.nodeName == node.nodeName) {
                ++position;
            }
        }
        return position;
     }

    if (node instanceof Document) {
        return '/';
    }

    for (; node && !(node instanceof Document); node = node.nodeType == Node.ATTRIBUTE_NODE ? node.ownerElement : node.parentNode) {
        comp = comps[comps.length] = {};
        switch (node.nodeType) {
            case Node.TEXT_NODE:
                comp.name = 'text()';
                break;
            case Node.ATTRIBUTE_NODE:
                comp.name = '@' + node.nodeName;
                break;
            case Node.PROCESSING_INSTRUCTION_NODE:
                comp.name = 'processing-instruction()';
                break;
            case Node.COMMENT_NODE:
                comp.name = 'comment()';
                break;
            case Node.ELEMENT_NODE:
                comp.name = node.nodeName;
                break;
        }
        comp.position = getPos(node);
    }

    for (var i = comps.length - 1; i >= 0; i--) {
        comp = comps[i];
        xpath += '/' + comp.name;
        if (comp.position != null) {
            xpath += '[' + comp.position + ']';
        }
    }

    return xpath;

}

It might need some changes if you want it to work in IE as well.

Jakub Roztocil
  • 15,930
  • 5
  • 50
  • 52
3

Well you can work it out with a Github library at https://github.com/bermi/element-xpath

  1. Include the script

      <script src="https://raw.github.com/bermi/element-xpath/master/element-xpath.min.js" type="text/javascript"></script>
    
    1. Use this code

       document.body.addEventListener("click", function (ev) {
           console.log(ev);
           console.log(ev.toElement);
      
           var xpath = getElementXpath(ev.toElement);
           console.log(xpath);
      });
      
Vivek Kumar
  • 2,625
  • 2
  • 25
  • 33
3

There is no such thing as "the" XPath for an element.

When people ask this question they usually want one of three things:

(a) the names of the elements in the ancestry of the element, for example /a/b/c/d

(b) as (a) but with positional information added, for example /a[2]/b[3]/c[1]/d[4]. (A variant is to want the positional information only where it's not redudant)

(c) as (b) but with no namespace dependencies, for example /[namespace-uri()='x' and local-name()='y'][1]/[namespace-uri()='x' and local-name='z'][5]/...

All three are easy enough to construct with a simple recursive function in whatever language takes your fancy.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
1

Need more info, but it's easy if you have an id for the element. If for instance your jquery reference is grabbing a div with id="myDiv", a suitable xpath would be

//div[@id="myDiv"]

Here's an xpath tutorial: http://www.tizag.com/xmlTutorial/xpathtutorial.php

And a good ref: https://developer.mozilla.org/en/XPath

EDIT: I see you're wanting absolute xpath given only the node. In that case, I believe you'll have to build the xpath by walking the DOM tree backwards using parentNode (reference here). You'll also need to do some work to check which child number each node is based on tag name. Remember also, in xpath, indices start with 1, not 0, per the spec (reference here).

EDIT #2: See getPathTo() in this post. It doesn't go all the way up the tree, but it gets the position relative to siblings. Also, there's an unaccepted answer with a full-blown attempt at the absolute path here.

Community
  • 1
  • 1
Jonathan M
  • 17,145
  • 9
  • 58
  • 91
  • I want to get the absolute xpath. This will run on an unknown page. So I need the xpath right from body. Is there anyway of getting this. Hence I cannot rely on id – SoWhat Feb 08 '12 at 17:04
  • I don't have an id. Because I don't know whether it will have an id. If every element had an id, I wouldn't need to store the xpath to uniquely identify the element. I'd just need the id – SoWhat Feb 08 '12 at 17:07
  • Hmmm. Tell us a bit more about your application in the main post. Tell us why you're wanting the absolute path. Having the absolute path can be bad if you don't have control of the page and the page changes (such as adding/deleting an element). You're not using an absolute path in jQuery are you? Why do so in xpath? – Jonathan M Feb 08 '12 at 17:08
  • k I need to identify an element uniquely. Basically, i am making an A/B optimizer. So when there is a click on an element, I need the Xpath of the element to identify it and store where on that element the click was made – SoWhat Feb 08 '12 at 17:12
  • Is there another way of doing to do this. Since I don't have any control over where the click was made, I don't knw if it has an id or even a class – SoWhat Feb 08 '12 at 17:12
  • ah. I was trying to avoid doing that – SoWhat Feb 08 '12 at 17:25
  • 1
    Of course, you could develop the code as a jQuery add-on and receive the love and adulation of your peers for sharing your hard work. :) – Jonathan M Feb 08 '12 at 17:26