463

I am looking for something like:

getElementByXpath(//html[1]/body[1]/div[1]).innerHTML 

I need to get the innerHTML of elements using JS (to use that in Selenium WebDriver/Java, since WebDriver can't find it itself), but how?

I could use ID attribute, but not all elements have ID attribute.

1
  • 2
    Incidentally, the html and body selectors are superfluous since a DIV must be a descendent of BODY (immediate or deeper) and BODY must be a child of HTML, so provided there are no other DIV elements in the document, //DIV[1] should work (though I'm pretty rusty on XPath expressions). The DOM equivalent is document.getElementsByTagName('div')[1] (or maybe 0).
    – RobG
    CommentedMay 15, 2012 at 10:29

13 Answers 13

744

You can use document.evaluate:

Evaluates an XPath expression string and returns a result of the specified type if possible.

It is w3-standardized and whole documented: https://developer.mozilla.org/en-US/docs/Web/API/Document.evaluate

function getElementByXpath(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } console.log( getElementByXpath("//html[1]/body[1]/div[1]") );
<div>foo</div>

https://gist.github.com/yckart/6351935

There's also a great introduction on mozilla developer network: https://developer.mozilla.org/en-US/docs/Introduction_to_using_XPath_in_JavaScript#document.evaluate


Alternative version, using XPathEvaluator:

function getElementByXPath(xpath) { return new XPathEvaluator() .createExpression(xpath) .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE) .singleNodeValue } console.log( getElementByXPath("//html[1]/body[1]/div[1]") );
<div>foo/bar</div>

11
329

In Chrome Dev Tools you can run the following:

$x("some xpath") 
7
  • 2
    I've played around with this and it seems to work, but is there any documentation for this feature? I didn't find anything.
    – Eric
    CommentedJan 30, 2017 at 19:40
  • 13
    This needs to be higher up. Firefox supports it, too.
    – iSWORD
    CommentedNov 20, 2019 at 12:37
  • 1
    @Eric Documentation: developers.google.com/web/tools/chrome-devtools/console/…
    – rybo111
    CommentedMay 7, 2020 at 12:58
  • Indeed. But Selenium does not find everything that works with $x("").
    – bomben
    CommentedDec 1, 2020 at 7:39
  • 12
    $x wont work within script, it for console only. The accepted answer works within scripts running in browser context.CommentedJun 10, 2021 at 16:06
37

To identify a WebElement using and you have to use the evaluate() method which evaluates an xpath expression and returns a result.


document.evaluate()

document.evaluate() returns an XPathResult based on an XPath expression and other given parameters.

The syntax is:

var xpathResult = document.evaluate( xpathExpression, contextNode, namespaceResolver, resultType, result ); 

Where:

  • xpathExpression: The string representing the XPath to be evaluated.
  • contextNode: Specifies the context node for the query. Common practice is to pass document as the context node.
  • namespaceResolver: The function that will be passed any namespace prefixes and should return a string representing the namespace URI associated with that prefix. It will be used to resolve prefixes within the XPath itself, so that they can be matched with the document. null is common for HTML documents or when no namespace prefixes are used.
  • resultType: An integer that corresponds to the type of result XPathResult to return using named constant properties, such as XPathResult.ANY_TYPE, of the XPathResult constructor, which correspond to integers from 0 to 9.
  • result: An existing XPathResult to use for the results. null is the most common and will create a new XPathResult

Demonstration

As an example the Search Box within the Google Home Page which can be identified uniquely using the xpath as //*[@name='q'] can also be identified using the Console by the following command:

$x("//*[@name='q']") 

Snapshot:

googlesearchbox_xpath

The same element can can also be identified using document.evaluate() and the xpath expression as follows:

document.evaluate("//*[@name='q']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 

Snapshot:

document_evalute_xpath

0
    33

    For something like $x from chrome command line api (to select multiple elements) try:

    var xpath = function(xpathToExecute){ var result = []; var nodesSnapshot = document.evaluate(xpathToExecute, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); for ( var i=0 ; i < nodesSnapshot.snapshotLength; i++ ){ result.push( nodesSnapshot.snapshotItem(i) ); } return result; } 

    This MDN overview helped: https://developer.mozilla.org/en-US/docs/Introduction_to_using_XPath_in_JavaScript

    0
      17

      Although many browsers have $x(xPath) as a console built-in, here's an aggregation of the useful-but-hardcoded snippets from Introduction to using XPath in JavaScript ready for use in scripts:

      Snapshot

      This gives a one-off snapshot of the xpath result set. Data may be stale after DOM mutations.

      const $x = xp => { const snapshot = document.evaluate( xp, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); return [...Array(snapshot.snapshotLength)] .map((_, i) => snapshot.snapshotItem(i)) ; }; console.log($x('//h2[contains(., "foo")]'));
      <h2>foo</h2> <h2>foobar</h2> <h2>bar</h2>

      First ordered node

      const $xOne = xp => document.evaluate( xp, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue ; console.log($xOne('//h2[contains(., "foo")]'));
      <h2>foo</h2> <h2>foobar</h2> <h2>bar</h2>

      Iterator

      Note however, that if the document is mutated (the document tree is modified) between iterations that will invalidate the iteration and the invalidIteratorState property of XPathResult is set to true, and a NS_ERROR_DOM_INVALID_STATE_ERR exception is thrown.

      function *$xIter(xp) { const iter = document.evaluate( xp, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); for (;;) { const node = iter.iterateNext(); if (!node) { break; } yield node; } } // dump to array console.log([...$xIter('//h2[contains(., "foo")]')]); // return next item from generator const xpGen = $xIter('//h2[text()="foo"]'); console.log(xpGen.next().value);
      <h2>foo</h2> <h2>foobar</h2> <h2>bar</h2>

        11

        You can use javascript's document.evaluate to run an XPath expression on the DOM. I think it's supported in one way or another in browsers back to IE 6.

        MDN: https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate

        IE supports selectNodes instead.

        MSDN: https://msdn.microsoft.com/en-us/library/ms754523(v=vs.85).aspx

        1
        • 8
          I'd like to note that {document.evaluate} does not work in IE.CommentedJan 30, 2013 at 22:30
        7

        To direct to the point, you can easily use xpath. The exact and simple way to do this using the below code. Kindly try and provide feedback. Thank you.

        JavascriptExecutor js = (JavascriptExecutor) driver; //To click an element WebElement element=driver.findElement(By.xpath(Xpath)); js.executeScript(("arguments[0].click();", element); //To gettext String theTextIWant = (String) js.executeScript("return arguments[0].value;",driver.findElement(By.xpath("//input[@id='display-name']"))); 

        Further readings - https://medium.com/@smeesheady/webdriver-javascriptexecutor-interact-with-elements-and-open-and-handle-multiple-tabs-and-get-url-dcfda49bfa0f

        5
        • I believe the OP didn't ask for a Selenium solution like yours, correct?
          – ankostis
          CommentedMay 8, 2020 at 12:15
        • @ankostis It mentions "Selenium webdriver" in the title.
          – MLEN
          CommentedNov 1, 2020 at 6:52
        • @mlen the original title had no such then - it wasbadded later.
          – ankostis
          CommentedNov 2, 2020 at 10:32
        • @ankostis That's a funny way of apologizing for a snarky comment that was wrong.
          – Andrewcpu
          CommentedJan 27, 2023 at 23:46
        • The original post body had Selenium mentioned, so it's always been a part of the question. Titles tend to miss important details that eventually get hoisted from the body to improve SEO.
          – ggorlen
          CommentedMay 25, 2023 at 22:26
        2

        Assuming your objective is to develop and test your xpath queries for screen maps. Then either use Chrome's developer tools. This allows you to run the xpath query to show the matches. Or in Firefox >9 you can do the same thing with the Web Developer Tools console. In earlier version use x-path-finder or Firebug.

          2
          public class JSElementLocator { @Test public void locateElement() throws InterruptedException{ WebDriver driver = WebDriverProducerFactory.getWebDriver("firefox"); driver.get("https://www.google.co.in/"); WebElement searchbox = null; Thread.sleep(1000); searchbox = (WebElement) (((JavascriptExecutor) driver).executeScript("return document.getElementById('lst-ib');", searchbox)); searchbox.sendKeys("hello"); } } 

          Make sure you are using the right locator for it.

          1
          • 1
            Hi Prerit, the question was to select by an element based on its xpath. The solution you have provided is to select it by the id. :)CommentedJun 8, 2018 at 18:56
          2
          **Different way to Find Element:** IEDriver.findElement(By.id("id")); IEDriver.findElement(By.linkText("linkText")); IEDriver.findElement(By.xpath("xpath")); IEDriver.findElement(By.xpath(".//*[@id='id']")); IEDriver.findElement(By.xpath("//button[contains(.,'button name')]")); IEDriver.findElement(By.xpath("//a[contains(.,'text name')]")); IEDriver.findElement(By.xpath("//label[contains(.,'label name')]")); IEDriver.findElement(By.xpath("//*[contains(text(), 'your text')]"); Check Case Sensitive: IEDriver.findElement(By.xpath("//*[contains(lower-case(text()),'your text')]"); For exact match: IEDriver.findElement(By.xpath("//button[text()='your text']"); **Find NG-Element:** Xpath == //td[contains(@ng-show,'childsegment.AddLocation')] CssSelector == .sprite.icon-cancel 
            1
            import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; System.setProperty("webdriver.chrome.driver", "path of your chrome exe"); WebDriver driver = new ChromeDriver(); driver.manage().window().maximize(); driver.get("https://www.google.com"); driver.findElement(By.xpath(".//*[@id='UserName']")).clear(); driver.findElement(By.xpath(".//*[@id='UserName']")).sendKeys(Email); 
            1
            • 1
              add a descriptive answer. your description will help questioner to understand your solution quickly.
              – NickCoder
              CommentedSep 11, 2019 at 12:18
            0

            A shorter form of document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue:

            document.evaluate(xpath, document).iterateNext() 
              0

              To return the first matched element:

              function get_element_by_xpath(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } let my_element = get_element_by_xpath('//div'); 

              To return an array of matched elements:

              function get_elements_by_xpath(path) { const nodesSnapshot = document.evaluate(path, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); return Array.from({ length: nodesSnapshot.snapshotLength }, (_, i) => nodesSnapshot.snapshotItem(i)); } let my_elements = get_elements_by_xpath('//div'); 

                Start asking to get answers

                Find the answer to your question by asking.

                Ask question

                Explore related questions

                See similar questions with these tags.