Yes, that was a bit too terse, even for my standards. Here’s what I wanted to say.
Suppose you want to download the first image on a page that’s currently loaded in the topmost Safari window. The JavaScript code to get the image’s URL might be
const URL = document.getQuerySelector('img:nth-of-type(1)').src;
(no error-checking here, but you should add that for real-life code). This line is what I called constant
in the above paragraph.
Now, you want to get this URL into your calling script:
// THIS DOES NOT WORK YET!
(() => {
const browser = Application("Safari");
const tab = browser.windows[0].currentTab();
const browserCode =
`const URL = document.getQuerySelector('img:nth-of-type(1)').src;`;
const result = browser.doJavaScript(browserCode, {in: tab});
})();
here, browserCode
is set to a JavaScript template string, as indicated by the surrounding backticks. By using this construct, you avoid complicated quoting of single and double quotes, and you can also put JavaScript code inside the string if need be.
But this code does not work as one would want it to: When running the script, result
will be undefined
. That is because the return value of doJavaScript
is the value of the last expression executed by the browser. That is an assignment to URL
whose value is undefined
.To have the browser return the URL itself, omit the assignment:
(() => {
const browser = Application("Safari");
const tab = browser.windows[0].currentTab();
const browserCode =
`document.getQuerySelector('img:nth-of-type(1)').src;`;
const result = browser.doJavaScript(browserCode, {in: tab});
})();
Now, the last statement executed is querySelector...src
, and its value is the value of the img's src
property.
Which is a string so that the whole thing works: doJavaScript
can easily return strings and numbers to the calling code.
However, it can’t return more complicated objects like Array
. Say you want to get the URLs of all images on a page with
[...document.getQuerySelectorAll('img')].map(e => e.src)
This will give you an Array
of String
s. But in your calling code, you’ll get undefined
if you run this line with doJavaScript
. To overcome this, you can convert the result to a String
:
const arrayOfStrings = [...document.getQuerySelectorAll('img')].map(e => e.src);
JSON.stringify(arrayOfStrings);
JSON.stringify
converts its argument to a string, and since it’s the last statement in the code executed by the browser, this string will be returned to the calling script.
const browserCode = `const arrayOfStrings = [...document.getQuerySelectorAll('img')].map(e => e.src);
JSON.stringify(arrayOfStrings);`
const browser = Application("Safari");
const tab = browser.windows[0].currentTab();
const resultString= browser.doJavaScript(broserCode, {in: tab});
const resultArray = JSON.parse(resultString);
Here, JSON.parse
converts the string back into an array of strings.
I hope it’s a bit clearer now.