In C# I can write event handlers as follows:
var wdApp = new Microsoft.Office.Interop.Word.Application();
wdApp.DocumentBeforeSave += (Document doc, ref bool saveAsUI, ref bool cancel) => {
//do stuff here
};
In VBA/VB6, I can use static event handling:
Dim WithEvents wdApp As Word.Application
Private Sub wdApp_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
'do stuff here
End Sub
I would prefer to use dynamic event handling. However, in Javascript, even when using static event handling with the syntax described here:
var wdApp = new ActiveXObject('Word.Application');
wdApp.Visible = true;
function wdApp::Quit() {
window.alert('Quit');
};
it fails:
0x800a138f - JavaScript runtime error: Object expected
Also, static event handling is an option in VBA/VB6, because the declarations can be marked Private
. However, in Javascript, both the variable and the handler have to be declared in the global scope.
Two questions:
How can I handle events of Automation-created objects with Javascript in an HTA environment? (Note: I know that it is possible in WSH using a prefix passed to
CreateObject
, and a function namedwdApp_Quit
, but I am looking for an HTA solution.)How can I do this without polluting the global scope?
There is an older question here.
The error appears to be because
in Javascript function declarations are evaluated first, before the variable is initialized. It's as if the code was written thus:
The Microsoft JScript parser interprets the specially-named declaration as an instruction to attach the function as an event handler.
wdApp
, becausewdApp
at that point is stillundefined
. Hence, the error.The solution is to force the function declaration to be evaluated after
wdApp
is initialized. This can be done in one of three ways1:Since the function declaration is hoisted only to within the function scope, wrap the function declaration in an IIFE:
Create the declaration using some sort of string -> code mechanism --
eval
,setTimeout
,window.execScript
, ornew Function
:Initialize the variable before the current
SCRIPT
block. Most of the examples in the Scripting Events article do this by setting theid
property on some element before theSCRIPT
block:but this could also be done using multiple
SCRIPT
blocks:As far as polluting the global namespace, only the third variant requires
wdApp
to be in the global namespace; the other two variants can be wrapped in an IIFE.1. Technically, there is a fourth way under IE and HTAs, but it involves non-standard HTML instead of non-standard Javascript; But it can only be used if the object is declared using the HTML
OBJECT
tag, not withnew ActiveXObject( ... )
.I could not make the object::event syntax work. So, I delegated the event handling to an auxiliary script, which follows.
caller.js file:
Then, call this script from within a HTA file
main.hta file:
Both files must be on the same folder. Run main.hta and the code in caller.js will be called, without polluting the global namespace of the HTA.
This example is for testing purposes. There is a 20 seconds timeout for the script. After this time, it will quit and events won't be processed.
If it works, tell me so we can create a better way of communicating from the HTA (mshta.exe) to the JS (wscript.exe). There is a chance of using "GetObject" from the HTA to control the word application (I did it once, but with VBScript), while the events are processed by the JS file.