Web page elements loading, interactive, or complete

Applescript appears to lack methods to listen to the state of a Web page fully loading its elements. As loading web elements occurs erratically and asynchronous to the Applescript timing, this interaction often runs afoul of Applescript’s execution.

To remedy this asynchonocity, a delay command can be inserted, to allow web elements to load, before Applescript continues to execute. This however leads to erratic delays and, when the delay command time in Applescript is too short , results in errors.

I wrote the following javascript based Applescript to provide a more effective approach to asynchronous web element loading, interaction and completion. The repeat block loops through three states of javascript’s ready states of “loading”, “interactive”, and “complete”.
The function of readystate calls the javascript command “document.readyState”, and exits should the target state be found within 5 repetitions. If the target state is found, then the loop exits for that specific readystate and proceeds to the next, that is to say, from “loading” to “interactive” to “complete”.

set ReadyStateList to {"loading", "interactive", "complete"}
repeat with aReadyState in ReadyStateList
	(my readystate:aReadyState)
end repeat

on readystate:readyStateStatus
	log readyStateStatus
	set NewReadyStateStatus to missing value
	set JS to "document.readyState"
	tell application "Safari"
		repeat with i from 1 to 5
			set NewReadyStateStatus to do JavaScript JS in document 1
			log NewReadyStateStatus
			if NewReadyStateStatus is readyStateStatus then
				exit repeat
			else
				log i
				delay 0.2
			end if
		end repeat
	end tell
	NewReadyStateStatus
end readystate:

I welcome any improvements or alternatives to this script.

1 Like

This is cool! I’m not sure if I fully follow the logic. Won’t this handler always delay a minimum of 2 seconds before accepting the “complete” status even if the document.readyState is “complete” at the start? Is there a purpose for checking for each of the intermediate document.readyStates?

I’ve modified this to match my local style, made it check only for a “complete” document state, and limited it to a time duration instead of just a preset loop count. Sorry for renaming your variables and the handler, this is going in my Safari lib.

use AppleScript version "2.4"
use scripting additions


Safari_Document_ReadyState_Complete(5)


on Safari_Document_ReadyState_Complete(maxDelayInSeconds)
	if class of maxDelayInSeconds is not in {integer, real} then error "maxDelayInSeconds must be an integer or a real."
	set startTime to current date
	tell application "Safari"
		repeat
			set currentDocumentReadyState to do JavaScript "document.readyState" in document 1
			if currentDocumentReadyState is "complete" then
				return true
			else
				delay 0.1
				if ((current date) - startTime) > maxDelayInSeconds then return false
			end if
		end repeat
	end tell
end Safari_Document_ReadyState_Complete
1 Like

PaulSkinner, thank you for your ideas on a limited time duration to replace a preset loop count.

Safari_Document_ReadyState_Complete(5)

on Safari_Document_ReadyState_Complete(maxDelayInSeconds)
	if class of maxDelayInSeconds is not in {integer, real} then error "maxDelayInSeconds must be an integer or a real."
	set startTime to current date
	tell application "Safari"
		repeat
			set currentDocumentReadyState to do JavaScript "document.readyState" in document 1
			if currentDocumentReadyState is "complete" then
				return true
			else
				delay 0.1
				if ((current date) - startTime) > maxDelayInSeconds then return false
			end if
		end repeat
	end tell
end Safari_Document_ReadyState_Complete

Thanks for the revision.

Web page ready state sequence progressing from loading to interactive to completed

Although the goal of the Applescript is to detect a ready state of complete, some web pages may return a status of complete, before the web page has fully loaded all of its elements. This has led to an Applescript proceeding without the benefit of a fully loaded web page’s elements. Perhaps, the resting state of a web page is complete, and that the javascript command of document.readyState returns its resting state complete, and then fails to wait for the web page to transition through possible loading or interactive states.

A ready states of loading, in my experience has been returned from a web page, following an AppleScript to reload the page.

tell application "Safari"
		set docUrl to URL of document 1
		set URL of document 1 to docUrl
		set JS to "location.reload();"
		do JavaScript JS in document 1
end tell

An interactive state has been returned, when a web page loads various of its elements, possibly triggered by a button clicked on the web page.

As the ready state sequence generally moves from loading to interactive to completed, I added the following loop

set ReadyStateList to {"loading", "interactive", "complete"}
repeat with aReadyState in ReadyStateList
	(my readystate:aReadyState)
end repeat

It may be possible, perhaps, to skip requesting the ready state loading when a url is not reloaded, but only when a web page element, such as a button, is clicked to trigger updating various of the page’s elements.

I appreciate any insights or ideas you might have.