Calling a function via ExecuteJavaScript

Noobie here…WebApp project…loaded HTML and a JavaScript file into an WebHTMLViewer. In the browser’s debugger, I can view the script file and can set breakpoints. Now I want to invoke functions defined in the script from Xojo by calling ExecuteJavascript on the WebHTMLViewer object.

For testing, I use a Xojo button on the WebPage which I click after the .html and .js files are loaded into the iFrame and displayed. I then open the browsers’ debugging tools and watch for breakpoint to be hit in the JavaScript…

ButtonPressed()

htmlViewer.executeJavaScript ( "console.log ('Sending Message From Xojo')" )
//success...outputs the message to the browser console
htmlViewer.executeJavaScript ( "myFunction;" )
htmlViewer.executeJavaScript ( "myFunction();" )
//fails...breakpoint at myFunction is not hit

Instead, the browser console reports a JavaScript error with what appears to be the session.identifier as the base url:

POSThttp://127.0.0.1:9000/2F3B8876D2CD33CA6E452829255E0C196D9AF60F02B08083D9232DE0D3032AC5/comm/event/JavaScriptError.

So…it appears I am trying to send mal-formed JavaScript and the error is generated on the server side.

Makes me think the ‘syntax’ for calling the JavaScript function is incorrect.
I also tried sending an immediately invoked function which then called ‘myfunction()’ but no luck, still JavaScript error.

Simple, but what am I missing?

TIA

Have you tried running the Javascript you’re attempting to execute through ClosureCompiler to check for errors?

This line will not execute the function, but will not impede the functionality of the Javascript passed:

htmlViewer.executeJavaScript ( "myFunction;" )

I just did the following test by adding an HTMLViewer to a WebPage. I added a Button to the page with the following code in the Pressed event, and it executed exactly as expected whether I loaded a URL in to the viewer first or not:

var exec() as String
exec.Append( "console.log('start');" )
exec.Append( "function myFunction() {console.log('in function');};" )
exec.Append( "myFunction;" )
exec.Append( "myFunction();" )
exec.Append( "console.log('end');" )

HTMLViewer1.ExecuteJavaScript( String.FromArray( exec, "" ) )

With the following result:
image

I would look at the Javascript for your function. Depending on where you’re storing your myFunction, it’s probably also gone out of scope.

Anthony:

First, thanks for the kindness of your reply. I remained stumped so any help appreciated.

I thank you for Closure Compiler, a great tool to have now and in the future.

I ran my script through Closure Compiler and it said “Compile Successful” with no errors reported (almost to my surprise :wink:)

‘myFunction()’ is defined at the global level of the script. It is not part of either a class, object, variable, callback, or other function. It is at the top level of the script. It is a named function (not anonymous).

I also wondered if the browsers JavaScript engine sees ‘myFunction()’ to be out of scope. What initially confused me was the error appearing in the browser console seems to be originating in Xojo and sent to the browser as a POST reply.

If I load a page from a non-Xojo project, with a script error, the Firefox console highlights JavaScript errors in red, and shows a system error type message (not a Xojo Script error … as in my OP). Before Closure Compiler, I was using the browser error messages to detect missing brackets, parenths, commas in my nascent scripts (an agonizing slow process…) so at least I was familiar with how the JavaScript engine on the browser side coughed up my prior syntax mistakes.

Do I need to explicitly expose function declarations in my scripts using ‘declare’ or ‘export’ (as in a module)? It did not seem to be necessary, but I am not finding the solution. And, of course, in your example, no such ‘export’ was needed.

I am going to try your example exactly, verbatim so stay tuned and many thanks again.

Anthony:

Here are my test results:

  1. If I use your code verbatim in a button.Pressed event on a WebPage containing an htmlViewer, it works just as you said, however…
  2. If I place a ‘myFunction’ definition in a .js file, then load it into the WebHTMLViewer using viewer.LoadURL(myWebFile.url), then it fails as in my OP, showing a POST reponse containing a url …comm/event/JavaScriptError

To be clear, I re-tested by taking my original .js file and creating a new definition at the root level named exactly ‘myFunction’ with just the following code (and it passed error free in ClosureCompiler)

function myFunction() {
	console.log ('success..in function');
};

Then, I put the following code into a button.Pressed event handler on a WebPage where the .js file had been loaded into the htmlViewer object via loadURL:

htmlViewer.ExecuteJavaScript ("myFunction();")
//failed, with this message in the browser console:
XHR POSThttp://127.0.0.1:9000/05510EFC1557670FAA16467CFAA55809B38A1D5392DB9051E326440A6B283914/comm/event/JavaScriptError.

So, it works if I feed the entire function definition at the time of the executeJavaScript(), but not if the same code resides in the .js file loaded into the browser, and I try to invoke this function using its name in a executeJavaScript(); call.

Its seems I have no problem triggering window/document/system functions (like console.log) using ExecuteJavascript, but cannot invoke functions that I have defined. And the error is appearing on the Xojo framework side, not the browser’s interpreter side.

Confused…any help appreciated…

Maybe you could repeat my experiement and see if you get the same error…

Many thanks

Sounds like it’s going out of scope. The error looks like it’s coming from Xojo because Xojo is using eval() on the Javascript you pass via ExecuteJavascript under the hood, and this occurs as a result of the XHR.

Try explicitly assigning your function to a global variable, something like:

window.myFunction = function() {
	console.log ('success..in function');
};

Thanks again…mystery deepens. I ran several tests. In my button.Pressed event, I use consistent but simple call to myFunction(), as below…

htmlViewer.executeJavaScript( "myFunction();" )

Then I ran 3 tests (separately) by changing the syntax of the function declaration in the external .js file.

Test 1:

window.myFunction = function () {
     console.log ('success...in function');
};

Test 2:

  let myFunction = function () {
    	console.log ('success..in function');
    };

Lastly, I went bare bones. I created a new, empty .js file with just one function, and nothing else, trying to eliminate any other code that might possibly affect the scope of myFunction() during eval…

Test 3.…bare naked .js file with just…

  let myFunction = function () {
    	console.log ('success..in function');
    };

All 3 examples return the same XHR…POST…JavaScriptError.
Still unable to invoke a named function in a pre-loaded .js file.

Hope I am not dealing with bug in the Framework, but instinctually I believe this comes down to how I invoke the function in my executeJavaScript call.

My appreciated…but still swimming upstream

I just set the function in one Button.Pressed:

var exec() as String
exec.Append( "window.myFunction = function() {console.log('in function');};" )

HTMLViewer1.ExecuteJavaScript( String.FromArray( exec, "" ) )

And used it in another:

var exec() as String
exec.Append( "myFunction();" )

HTMLViewer1.ExecuteJavaScript( String.FromArray( exec, "" ) )

Without issue, so I think I’ve contributed as much as I can here.

Thanks again…

It seems the current Framework does not allow one to call named functions loaded via a script tag in the html file, which limits executeJavascript() to mostly simple functionality, like setting the .innerHTML or style attributes of an element.

I will keep testing…

Very appreciative of your time and insights.

Many thanks again

Forum for Xojo Programming Language and IDE. Copyright © 2021 Xojo, Inc.