This works:
<script src="some.js">
<script>
afunction(); //this function is in some.js
</script>
Then I thought to improve the page speed of the site and load some.js like this
var element = document.createElement("script");
element.src = "some.js;
document.body.appendChild(element);
and now the function doesn't exist and i get an error.
Is there a solution to this?
As others pointed out above, some of the errors you might be encountering are perhaps due to missing " and so on. Assuming that you have every syntax correct, this approach will fail because how HTML parsing happens:
Assuming that you've got this code in the <HEAD> section:
When the parser (in your browser) reads the file stream where you've got this code, it is going to construct the DOM as it is doing so. This means that when it gets to the point in your code where you are telling it to append as a child your script node element to the body, it is going to fail because document.body does not exist yet and this is an error.
Assuming that you've got this code in the <BODY> section:
Then by the mere that you have attached a SCRIPT node element to the DOM does not imply that the file has been loaded by the Javascript engine and processed the content of "some.js". Therefore, when "aFunction" is called, it is undefined.
Finally, i do not think you gain anything peformance-wise by loading your script after your document has loaded.
There are many Syntax Error in your Script
But the answer to the actuall question is window.onload
<script>
window.onload = function () {
afunction();
}
</script>
Syntax Need Correction
1) Close Script Tag :
<script src="some.js"></script>
<script>
afunction(); //this function is in some.js
</script>
2) Missing double quote(")
var element = document.createElement("script");
element.src = "some.js";
document.body.appendChild(element);
The function needs to be written globally like,
$(document).ready(function () {
});
afunction () {
}
in 'some.js' script.
and access tat function using div tag.
<div id = 'a' onClick='afunction()'>
</div>
The problem is caused by calling the function too soon after the node has been appended. All you need is a timeout.
<script type="text/javascript">
var elem = document.createElement("script");
elem.src = "scripts/test.js";
elem.type="text/javascript";
//
function runTest()
{ document.body.appendChild(elem);
setTimeout("alertThis('This is a test')",50);
}
window.onload=runTest;
</script>
The external js for this example says
function alertThis(msg) { alert(msg); }
It runs correctly in all browsers.
Related
I'm looking for a way to pass functions as parameters to the script tag. For example, to make the following work:
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction()}></script>
<script>
myfunction() {
console.log("hello world")
}
</script>
And then trigger the function from the script.
Since we can pass values in attributes and capture using getAttributes : ref
Try this
<script>
// move function definition above and pass function ref - don't call that function
myfunction(){
console.log("hello world")
}
</script>
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction}></script>
Yes there is a way!
you can delete the " () "
just turn :
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction()}></script>
into:
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction}></script>
And over!
It's my pleasure to help you!
by the way if you are interested, please help me also:
The is my question
that's pretty easy however it won't be accurate as you don't know which script tag will work first or if it will be compiled using inline or not.
if it uses inline your code will not work and you have to use the server to render javascript instead
here is an example using pure javascript. in my understanding you want after loading script /widget.js it will execute function stored in data-myfunc:
widget.js
if (document.currentScript) {
const script = document.currentScript.getAttribute('data-myfunc')
if (script) {
new Function(`
// sandbox faker
const window = undefined;
const document = undefined;
const Document = undefined;
const Window = undefined;
// run script
${script}
`)()
}
} else {
console.warn('widget.js loaded inline. Not working')
}
note if you want to declare the function myFunc after the script /widget.js you have to edit my code to use events like DOMContentLoaded to make sure the function exists
I'd like to load this code (1) via XMLHttpRequest in another page
<input type="text" onkeypress="info(event)"/>
<script>
function info(e) { console.log(e.keyCode); }
</script>
This (2) is the way I tried...
<div id="x"></div>
<script>
var xhr = new XMLHttpRequest();
xhr.onload = handleResponse.bind(xhr);
xhr.open("POST","test.php",true);
xhr.send();
function handleResponse() {
var x = document.getElementById("x");
x.innerHTML = this.responseText;
var js = x.getElementsByTagName("script")[0];
eval(js.innerText);
}
</script>
...and this is the error I got on keypress:
Uncaught ReferenceError: info is not defined
Why the info() is not found since its definition was eval'd? How to make the info fire on keypress if (2) should be unaware of (1)? No jQuery.
Following Mike's recommendation, I created this question to solve separated problem that leads to explanation of this problem.
(edited for es202020, because JS has changed drastically since 2013 and the old answer is now an anti-pattern. Don't use eval)
Scope. You didn't eval it on window, you added it to the scope of that function handleResponse so it won't exist outside of handleResponse. Also, in this case you don't really need eval at all, just create a script element and add that:
function handleResponse(evt) {
const s = document.createElement(`script`);
s.textContent = evt.responseText;
document.head.appendChild(s);
}
The browser already knows how to interpret scripts, but you need to add elements to the head if you want them to execute instead of be inserted as inactive DOM nodes after the page has already loaded.
Don't use eval().
Let's say I have some simple Javascript like:
<script>
var hello = function(){
alert("Hello World!");
}
</script>
.. on a page helloworld.html. If I loaded this script block into another page using Pjax. How do I execute the function hello()?
For security reasons, many browsers will not run Javascript injected by innerHTML, which I'm thinking Pjax likely uses. (Here's a minimal example.)
Maybe the solution proposed in Pjax's issue #48 will help
What worked for me was to place my jQuery code in a function, call it
normally on document.ready (for non-pushState browsers), and then bind
the function to pjax:end, i.e.:
$('body').bind 'pjax:end', mainFunction
This is possible with PJAX. You just need to have the script tag with type text/javascript.
Code from PJAX library:
function executeScriptTags(scripts) {
if (!scripts) return
var existingScripts = $('script[src]')
scripts.each(function() {
var src = this.src
var matchedScripts = existingScripts.filter(function() {
return this.src === src
})
if (matchedScripts.length) {
matchedScripts.remove();
}
console.error("FOUND SCRIPTS", scripts, matchedScripts.length);
var script = document.createElement('script')
script.type = $(this).attr('type')
script.src = $(this).attr('src')
document.head.appendChild(script)
})
}
I want to mimick this behavior:
<script src="console.log.1.js"></script>
<script>console.log(2)</script>
<script>console.log(3)</script>
That logs out:
1
2
3
Doing this doesn't work:
<script>
var x = document.createElement("script");
x.src = "console.log.1.js";
x.async = false;
x.defer = false;
document.body.appendChild(x);
console.log("2");
console.log("3");
</script>
It logs out:
2
3
1
The only way I found so far to achieve it:
<script>
document.write("<scrip" + "t src='console.log.1.js'></scrip" + "t>");
</script>
<script>
console.log("2");
console.log("3");
</script>
Is that really the only way to force synchronous loading of external scripts in all browsers? Why doesn't setting async=false, defer=false work?
UPDATE
FYI, if someone is wondering, the following document.write inception works (in Chrome..):
<script>
// http://jsbin.com/avatiw logs "during"
document.write('<scrip' + 't>console.log("before");document.write("<scrip" + "t src=\\"http://jsbin.com/avatiw\\"></scrip" + "t>");</scrip' + 't>');
document.write('<scrip' + 't>console.log("after");</scrip' + 't>');
</script>
Works and logs out:
"before"
"during"
"after"
Yes, that's the only way to force the script to load during the page parsing. Or at least, the only way I'd be willing to believe worked well cross-browser.
If your script were like this, I could see your thinking:
<script>
var x = document.createElement("script");
x.src = "console.log.1.js";
x.async = false;
x.defer = false;
document.body.appendChild(x);
</script>
<script><!-- note the new script element -->
console.log("2");
console.log("3");
</script>
...because in theory, when the parser hits the script element, it suspends everything (because there might be document.write statements) and calls into the JavaScript layer. So you might think, well, adding a script element to the end of the body at that point would insert it between the two.
But adding a script element via appendChild is just fundamentally different, it's by nature an asynchronous operation (your code continues while the script is being downloaded, which is not the case with script elements in the markup barring the defer or async attributes). I can't point at any one spec to say why, but the behavior you're seeing is exactly what I'd expect. The treatment of script elements inline with the markup is a bit special.
We can see that it's the download that's the issue — at least in Chrome — by comparing the result with using a script element with inline content.
Using an external file (live copy | live source):
<script>
console.log("before");
(function() {
var s = document.createElement("script");
s.src = "http://jsbin.com/avatiw"; // Logs the word "during"
document.body.appendChild(s);
})();
</script>
<script>
console.log("after");
</script>
Result:
before
after
during
Using inline script (live copy | live source — note that I've made no attempt to make this cross-browser, it works in Chrome and Firefox as they support the text property on script elements):
<script>
console.log("before");
(function() {
var s = document.createElement("script");
s.text = "console.log('during');";
document.body.appendChild(s);
})();
</script>
<script>
console.log("after");
</script>
Output:
before
during
after
I actually found that using a LazyLoad plugin was perfect for this use case, i.e.
if (typeof jQuery === 'undefined')
LazyLoad.js('//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js', function() {
initialize();
});
else
initialize();
My website dynamically embeds an external Javascript file into the head tag. The external Javascript defines a global variable myString = "data". At what point does myString become accessible to Javascript within the website?
<html>
<head>
<script type="text/javascript">
myString = null;
external = document.createElement("script");
//externalScript.js is one line, containing the following:
//myString = "data";
external.setAttribute("src", "externalScript.js");
external.setAttribute("type", "text/javascript");
document.getElementsByTagName("head")[0].append(external);
alert(myString);
<script>
</head>
<body>
</body>
</html>
This code alerts null (when I thought it would alert "data") in Chrome and IE, even though the DOM has loaded in externalScript.js at this point. When is externalScript.js actually evaluated by the browser and at what point do I have access to the new value of myString?
You can access it when the onload event fires for that script element, like this:
external = document.createElement("script");
external.onload = function() { alert(myString); };
script.onreadystatechange= function () { //for IE, the special kid...
if (this.readyState == 'complete') alert(myString);
}
external.setAttribute("src", "externalScript.js");
external.setAttribute("type", "text/javascript");
document.getElementsByTagName("head")[0].appendChild(external);
This just attaches a function to run once that script has loaded, execute any code that depends upon the script in there. Correction to previous answer: as seanmonstar points out in comments (thanks!), you do indeed need an IE exception here again, because it's a bit "special"...