Script onload - call internal function - javascript

In my app, I'm using a script tag to load an externally-hosted JS script. This script defines a function which I want to call on the frontend when it's loaded, and to pass certain variables. So you could imagine this file looks something like this:
window.someFunc = (args) => {
// do something
}
And then in my frontend HTML:
<script src="/someFile.js" onload="someFunc('hello')"></script>
This works, but if I change window.someFunc to const someFunc, it no longer works, because the onload doesn't have access to someFunc. It seems to me non-ideal that I have to attach the function to the window object. Is there a way to avoid this and still access this function in the onload of the script tag?
I know this question seems pretty basic, but I've done a number of searches and can't find an answer about how to do this neatly.

Related

How can I make a function defined inside another function be in the window scope?

I am creating a wrapper for some arbitrary code (let's call it managed code). The managed code may include some functions that are defined in the window scope and are expected by other scripts on the page (horrible, 1997, practices, I know, but such is what I have to deal with), as global functions.
The purpose of the wrapper is to delay executing the wrapped code until jQuery is loaded. It looks like this:
(function () {
var once = true,
check = setInterval(function () {
if (window.$ && once) {
once = false; // setInterval can stack up if the UI freezes. Ensure this only gets called once.
executeBundle();
clearInterval(check);
console.log('Jquery loaded');
}
}, 100);
})()
// Wrapper proper
function executeBundle() {
// oodles of code of any origin
}
Now that the managed code is wrapped inside the executeBundle function, all functions/variables declared within it will be scoped to that function. This isn't a problem for the managed code itself, but for other scripts that load separately that may rely on global functions it provides.
I'd like to know if anyone knows a strategy like eval, but without the security issues, that may allow me to preserve the window scope for the running of the managed code. The constraint is that I can't modify the managed code at all--just the wrapper.
Based on T.J. Crowder's phenomenal answer, I realized that I could add the managed code to a <script> element and add that to the <head> like this:
var codeBundle = // Code in one long string
function evaluateBundle() {
var script = $('<script type="text/javascript"/>')
script.html(codeBundle);
$('head').append(script);
}
And let the parser evaluate the code.
I'd like to know if anyone knows a strategy like eval, but without the security issues
If you're evaling code of your own that you would run by having it in a script tag anyway, there are no security issues. You're running code either way.
You can't do this if the code you're wrapping will appear directly within evaluateBundle and it has declarations (vars and function declarations) that were supposed to be at global scope. Handling those would require modifying the wrapped code.
You can do this if you load that code separately, though, and then do a global eval on it. For instance, put it in a script block with a non-JavaScript type so the browser doesn't execute it:
<script type="x-code-to-wrap"></script>
...and then:
function evaluateBundle() {
var code = document.querySelector('script[type="x-code-to-wrap"]').textContent;
(0, eval)(code);
}
(The (0, eval)(code) bit is the global eval, more on MDN).
You may have to adjust the textContent part of that for cross-browser compatibility. This question's answers suggest using jQuery's html function:
function evaluateBundle() {
(0, eval)($('script[type="x-code-to-wrap"]').html());
}
Live example on JSBin

HTML and Javascript Why must I perform all tasks in javascript inside functions that get called by the HTML?

It keeps me from easily defining global variables and its often a nuisance. Why doesn't the code outside functions that are called execute? For example, if I call the function myFunction from HTML, this works...
function myFunction() {
var myObject = document.getElementById("myHTMLObject");
myObject.style.backgroundColor = "blue";
}
but not this...
var myObject = document.getElementById("myHTMLObject");
function myFunction() {
myObject.style.backgroundColor = "blue";
}
If I call the function from the HTML, only the code inside that function will run (unless that function calls other functions). Am I making a mistake or is there a way around this? I don't want to encompass all my code in a window.onload function.
P.S. I run my html on Chrome if it makes a difference.
Thanks for any help.
It does execute, and does when when the script runs, which is when the <script> element is parsed.
If you try to get an element that is added to the DOM by HTML that appears after the <script>, then it won't exist when you look for it so you will get nothing back.
If the <script> appears after the element, then you won't have a problem.
If this example:
var myObject = document.getElementById("myHTMLObject");
function myFunction() {
myObject.style.backgroundColor = "blue";
}
doesn't work, then here are a couple possible reasons:
The script is running too early and thus when you do document.getElementById("myHTMLObject");, the page has not yet been loaded and thus myHTMLObject does not exist yet.
You have more than one global definition of myObject and one is overwriting the other.
Your second coding example is recommended for a number of reasons:
Doesn't use a global variables which is advantageous (variables are private to within the function and can't create conflicts with any other code or be interfered with by any other code).
The functionality is entirely contained within the function
There are no timing related issues with when the initialization code is run because the DOM is searched only when the operation is about to be carried out.
Getting DOM objects when needed works better with dynamically added HTML.
A simple user, triggered operation is plenty fast getting a DOM object when needed.

javascript - how to call a function newly added from ajax

I have a coding difficulty which have been asked in this forum before:
Calling a JavaScript function returned from an Ajax response
But I didn't find the answers quite satisfying. To be more precise of the problem I'm dealing, here is the detail:
I dynamically load a document (HTML and javascript) using jquery
var url = 'document.php';
$('#container').load(url);
Example of what the document.php looks like:
<form>
<input name="firstname"></input>
</form>
<script>
function dosomething()
{
console.log($('input[name=firstname]').val());
}
</script>
*The dosomething() function is the one I'd like to call later
And then I want to call the functions from that document.php. Due to my requirement, I don't want to call the function after the documents' loaded, but rather to call it later when I need it. Because it was dynamically loaded, the DOM doesn't recognize the functions. How to properly call this function?
Thank you
the DOM doesn't recognize the functions
This sounds like your other functions are wrapped in $(document).ready() in the remote page. If that is the case they are out of scope for you to call them from code in the main page and you need to move them out of the ready handler to make them globally accessible.
EDIT: Other possibilities
Script tags in head- move to body after html, or use $.getScript in ajax callback to retrieve
I think that you're trying to implement the technique called on-demand javascript (or lazy-loading). In other words, your page should initially load just a small script - but use a bunch of objects and functions, which are available in some other files, but will be loaded when they're required.
If that's the case, I have to warn you: you'll probably need to update your existing code. Instead of just calling some function right as it is, in all gun-blazing glory, you should check for its existence first - and if it's not available, wait for its loading:
if (typeof lazyObjects.someLazyFunction !== 'function') {
lazyLoad('lazyFunction.js');
}
lazyObjects.someLazyFunction();
The key point here is that lazyLoad should be synchronous. In other words, you'll have to wait until the script containing your function is actually loaded. Otherwise someLazyFunction just won't be defined when it's called, even with this sort of checks.
lazyFunction.js, in turn, will contain some code that will alter lazyObjects, adding to them the required method as a property:
// in lazyFunction.js
lazyObjects.someLazyFunction = function() { ... }
While it's technically possible to use global (=window) object for these cases, I usually don't do this - and won't recommend doing it either.
See, it's not that simple. ) I'd recommend reading this article to find out more about this technique - and actually using some established components to implement it in your code (some of them are mentioned in the linked article).

Why is script ordering important when assigning a function to window.onload?

I have a website with three files:
index.html
<html>
<head>
<script src="First.js"></script>
<script src="Second.js"></script>
</head>
<body></body>
</html>
First.js
window.onload = Main;
Second.js
function Main() { var foo = 1; }
When I open the page, I expect the variable 'foo' to be set to '1'. Instead, when I open the page, it breaks indicating 'Main is not defined' and 'Main()' is never called.
If the '.onload' event for the window isn't supposed to fire until the page is fully loaded and thus assumingly both scripts have been loaded, why doesn't the window object have a reference to the 'Main()' method in 'Second.js'? Should 'Main()' be a globally accessible function?
NOTE: I realize I can change the order in which I load the scripts and then my code would work, but that's not the purpose of my question. What I really want to do is validate my assumption.
It doesn't work because the code in <script> elements are run sequentially and independently (this assumes synchronous scripts, the default). That is, two different <script> sections cannot forward-reference each other. Now, JavaScript "hoists" function name () {} constructs within the same context, so this would have worked "out of order" in a single <script>.
Preamble / warning:
Consider not using onload. If something else tries to use it (onload), then something will likely break. (I recommend jQuery because it "gets this right" and "is so darn easy"... then again, so do many other frameworks. Pick one :-)
For instance, this would work in jQuery:
jQuery(function () { Main() })
Explanation of why it works (and how to fix it without reordering scripts or using jQuery):
Note that in this case the jQuery internal event handler is called, which calls the anonymous callback, which then calls Main (which is now resolves to a function-object). The "similar" code here would be:
window.onload = function () { Main() }
These work because Main is not evaluated until the onload has occurred, in which case all the [synchronous] <script> elements have been executed. (Please see my comment as to what it means to evaluate Main.)
On the other hand, window.onload = Main (or jQuery(Main)) evaluate Main right-then and use the resulting value; as others have noted, Main is not set ("is undefined") at this point due to the ordering of the <script> elements (they run sequentially).
Please refer to the note at top as to why to not to use onload directly ;-)
Happy coding.
The second script file should be included first! That's where the function is defined. You can't reference it in the first script until it's not loaded.
The Main() function doesn't exist until Second.js has been loaded, so if you're loading First.js first there's no function-object to use in window.onload = Main; (Main is undefined). Load Second.js first if you aren't already.

Pass a callback in ExternalInterface

I want to call a Javascript function from Flash, which I can do with ExternalInterface, but the Javascript function takes a callback. Is there a way to give it a Flash callback?
I've thought of something like this:
ExternalInterface.addCallback("foo", function(){...});
ExternalInterface.call("theFunction", "foo");
But that wouldn't work since theFunction would attempt to do foo(), while it should really do swfObject.foo(). The problem is the page and its Javascript are not under my control (though I can request changes if really needed).
This is closely related to the first question in the related questions section.
Along the same lines as the answer to that question, you can do:
ExternalInterface.addCallback("foo", function() { /* ... */ }); // The callback
ExternalInterface.call("theFunction(function() { swfObject.foo(); })");
You're misunderstanding the documentation, I think. callback in this instance is just a reference to a function inside Flash, not a callback to something you call.
Basically, you use .call() to call a JS function from AS; and you use .addCallback() to tell the Flash Player which AS function should be called based on the name.
On your example, theFunction would get one parameter as being 'foo', which is the name that references your anonymous AS function. Not sure why you would want to pass the function like that, but if you need, you could just call it from JavaScript with
function theFunction(callback) {
// .. do something...
swfObject[callback]();
}
Now, if you don't have control over the JS/HTML side, I'm not sure if you can do that. Not sure why you'd need, anyway - JS calls are synchronous, as if they were running on the same thread, meaning the Flash Player will execute the JS code and only then return to the Flash Player... you don't have to wait for execution or anything.
Also, if you really need to control the page without touching the JS/HTML side, remember you can inject entire pieces of JS code via .call - it doesn't need to be a simple function call. You can create your entire functions from inside the SWF. For example,
var js:XML = <script><![CDATA[
// Javascript code...
]]></script>;
ExternalInterface.call(js);
Or, if you need the return data, you don't need a callback either - just do a simple call as in
// JS
function isNumberZero(__num) {
return __num == 0;
}
// AS
trace ("Is number zero = " + ExternalInterface.call("isNumberZero", 10));
Not sure if this helps at all. If not, it'd be good to have more information on what exactly you're trying to do.

Categories

Resources