0

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)

d-b
  • 695
  • 3
  • 14
  • 43
  • `document.evaluate` returns an `XPathResult`, not an Array. https://developer.mozilla.org/en-US/docs/Web/API/XPathResult – Conal Tuohy Apr 02 '23 at 01:08
  • please show a reproducible example of the code you say is working "on a superficial level" and the code which you expect to work and doesn't work – Conal Tuohy Apr 02 '23 at 01:31
  • @ConalTuohy I use the exact code from the linked codereview-question, just changing the xpath. The original problem is that I fail to write an xpath that extracts an html-attribute. The solution to the original problem was to add .map(link => link.href) but that doesn't work with document.evaluate. Do you have any ideas why I have this original problem? – d-b Apr 02 '23 at 07:31
  • I'm not sure I understand what the original problem was because you hadn't posted a reproducible example, just the XPath. For me, and others, the XPath worked. I know that there are some XPath APIs that let you execute an XPath expression but will only return element nodes, but the standard `document.evaluate` is supposed to be able to return attributes. You say you used the exact code from the linked example but "changing the xpath" but you don't say what you changed the XPath to. Did you have `/@href` at the end? Post a reproducible example, is my recommendation. – Conal Tuohy Apr 02 '23 at 12:15
  • In my comment above I told you why `.map(link => link.href)` doesn't work with `document.evaluate`; it's because `map` is a method of an Array, but `document.evaluate` doesn't return an Array; it returns an `XPathResult`. I will post an answer. – Conal Tuohy Apr 02 '23 at 12:22
  • @ConalTuohy You misunderstood me. `.map(link => link.href)` solved my original problem - "the original problem is that I fail to write an xpath that extracts an html-attribute" - see this question https://stackoverflow.com/questions/75910824/alternative-ways-of-getting-xpath-attributes-safari-doesnt-support-must-wo/75911476 , especially the "update"-part of it. – d-b Apr 03 '23 at 10:14

2 Answers2

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"
LMC
  • 10,453
  • 2
  • 27
  • 52
0

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?

Conal Tuohy
  • 2,561
  • 1
  • 8
  • 15
  • It seems Safari has a bug/behaves differently, see https://stackoverflow.com/questions/75910824/alternative-ways-of-getting-xpath-attributes-safari-doesnt-support-must-wo/75911476 - all attributes but href works as expected. Very odd. – d-b Apr 02 '23 at 15:36