Dynamically load jQuery and use it in another function - javascript

function getArray() {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
var my_array = [];
j.addEventListener('load',function(){
// Some jQuery codes to fill my_array
});
return my_array;
}
I use above code to dynamically load jQuery in console, and then use jQuery to get some data from the DOM and store them in the array. However, it returns an empty array. I think it is because loading jQuery takes some time and the function gets returned before the jQuery is loaded and the jQuery codes are executed.
So before getArray() returns, I must make sure the jQuery codes have been executed. I've tried to put return my_array inside the addEventListener, of course it won't work because that way it will return the anonymous function. I can think of some ways to deal with this issue, like making the my_array a global so I don't have to return the function, or putting the jQuery loading codes to another loadjQuery function and call it before I execute the jQuery codes, but is there a better way to do it?

The problem is due to asynchronous call of loading jquery script.
The best way to do it will be, write a function to load a script and pass the callback function, then on successful load of script call your callback function, eg:
function loadScript(callback) {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
j.addEventListener('load',function(){
if(typeof(callback) == "function")
});
}
function getArray(){
var my_array = [];
// Some jQuery codes to fill my_array
return my_array;
}
loadScript(getArray)

Unfortunately, that can't be done. JavaScript is an event based single-thread asynchronous language. What you're trying to do can't work in that type of environment.
However, it's likely you simply need to load jQuery before processing this function (even using a simple <script> tag) to solve your issue. Otherwise, you're likely to encounter a very noticeable delay when calling the function due to the downloading & evaluating of the jQuery library. Another issue would be that if you call the function more then 1 time, you'll load jQuery again, and that might create a big big mess.
Alternatively, if you "insist" on using jQuery & have it only loaded once your function is called, you could return a Promise that will resolve to your array like this (This will require a supporting browser or some polyfills / promise library):
function getArray() {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
return new Promise(function(resolve, reject) {
j.addEventListener('load',function(){
var my_array = [];
// Some jQuery codes to fill my_array
resolve(my_array);
});
});
}

Related

add a function to a 3rd-party closure in javascript

Ok, I wouldn't think to do this in C#, but javascript is designed with much more flexibility in access.
there's a plugin like this
(function($)
{
...more stuff
var results={a:1,b:2} //this I need to modify
var someData={x:1}
send = function(){
//send results ajax
};
if(typeof beforeSend=='function')
beforeSend(someData) //hook to use results
})(jQuery)
So, in my own code, I have the function window.beforeSend = function(d){}
and it does have the someData which is in the scope I need to modify. But here's the question:
How can I modify the results var that's within the closure before it sends it.
I need to add
window.beforeSend = function(d){
window.quantumTunnelThroughScope.results['c']=1
}
The reason I need to do this is because I cannot modify the code of the plugin. Of course if I add the beforeSend within the closure, it works, but then I'm modifying the library which I'm not allowed to do in this case.
I've seen some awesome eval('this.xx' =function ) etc etc but I can't make it work.
EDIT: I clarified that actually it's a different var in the same scope that needs to be edited
No, there's no reasonable way for beforeSend to reach into that closure and modify results. results in the code presented is entirely private to code within that closure.
The unreasonable way to try to do it is to decompile and recompile the plugin function, via eval, and insert a call to a function before the beforeSend that lets us modify results:
(function($) {
$.run = function() {
// You mentioned "ajax," so let's make this
// asynchronous
setTimeout(function() {
var results = {
a: 1,
b: 2
};
var someData = { // Need to modify this
x: 1
};
send = function() {
//send results ajax
};
if (typeof beforeSend == 'function') {
beforeSend(someData); //hook to use results
}
console.log("in plugin, results = ", results);
}, 10);
};
})(jQuery)
window.modifyResults = function(d) {
return ["new", "results"];
};
window.beforeSend = function(r) {
r.c = 1;
};
jQuery.run = (function() {
// Function#toString, on nearly all browsers, returns the source
// code of he function (or something near to it) except on functions
// implemented in native code. We take that string and replace
// the "beforeSend(someData);" call with two calls, the first of
// which lets us modify the `results` variable. Then we use eval
// to turn that back into a function, and assign the result to
// where the plugin put its function originally.
return eval("(" + jQuery.run.toString().replace(
"beforeSend(someData);",
"results = modifyResults(results); beforeSend(someData);"
) + ")");
})();
jQuery.run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
But may or may not work, depending on how the plugin is written, as it lifts it out of its original scope and recompiles it in the scope of our function updating jQuery.run.
I think I'd prefer to take the hit of modifying the plugin. :-)
Note: In the above, I've used a "static" jQuery function. If the plugin you're replacing provides an instance function, the kind you can call on jQuery instances, e.g. the bar in $(".foo").bar(), you'll find it on jQuery.fn instead of jQuery:
jQuery.fn.pluginFunction = eval(...);

Executing imported Javascript functions in order

I want to execute 2 functions in a specific that I have imported from 2 other .js files that I have made. The function that needs to complete first takes a bit of time and the 2nd one starts before the first is ended and I need files the first one created for it to work. Here's basically what my .js looks like:
var pdfToPng = require("./pdfToPng.js");
var doStuffToPng = require("./doStufftoPng.js");
var pdfFilePath = process.argv[2];
var pngFilePath = pdftoPng.convert(PdfFilePath);//convert takes a path
//and makes a png and returns path
//to the png
doStuffToPng.doStuff(pngFilePath);
//I want "doStuff()" to start AFTER "convert()" is done.
Im pretty sure it has something to do with callbacks, but I'm a javascript noob and need help. I can get it to work with setTimeout(), but that seems like a "duct tape fix" to me. Is there some way more elegant?
Edit: some wonderful people wanted to help and asked to post this, the pdfToPng.js:
var spindrift= require('spindrift');//this is a node module
var fs = require('fs');
//Makes a png from pdf in pngFolder and returns the path to that png
exports.convert = function(path)
{
var pdf = spindrift(path);
var pathToPng = path.substring(0, path.length-4); //takes off the .pdf
pathToPng += "_out.png";
//this is spindrift's stuff, makes a png in dir pngFolder/pathToPng
pdf.pngStream(500).pipe(fs.createWriteStream("pngFolder/" + pathToPng));
return "pngFolder/" + pathToPng;
}
Welcome to the async world of javascript. The function callback though created synchronously is executed asynchronously. So you have to modify the code to get doStuff executed only after you know for sure that convert function has executed. You can find how this can be done # Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
if so, you need to implement your own callback,
- Open pdftoPNG.js
- modify convert function with one more parameter
function convert(PdfFilePath, finishConvert) {
//now just insert this line where you finally instead of return
//remove return yourpngpath; //or something, i assume
//add following in the place of return
finishConvert(yourpngpath);
}
Then Please call like this
var pdfToPng = require("./pdfToPng.js");
var doStuffToPng = require("./doStufftoPng.js");
var pdfFilePath = process.argv[2];
pdftoPng.convert(PdfFilePath,function(path){
if(path!="") {
doStuffToPng.doStuff(path);
}
});
You have to update your convert method to support callbacks/ promises.
Here is an example using Callbacks
exports.convert = function(path, fnCallback)
{
var pdf = spindrift(path);
var pathToPng = path.substring(0, path.length-4); //takes off the .pdf
pathToPng += "_out.png";
//this is spindrift's stuff, makes a png in dir pngFolder/pathToPng
pdf.pngStream(500).pipe(fs.createWriteStream("pngFolder/" + pathToPng));
if (fnCallback && typeof(fnCallback) === "function") {
fnCallback("pngFolder/" + pathToPng);
}
}
You'll see the following
A new parameter being passed in fnCallback
A check to make sure that the parameter is passed in and that it is a function
The fnCallback function gets called with the results passed in as a parameter
Removing of the return statement
Now when the convert method is called, after the long running process completes, the Callback method will get executed.
To call the modified convert method you now have to pass in a callback function
function myCallback(path){
// do something with the path
}
pdftoPng.convert(PdfFilePath,myCallback);

why javascripts functions are not loaded one after the other?

at the beginning of my html iam initializing variable "movieSRC" like this:
<script type="text/javascript">
var activeStep = getUrlVars();
var movieSRC;
cloud.getStepsForModules(activeStep.module, "UMCK").then(function(steps) {
var calculatedStep = activeStep.step - 1;
movieSRC = steps.steps[calculatedStep].movie;
});
at the end of my html i have some function for videojs.
<script type="text/javascript">
_V_("movie").ready(function() {
var myPlayer = this;
myPlayer.src(movieSRC);
</script>
</html>
I dont understand why a html part at the end will be loaded first and a one after the other at the beginning of my page as a second one. i need it opposite!
i need to keep videojs script at the end of the page because with video should be loaded first and just after that a script.
conclusion: movieSRC in myPlayer.src(movieSRC) is alwas empty :(, but why?
It is executing in order. First a couple of variables are declared, then a function is executed, then later that part at the end executes. What you're overlooking is that the function being executed is asynchronous. And no matter how fast it runs, it's highly unlikely that it will complete before the page finishes loading.
This is asynchronous:
cloud.getStepsForModules(activeStep.module, "UMCK")
So while it's executing everything else is loading. Anything that you need to happen after it executes will need to be invoked from its .then() handler:
cloud.getStepsForModules(activeStep.module, "UMCK").then(function(steps) {
var calculatedStep = activeStep.step - 1;
movieSRC = steps.steps[calculatedStep].movie;
// I don't know this plugin, but find your "myPlayer" here somehow
myPlayer.src(movieSRC);
});
Or perhaps this order will make more sense overall for the plugin:
_V_("movie").ready(function() {
var myPlayer = this;
cloud.getStepsForModules(activeStep.module, "UMCK").then(function(steps) {
var calculatedStep = activeStep.step - 1;
movieSRC = steps.steps[calculatedStep].movie;
myPlayer.src(movieSRC);
});
});
Most likely, the script at the end is being evaluated before the response returns from the cloud.getStepsForModules function above.
Also, there's a syntax error in the second script - you're missing the closing brace/paren/semicolon set.
Try putting the function that uses the result in the callback, like
<script type="text/javascript">
var activeStep = getUrlVars();
var movieSRC;
cloud.getStepsForModules(activeStep.module, "UMCK").then(function(steps) {
var calculatedStep = activeStep.step - 1;
movieSRC = steps.steps[calculatedStep].movie;
_V_("movie").ready(function() {
var myPlayer = this;
myPlayer.src(movieSRC);
});
});
JavaScript code does execute in the order it appears. However, there are functions that are asynchronous, which means they execute in a separate thread. Is it possible that your first function is asynchronous?
If you want to make sure that certain code is executed AFTER an asynchronous function, call that code in the asynchronous function's callback method.

Conditional javascript loading

For my backend I want to automatically load javascript files when it detects certain elements. Here is an example:
if($('.wysiwyg').length>0) {
include('javascript/ckeditor/ckeditor.js');
$(".wysiwyg").ckeditor();
}
But when I execute the code I get $(".wysiwyg").ckeditor is not a function because it seems the browser is still loading or parsing the javascript file that was included on the line before. If I put an alert popup right before the function it does work because it "pauzes" the script I guess and gives it time to load the file.
Is there a way I can know when the file is actually loaded so that the followed code can be executed?
EDIT:
Seems that I asked this question a bit too soon. I found out the e.onload property for a callback function that solved this problem. This is my function now if others might stumble upon the same problem:
function include(script, callback) {
var e = document.createElement('script');
e.onload = callback;
e.src = script;
e.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
}
if($('.wysiwyg').length>0) {
include('javascript/ckeditor/ckeditor.js', function() {
$(".wysiwyg").ckeditor();
});
}
Why not use the built in ajax-based getScript?
It also has a callback mechanism that allows you to execute some code only after the required script has been succesfully loaded :
function include(script,callback){
$.getScript(script, function() {
if(typeof callback == 'function')
callback.apply({},arguments);
});
}
and then you can use it in such a manner:
if($('.wysiwyg').length>0) {
include('javascript/ckeditor/ckeditor.js',function(){
$(".wysiwyg").ckeditor();
});
}
When you're using jQuery with promises you can use a modified version of the above code like so:
function include(srcURL) {
var deferred = new $.Deferred();
var e = document.createElement('script');
e.onload = function () { deferred.resolve(); };
e.src = srcURL;
document.getElementsByTagName("head")[0].appendChild(e);
return deferred.promise();
}
Then you can use the above code with a '$.when(include('someurl.js'))' call.
This will let you have
The global window context (which you need for CKEditor)
The ability to defer executing other code until the when resolves
A script that doesn't require a callback and a context for that to be passed because jQuery is handling that with the promises functionality it includes.
I hope this helps someone else who is looking for more than a callback, and multiple scripts to be loaded with jQuery's promises/deferred functionality.
You can also try YepNope - a conditional javascript loader
yepnope is an asynchronous conditional resource loader that's
super-fast, and allows you to load only the scripts that your users
need.
You can do it this way
$(document).ready(function()
{
if($('.wysiwyg').length>0) {
$('head').append('<script language="javascript" src="javascript/ckeditor/ckeditor.js"></script>');
$(".wysiwyg").ckeditor();
}
});
Modernizr can do this for you. See this MetaFlood article: Use jQuery and Modernizr to load javascript conditionally, based on existence of DOM element.

Execute javascript after external javascript document has loaded

I want to include a remote js file and then invoke a function once that has finished executing. I thought I could do something like this:
var sc = document.createElement('script');
sc.setAttribute('type', 'text/javascript');
sc.setAttribute('src', src);
sc.innerHTML = "alert('testing');"
parentNode.appendChild(sc);
Turns out, the alert('testing') gets wiped out be whatever is in the file. Is there anyway to do this?
This function will load library from scriptPath and execute passed handler function once script is loaded:
loadExternalScript : function(scriptPath, handler) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptPath;
script.charset = 'utf-8';
script.onload = handler;
head.appendChild(script);
}
First thing is, forget about using src and inner contents on the same script tag. It doesn't work in any general way, although John Resig gave it some thought in this blog post.
Second thing is, decide whether you want to load the script synchronously or asynchronously. If the script is large or long-running, you'll either want to do it asynchronously, or do it synchronously at the bottom of the page so as not to block rendering.
Your approach (dynamically appending script tags) will load and run it asynchronously, which means the code that should run after it's finished needs to go in a callback that fires when the script is finished. Setting this up isn't very straightforward, so I'd suggest either using jQuery and its ajax.getScript function, or just copy the getScript functionality right out of the jQuery source (lines 3473-3505 of jQuery 1.3.2).
If you want to avoid all of that, just load it synchronously. This is done by using document.write. Your provided example would look like:
document.write("<scr" + "ipt src='" + src + "' type='text/javascript'></script>");
// The script is guaranteed to have executed at this point
alert('testing');
Be sure to keep "script" split up like that, I'm not sure why but it's a quirk of JavaScript.
Have you tried just creating a second script element containing the code you want to run and adding that after the you've added the one that needs downloading?
Adding another
<script></script>
section after the first one should work. AFAIK you can't mix external and inline JS in one tag.
However I'm not sure whether putting code into "innerHTML" will work as expected. I'm interested to see whether it does.
You might be able to use the sc load event to figure out when that script has loaded then do some action.
example http://iamnoah.blogspot.com/2008/01/ie-script-load-event.html
I created this script for myself yesterday. It uses jQuery to load JavaScript files via AJAX and adds them in a script tag to the header, and then calls a callback function I pass it.
Has been working fine for me.
/**
* Fetches and executes JavaScript files from the server.
* #param files A list of files to load, or a single filename as a string.
* #param callback The function to call when the process is done. Passes a boolean success value as the only parameter.
* #param thisObject The calling object for the callback.
*/
window.include = function(files, callback, thisObject) {
var current_location = null;
var recursive = false;
if(!(thisObject instanceof Object)) {
thisObject = window;
}
if(files instanceof Array || files instanceof Object) {
if(files.length > 0) {
current_location = files.shift();
recursive = true;
}
else {
callback.apply(thisObject, [true]);
return;
}
}
else if(typeof files == 'string') {
current_location = files;
}
else {
callback.apply(thisObject, [false]);
return;
}
if((current_location instanceof String || typeof current_location == 'string') && current_location != '')
{
$.ajax({
type : 'GET',
url : current_location,
timeout : 5000,
success : function(data) {
var scriptTag = $(document.createElement('script'));
scriptTag.attr('type', 'text/javascript');
scriptTag.html(data);
$('head').append(scriptTag);
if(recursive) {
window.adlib.include(files, callback, thisObject);
}
else {
callback.apply(thisObject, [true]);
}
},
error : function() {
callback.apply(thisObject, [false]);
}
});
}
}

Categories