skip to Main Content

I’m using AppleScript to control Safari. Now I need to execute a Javascript in Safari using AppleScript’s do javascript to evaluate an Xpath and use the return values in the AppleScript

This works

$x('//div/div/a').map(link => link.href)

But when I try something like this

document.evaluate(('//div/div/a').map(link => link.href), document, null, 0, null)

I get

TypeError: ('//div/div/a').map is not a function. (In '('//div/div/a').map(link => link.href)', '('//div/div/a').map' is undefined)

According to Google this error message means that I try to use map on non-array object. I think I understand what this means – (‘//div/div/a’) has not been evaluated before the .map(link => link.href) is executed but I can’t figure out how to express this statement so that map can be applied on an array.

If I add parenthesis like this

(document.evaluate(('//div/div/a')).map(link => link.href), document, null, 0, null)

document.evaluate don’t get enough arguments. I also tried

(document.evaluate(('//div/div/a'), document, null, 0, null))).map(link => link.href) thinking that document.evaluate would return an array but that got me the same error message as above map is not a function.

I’m not sure document.evaluate even gives a meaningful result:

document.evaluate(('//div/div/a'), document, null, XPathResult.ANY_TYPE, null)

XPathResult {resultType: 4, invalidIteratorState: false, iterateNext: function, snapshotItem: function, ANY_TYPE: 0, …} = $3

Isn’t this basically an empty result? That is, not an array?

How can I use ‘//div/div/a’).map(link => link.href in document.evaluate or similar function so I get an array-like datastructure as the result?


Update: I found this https://codereview.stackexchange.com/questions/167571/evaluating-an-xpath-with-document-evaluate-to-get-an-array-of-nodes and it works on a superficial level – but then I am back at my original problem – for some reason I can’t get the href-attribute using an xpath – to which the only solution seems to be to use .map(link => link.href)

2

Answers


  1. On XPathResult use iterateNext() method

    arr = document.evaluate(('//div/div/a'), document, null, XPathResult.ANY_TYPE, null);
    // XPathResult { resultType: 4, invalidIteratorState: false }
    
    a = arr.iterateNext()
    // <a class="s-avatar s-avatar__32 s-user-card--avatar" href="/users/2834978/lmc">
    
    a.getAttribute("href")
    // "/users/2834978/lmc"
    
    Login or Signup to reply.
  2. I navigated to https://www.facebook.com/zuck/followers in Firefox, opened the JS console, and ran this code which is identical to the one in the page you linked to in your update, above, but with the XPath changed:

    var result = document.evaluate("//a[starts-with(@href, 'https://www.facebook.com/profile')]/@href", document, null, XPathResult.ANY_TYPE, null);
    
    var node = result.iterateNext(); 
    var nodes = [];
    while (node) {
      nodes.push(node);
      node = result.iterateNext();
    }
    console.log(nodes);
    

    The result was an array of href attributes containing the URLs of the profile pages of Zuckerberg’s followers.

    Array(24) [ 
    href="https://www.facebook.com/profile.php?id=100085151235324", 
    href="https://www.facebook.com/profile.php?id=100085151235324", 
    href="https://www.facebook.com/profile.php?id=100040676620405", 
    href="https://www.facebook.com/profile.php?id=100040676620405", 
    href="https://www.facebook.com/profile.php?id=100016890221427", 
    href="https://www.facebook.com/profile.php?id=100016890221427", 
    href="https://www.facebook.com/profile.php?id=100087501701895", 
    href="https://www.facebook.com/profile.php?id=100087501701895", 
    href="https://www.facebook.com/profile.php?id=100004876967937", 
    href="https://www.facebook.com/profile.php?id=100004876967937",
     … ]
    

    Does that work for you in your browser? If not, does it work for you with a different browser?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search