How can I loop this code to repeat and set a different variable for every occurrence of the class 'auto-date-time' ?
tell application "Safari"
set myValue to do JavaScript "document.getElementsByClassName('auto-date-time')[0].innerHTML;" in current tab of window 1
end tell
e.g : myValue1 document.getElementsByClassName('auto-date-time')[1]
MyValue2 document.getElementsByClassName('auto-date-time')[2] MyValue3
document.getElementsByClassName('auto-date-time')[3
I know this work fine
tell application "Safari"
set myValue to do JavaScript "var outPut=[]; var arr=document.getElementsByClassName('sortable fraudScoringTransactionCCSummary');for (var i in arr) {outPut.push(arr[i].innerHTML)};outPut;" in current tab of window 1
end tell
But I'm trying to do it different, can that be done?
I tried this which is working but obviously seems pretty bad + that wouldn't fix the issue of the variable
repeat with counter from 1 to 10
tell application "Safari"
set myValue to do JavaScript "document.getElementsByClassName('auto-date-time')[" & counter & "].innerHTML;" in current tab of window 1
end tell
if myValue = missing value then
exit repeat
end if
end repeat
I would say that the cleanest and most efficient way to obtain an AppleScript list of innerHTML property values for a collection of HTML elements is this:
tell application "Safari" to tell ¬
the front document to set ¬
myList to do JavaScript ¬
"Array.from(
document.getElementsByClassName('auto-date-time'),
x => x.innerHTML
);"
EDIT (2019-04-02):
Addressing a comment below where you reported no return value, and in lieu of you providing the console return value for the JavaScript above, here's an alternative method that you can try:
tell application "Safari" to tell document 1 to set myList to ¬
do JavaScript "[...document.getElementsByClassName('auto-date-time')]
.map( x => x.innerText );"
The two differences are the use of property innerText instead of innerHTML (which you can also apply to the first JavaScript), and method used to construct the array (although, assuming you're running the latest version of Safari, shouldn't make a difference).
A note on error-catching
Try to get out of the habit of using try blocks in the manner that you tend to do. They shouldn't be used if its purpose is to prevent a script from terminating through an error that you weren't expecting to occur or don't understand why the error occurs. They are used to catch errors you predict will arise in specific situations, knowing why it happens, and allowing you to use it to your advantage. Otherwise, all you are doing is stopping an unexpected error from alerting you to a flaw in your script, and where/when it's arising.
Find it :
I need to use repeat / plus list, although pretty sure that's still not the right way to do it.
set myList to {}
try
repeat with counter from 1 to 1000
tell application "Safari"
set myValue to do JavaScript "document.getElementsByClassName('auto-date-time')[" & counter & "].innerHTML;" in current tab of window 1
end tell
if myValue = missing value then
exit repeat
else if myValue is not equal to "" then
set the myList to the myList & myValue
end if
end repeat
on error
--
end try
return myList
Related
The below script is used to click on a button to load a date on our website, then get the value of that date.
It should then return that date value, which will be used in some comparisons later on.
However, when I log doJavaStuff and emptyDate, both come back as "(*missing value *)".
The display dialog for emptyDate also returns "msng".
Why isn't the result of the do JavaScript being returned?
I've looked at multiple articles and sources and tried multiple suggestions, but nothing has worked.
e.g. I tried setting the second line of the do JavaScript to a variable, and returning that variable, but that somehow made the AppleScript variable undefined.
I'm relatively new to AppleScript, so maybe I'm missing something obvious.
global emptyDue, serviceDate, doJavaStuff
set emptyDue to " "
set doJavaStuff to " "
set serviceDate to "2021-11-30"
on isEmptyDue()
tell application "Safari" to tell document 1
set doJavaStuff to do JavaScript
"document.getElementById('trgUseNext').click(); #clicks button to load date.
document.getElementById('inspDate').value;" --gets the value of date
return doJavaStuff
end tell
end isEmptyDue
set emptyDate to isEmptyDue()
display dialog emptyDate
log doJavaStuff
log emptyDate
I was able to duplicate your problem using an element that happens to be on this StackOverFlow page (post-id), using this simplified version of your script:
on isEmptyDue()
tell application "Safari" to tell document 1
set doJavaStuff to do JavaScript
"document.getElementById('post-id').value"
return doJavaStuff
end tell
end isEmptyDue
set emptyDate to isEmptyDue()
display dialog emptyDate
The return value is, as you noted, “msng”.
The problem appears to be that AppleScript is perfectly happy to have nothing but quoted text on its own line, and is also perfectly happy to run do JavaScript with no script. That is, the code as you’ve presented it doesn’t run any JavaScript: it calls do JavaScript with nothing, and then the next line is a string of text that AppleScript ignores.
If I combine the lines so that they are one line instead of two, display dialog emptyDate displays the expected 8-digit value of element post-id on this StackOverFlow page (found by viewing the source).
on isEmptyDue()
tell application "Safari" to tell document 1
set doJavaStuff to do JavaScript "document.getElementById('post-id').value"
return doJavaStuff
end tell
end isEmptyDue
set emptyDate to isEmptyDue()
display dialog emptyDate
That was oddly difficult to track down because neither of the “mistakes” provided an obvious error: do JavaScript without any text is not an error—instead, it returns a cryptic string, “msng”, which I suspect but do not know stands for “missing”—and a string on its own on a line is not an error either. So it looks like it’s doing something and not working, instead of not doing anything and not working.
I’m currently using AppleScript to automate some tasks in Safari. I have below code, which is supposed to set the value of the text box ‘ownerValue’ to the item from the loop. Whenever I execute the code, nothing happens.
set countryList to {"BR", "CN"}
repeat with country in countryList
tell application "Safari"
activate
tell document 1
do JavaScript "document.getElementById('ownerValue').value = country"
end tell
end tell
end repeat
When I replace the country in the loop to the actual country value, e.g. “BR”, it then inserts the text into the text field.
tell application "Safari"
activate
tell document 1
do JavaScript "document.getElementById('ownerValue').value = ‘BR'"
end tell
end tell
It also seems that AppleScript doesn’t recognise country as an item from the loop, since country is not in green.
Any ideas on how I can fix this so I can let AppleScript loop through the values in countryList?
Thanks!
This will resolve the issue of the variable not being treated as a variable:
set countryList to {"BR", "CN"}
repeat with country in countryList
tell application "Safari"
activate
tell document 1
do JavaScript "document.getElementById('ownerValue').value = '" & country & "';"
end tell
end tell
end repeat
However, you may have another issue here, in that you are looping while inputing the value of a new variable into the same input field. In other words the JavaScript command as written is going to first input BR and immediately overwrite it with CN.
Note that the color of country in the code above is not showing as green, however, here is a clipped screenshot from Script Editor and you see it's green.
To address the comment:
Could you please explain why it was previously not treated as a variable but now is?
When mixing two languages, i.e. AppleScript and JavaScript in this use case, and passing an AppleScript variable to JavaScript, you need to build out the JavaScript command using concatenation so the value of the variable is expanded.
In your code it was being treated as a fixed value as JavaScript has no way of knowing it was an AppleScript variable, hence concatenation, i.e. & ... &. In AppleScript, & is the concatenation character.
I opened the Chrome development console pane and evaluated this expression:
Array.from(document.querySelectorAll("div.fc-event-inner")).filter(e => {
let r = e.getBoundingClientRect();
return (r.left < 1354) && (r.top < 531) && (r.right > 1167) && (r.bottom > 487);})
It returned an empty array.
Then I used the "Select an element in the page to inspect it" button in the development pane to click on an element and see my element expanded in the element list. Then I re-evaluated the same expression and it returned an array of 2 elements even though nothing on the page had moved. Why am I getting inconsistent results?
BTW, I probably should have simplified the question. I think the same is true when I simply evaluate
document.querySelectorAll("div.fc-event-inner")
Edit: I am trying to evaluate the contents of each IFrame before running my target querySelectorAll. When I evaluate this expression, things are OK, but I still don't get any updated results:
document.querySelectorAll("iframe")[0].contentWindow.document.childElementCount
But when I try to evaluate this condition:
document.querySelectorAll("iframe")[1].contentWindow
Chrome has a rather hard error (STATUS_BREAKPOINT):
I am able to circumvent this error using eval, but I find that the content of the iframe is blank:
eval("document.querySelectorAll(\"iframe\")[1].contentWindow.document.body.outerHTML")
Result: "<body></body>"
Accessing content within an IFrame poses unique challenges. To access the content from JavaScript, it's necessary to first access the correct IFrame. For example:
eval("document.querySelector(\"iframe\").contentDocument.querySelector(\"iframe\").contentWindow.document.querySelectorAll(\".fc-event-inner\")")
The eval is necessary to avoid a Chrome error that occurs as soon as the development tools pane tries to evaluate and highlight the inner frame while you type. The eval prevents the real-time evaluation and highlighting.
If using Selenium, this seemed to be working for me:
var f = switchTo.frame(0)
var banners = f.findElements(By.cssSelector("div.fc-event-inner"))
Utilities.actionLog("Banners: " + banners.size)
I am trying to fill all the text fields in Safari where the name ends with _comment1 with a number obtained from a dialog query.
The source code of the text field looks like:
<input type="text" maxlength="6" size="6" name="233941_142117_comment1" value="" onKeyUp="return autoTab (this, 6, event);" onChange="disableButtons();">
It doesn't fill the fields though. Any ideas?
My applescript is:
set query to text returned of (display dialog "Enter Query" default answer "" buttons {"Input", "Cancel"} default button 1)
log "2"
activate application "Safari"
log "3"
tell application "Safari"
log "4"
do JavaScript "
let x = document.querySelectorAll('input[name$=_comment1]');
var i;
for (i = 0; i < x.length; i++) {
x[i].value = query;
}
"
end tell
I outlined the problems with your script in the comment I left below your question. To summarise:
① Your query variable is defined as an AppleScript variable, but goes unused. You then make a reference to a variable in JavaScript called query, which is undefined and has no value.
② Your do JavaScript command has no target within Safari, which needs to perform the action on a specific tab or document.
Solution:
Without seeing the specific site you're working with, I cannot test my solution, but I'm pretty confident that it should rectify these two explicit errors that are most certainly preventing your script from working. Whether or not any other unrelated issues crop up depends on the specifics of the website.
So you've defined your AppleScript variable query, which I'm just going to do lazily here:
set query to "blah blah blah"
The core bit of your script needing amending is here:
tell application "Safari" to tell ¬
the front document to do JavaScript ¬
"x=document.querySelectorAll(\"input[name$='_comment1']\"); " & ¬
"for(let i of x){ i.value=" & quoted form of query & "; }"
Your JavaScript itself is totally fine (although I slightly modified it to my own preference, which I perhaps ought not to have done). The main points here are that I have told Safari that it will be performing the JavaScript in the front document; and that the JavaScript now makes use of the AppleScript query variable with which it uses to set the value of the <input> fields.
Having tested a variant of this on some arbitrary website I found, it appeared successful.
Let me know how it turns out.
I am sure there is something simple that I am missing but I am stumped here.
The issue is that I am looping through an array of strings and using the string value to search for a part of that string using indexOf. The first time around the loop the index of is finding what I am looking for but the second time it is not.
Here is a fiddle - http://jsfiddle.net/jeremywrags/uSwjG/1/
the line that seems to be not working is this
var aliasIndex = fromclause.indexOf(" " + tableAlias + " " );
I am trying to build a SQL parser for a cloud app and the use case here is that when a table is aliased I need to get the original table name so that I can look up the table columns. The first time around the loop index of returns the index and then the table name. The second time around the index of is -1 and the table name is not retrieved.
If I need to provide more context please let me know.
thanks
It's not matching because on the second pass, tableAlias is the string " b" (note the space). So then you search for " b " (note two leading spaces), which isn't there.
Rather than using alert, use the debugger built into your browser. You can set breakpoints in the code, step through line by line, inspect variables, etc., etc. Doing that with this would have shown you, when looking at the variable tableAlias, that it had a leading space, hopefully helping you find the solution.
Here's what that looks like in Chrome's debugger, for instance:
(If you look at the jsFiddle source above the actual debugger's version, you'll see a debugger; statement in the code — normally you don't need that statement, you can just open your page, use the "Sources" tab to find your JavaScript file, navigate to the line, and click the margin to the left of it to set a breakpoint. But sometimes [for instance, when using jsFiddle], the debugger; statement is handy. What it does is, if the debugger is open, halts execution of the code at that point like a breakpoint does.)