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)
})
}
Related
I want to add a element into the existing DOM to have the javascript code run.
I did this with YUI:
var scriptNode = Y.Node.create('<script type="text/javascript" charset="utf-8">alert("Hello world!");<\/script>');
var headNode = Y.one('head');
headNode.append(scriptNode);
It's successfully added to the DOM but it doesn't give me an alert.
Someone knows what the problem is?
I have no idea how YUI's Node.create() function works, so no comment on that. But a simple cross-browser script is:
window.onload = function() {
var s = document.createElement('script');
s.type = 'text/javascript';
var code = 'alert("hello world!");';
try {
s.appendChild(document.createTextNode(code));
document.body.appendChild(s);
} catch (e) {
s.text = code;
document.body.appendChild(s);
}
}
The try..catch block is necessary as most browsers like the first method but some don't and throw an error. The second method covers those. You can also simply eval the code, which is more or less equivalent and what some libraries do.
I found this function in the JQuery source, which seems to do what you want and feels a bit cleaner than the other approaches to me. But then again I am a JS beginner and probably don't see the details. Anyways, somebody might take something useful away from this.
function DOMEval( code, doc ) {
doc = doc || document;
var script = doc.createElement( "script" );
script.text = code;
doc.head.appendChild( script ).parentNode.removeChild( script );
}
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.
Is there any way to check if javascript file is already attached to the page by its file name.
For eg :
if ("path-to-script/scriptname.js") already embeded
{
call related function
}
else
{
Append '<script src="path-to-script/scriptname.js" type="text/javascript"> </script> '
call related function
}
Basically I dont want 1 script to be attached twice on the same page.
You might not always know what objects or functions a script contains in advance, in such cases you can search for script tags containing the desired src.
With jquery:
$("script[src*='"+scriptName+"']");
Without jquery:
document.querySelector("script[src*='"+scriptName+"']");
You'd need to test whether the actual function from the script file exists, like this:
if (window.function_name) {
// script loaded
} else {
// script not loaded
}
I agree with #zathrus though I think you should be using requirejs for things like this. The idea is that dependencies must be fetched before executing the code. The above method you are using may work but you can not guarantee anything.
RequireJS will beautifully maintain all the dependency loading. It is very easy to learn as well.
Simply check if the library is defined, otherwise import it:
if ( !jQuery ) {
var s = document.createElement('script');
s.type = 'text/javascript';
document.body.appendChild(s);
s.src = "path-to-script/scriptname.js";
void(0);
}
// call function
you really need a script loader.. because as you said you want it specific with the javascript resource filename and this is sort of your javascript files are depending to each other
www.headjs.com
www.modernizr.com
www.yepnopejs.com
I thought this will help you.
if (typeof scriptname== "undefined") {
var e = document.createElement("script");
e.src = "scriptname.js";
e.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
}
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();
I'm using a fairly simple system to load javascript dynamically:
include = function (url) {
var e = document.createElement("script");
e.src = url;
e.type="text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
};
Let's say I have a file test.js which has the following contents:
var foo = 4;
Now, in my original script, I want to use
include(test.js);
console.log(foo);
However, I get a 'foo has not been defined' error on this. I'm guessing it has to do with the dynamic script being included as the last child of the <head> tag. How can I get this to work?
It is because you have to wait for the script to load. It is not synchronous like you would think. This may work for you:
include = function (url, fn) {
var e = document.createElement("script");
e.onload = fn;
e.src = url;
e.async=true;
document.getElementsByTagName("head")[0].appendChild(e);
};
include("test.js",function(){
console.log(foo);
});
That is one problem, but it also takes time for the browser to download and parse the remote JS file — and it hasn't done that before you call console.log.
You need to delay things until the script has loaded.
This is easiest done by letting a library do the heavy lifting, jQuery has a getScript method that lets you pass a callback to run when the script has loaded.