Wait for page to load

Hi,
Does anyone know how to make an applescript pause until a Safari page has completed loading? In my script right now, I have a pause for “x” number of seconds, but this is less than ideal since on a fast connection I am waiting around, and on a really slow connection, sometimes the script tries to move along too early. I have looked around for other solutions, but so far haven’t found anything.

thoughts appreciated!

Hi,

I’m using this handler, it returns true when the page has loaded successfully,
otherwise false. You can pass a timeout value.


on page_loaded(timeout_value)
	delay 2
	repeat with i from 1 to the timeout_value
		tell application "Safari"
			if (do JavaScript "document.readyState" in document 1) is "complete" then
				return true
			else if i is the timeout_value then
				return false
			else
				delay 1
			end if
		end tell
	end repeat
	return false
end page_loaded

HI there,

I found this post useful thanks, but I didnt find the “do javascript” method reliable enough.
I found that this works better. It’s an adaptation of a OSX hint:
http://hints.macworld.com/article.php?story=20071015132722688

I’ve set it to activate safari and beep when the page finishes loading.
I’ve not tried it with LION, but the OSX hint suggests:

Anyway, this works for me using OSX 10.6.7 Safari 5.0.5

tell application "Safari"
	activate
	repeat until SafariWindowHasLoaded(1) of me is true
	end repeat
	beep
end tell

on SafariWindowHasLoaded(inWindowIndex)
	tell application "System Events" to ¬
		tell application process "Safari"	
			set theStatusText to name of static text 1 of group 1 of window inWindowIndex as text
			if theStatusText begins with "Contacting" or ¬
				theStatusText begins with "Loading" or ¬
				theStatusText begins with "Waiting" then
				set theReturnValue to false
			else
				set theReturnValue to true
			end if
		end tell
	return theReturnValue
end SafariWindowHasLoaded

Cheers, nik

I am using 5.1.2 on 10.6.8. Any thought?

error "System Events got an error: Can't get static text 1 of group 1 of window 1 of application process \"Safari\". Invalid index." number -1719 from static text 1 of group 1 of window 1 of application process "Safari"
error "System Events got an error: Can't get static text 1 of group 2 of window 1 of application process \"Safari\". Invalid index." number -1719 from static text 1 of group 2 of window 1 of application process "Safari"

Are you are opening pages on a the same web site where you know certain facts about the page are always going to be there? You can customize your script where it waits for the page until meets that certain criteria.

for instance if you know how how large the page is supposed to be when fully loaded:


on wait4page()
	delay .5
	set safe to false
	repeat until safe
		delay 0.2
		tell application "Safari"
			do JavaScript "document.readyState" in document 1
			if result is "Complete" then exit repeat
		end tell
		tell application "Safari" to set PageText to the text of document 1
		if length of PageText is greater than 1000 then set safe to true
	end repeat
end wait4page

Or if you know there is always the same text in the content on every page. (Maybe the footer, or the last items loaded on page load.)


on wait4page()
	delay .5
	set safe to false
	repeat until safe
		delay 0.2
		tell application "Safari"
			do JavaScript "document.readyState" in document 1
			if result is "Complete" then exit repeat
		end tell
		tell application "Safari" to set PageText to the text of document 1
		if PageText contains "My Unique text" then set safe to true
		end if
	end repeat
end wait4page

I kind of think the JavaScript above no longer works in the current version of Safari, also waiting for “loading” in the window name is not longer usable either. Not sure about the first, but definitely on the second.

This sort of works but it doesn’t really check if the full page is loaded in Safari 6.0.1 for me.

--tell application "Safari" to open location "http://www.macosxhints.com/article.php?story=20091101035318405"

tell application "Safari" to open location "http://macscripter.net/viewtopic.php?id=35176"
if page_loaded(20) then
	say "Page Loaded"
	
	tell application "Safari"
		activate
	end tell
	
else
	say "Page Failed to load or Safari didn't open"
end if

on page_loaded(timeout_value) -- in seconds
	delay 1
	repeat with i from 1 to timeout_value
		tell application "Safari"
			if name of current tab of window 1 is not "Loading" then exit repeat
		end tell
		delay 1
	end repeat
	if i is timeout_value then return false
	tell application "Safari"
		repeat until (do JavaScript "document.readyState" in document 1) is "complete"
			delay 0.5
		end repeat
	end tell
	return true
end page_loaded

Hello!

According to the docs for document.readystate it should be fully loaded.

I wonder if there are any discrepancies you know of.

Edit
I got it, that it returns “complete”, without it actually being so, so nevermind.
(It would have been interesting to know if the tcp packets that the page are consisting of are received and acknowleged, and that it is just the rendering that is not fulfilled though.
Or if Safari is taking a very optimistic approach, having received a some packets, when stating that the page load is completed.)

Anyways, the fix for my case is just to add a delay loop if the getElementById should fail.

And thanks! :slight_smile:

Good call, thanks!

Is there any chance you could post an example of using getElementById to make this work?

Until now, I’ve been using a combination of the first two solutions, but with Safari 6, there is no text for Loading, Contacting, or Waiting that I can find.

Hello.

The idea behind getElementById is that you then see that you can get a value out of that element, that you have found with the domInspector. This seems to be a very hard thing to do, I haven’t the time to do it right now, but it really should be possible!

I use this:

tell application "Safari"
	# loading and stuff comes here...
	repeat
		local pageState
		set pageState to do JavaScript "document.readyState" in document 1
		if pageState = "Complete" then exit repeat
		delay 0.2
	end repeat
end tell

If it doesn’t return that value, then the page isn’t loaded. if you need further assurance, then you can go here and grab Red Menace’s handler.

I thing getElementById will work, if you have something that retuns text, so you better get a value off some DOM element that contains text.

This is a handler I just made, that you may rework that will return true if the page is fully loaded, it uses text items to split the pagesource of Safari’s first document into a list. If the element is there, clearly the page is fully loaded, and it will return true, since the list then has two elements.


to fullyLoaded()
	local tids, thelist, theText
	set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "</html>"}
	tell application "Safari" to set theText to source of its document 1
	set thelist to text items of theText
	set AppleScript's text item delimiters to tids
	if length of thelist = 1 then return false
	return true
end fullyLoaded

This will actually log you in at MacScripter if you turn off keychain access, and provide the password, and provide your own user name.


tell application "Safari"
	open location "http://macscripter.net/login.php"
	repeat
		set theReturn to do JavaScript "document.getElementById('navlogin').innerHTML" in document 1
		if theReturn is not missing value then exit repeat
		delay 0.2
	end repeat
	do JavaScript "oFormObject = document.forms['login']; oFormObject.elements['req_username'].value = 'McUsr'; oFormObject.elements['req_password'].value = 'XXXXXXXX';document.forms['login'].submit();" in document 1
end tell

As you can see, if the do javascript doesn’t returns something else than missing value, then the page is loaded.

Your script runs in a constant loop when I run it without the second JavaScript command. It never exits the repeat loop.

I have lost my password during fiddling with this :smiley:

It works for me, very well, I am on Safari 5.1.7 and I do have JavaScript enabled. (Just saying).

If you load Macscripters so that it is in Safari’s frontmost window, and run this from your ASEditor:


 set theReturn to do JavaScript "document.getElementById('navlogin').innerHTML" in document 1
log theReturn

Don’t you get any result? I am sorry if they have changed Safari v.6 to not get results of the getElementById, but I don’t to believe it. This is the safest way to check if a page is loaded, as document.readystate, is saying it is, when it isn’t. But when the DOM tree is working, well, then it is working, and the page must be loaded.

I am also running 5.1.7 and did the following script. I had to remove the last line to show the result, but the result is “missing value”. It could really use this script if I could get it to work.


tell application "Safari" to set theReturn to do JavaScript "document.getElementById('navlogin').innerHTML" in document 1
log theReturn

I tested it for the navlogin link, pleas try to show source of Macscripter.net before you log in, and find another element with id then, I can’t fathom that it is happening to you really.

Try with some other element with id, that has contents, that is the only advice I can give you. See if you get it working, the page with Macscripter.net must be the frontmost document open in Safari, before any source window or anything else.

I have also the developer menu enabled, so I can see the responses when I execute JavaScript from AppleScript, at least I then can see if there are any error messages from the JavaScript console.

Good Luck!

Ok, I think I know what is going on. I was running the script on this MacScripter post page thinking the script was just checking to see if the page was fully loaded. Looking a little closer at the script, let me ask a question. Is the script searching for the text “navlogin” in the source?

It more looks it up in the dom tree, and returns the html that is hanging on that element. I think id used like this can only be used with span and nav tags, and some other, so it basically works with block elements. To use this on other sites, you’ll have to find such an element with contents, on the page that are intitially loaded.

Maybe I should have stated that, I did somewhat take the context for granted. Especially since you have delivered two handlers. :slight_smile:

I am glad we sorted that out, because this seems to work good for me, so I hoped it could work well for other people too.