I need to call a function in an external ".js" file from another ".js" file, without referencing the external file in the <head> tag.
I know that it is possible to dynamically add an external ".js" file to the which allows access to that file, i can do that like so...
var AppFile = "test/testApp_1.js";
var NewScript=document.createElement('script');
var headID = document.getElementsByTagName("head")[0];
NewScript.src = AppFile;
headID.appendChild(NewScript);
However...
this is no use to me as the external files need to be stand-alone files that run start-up procedures on...
$(document).ready(function()
{...}
so adding the full file dynamically has an unwanted affect. Also, i cannot pre-reference the external file in the <head> tag as it needs to be dynamic.
So, this external file "test/testApp_1.js" contains a function that returns a string variable...
function setAppLogo(){
var LogoFile = "test/TestApp_1_Logo.png";
return LogoFile;
}
I need access to either this function, or I could store the string as a global var in the external file... either way is fine, I just need access to the value in LogoFile without loading the whole external file.
This one has had me stumped for a few hours now so any ideas would be greatly appreciated.
You might benefit from having some sort of app.js file that contains global variables/values that you will want to use from lots of places. You should include this .js file on every page (and maybe minify it/concatenate it with other js if you want to be clever and improve performance). Generally these globals should be attached to some object you create such as var APPNAME = { }; with variables/functions on it that will be used from many places.
Once you have this, then the external '.js' file that you want to load, and the one you are currently in, can both access the global APPNAME variable and all its attributes/functions and use them as desired. This may be a better approach for making your javascript more modular and separatable. Hope this helps.
You want to load the file once jQuery has loaded using ajax, and then run the related script in the successful ajax function.
See jQuery's getScript function: http://api.jquery.com/jQuery.getScript/
$(document).ready(function(){
$.getScript("http://domain.com/ajax/test.js", function(data, textStatus, jqxhr) {
console.log(data); //data returned
console.log(textStatus); //success
console.log(jqxhr.status); //200
console.log('Load was performed.');
//run your second script executable code here
});
});
It is possible to load the whole script through XHR (e.g. $.get in jQuery) and then parse it, perhaps using a regular expression, to extract the needed part:
$.get('pathtoscript.js', function(scriptBody) {
var regex = /function\s+setUpLogo\(\)\s*\{[^}]+}/g;
alert(scriptBody.match(regex)[0]); // supposed to output a function called
// 'setUpLogo' from the script, if the
// function does not have {} blocks inside
});
Nevertheless, it shall be noted that such an approach is highly likely to trigger maintenance obstacles. Regular expressions are not a best tool to parse JavaScript code; the example above, for instance, will not parse functions with nested {} blocks, which may well exist in the code in question.
It might be recommended to find a server-side solution to the problem, e.g. adding necessary script path or its part before the page is sent to browser.
I'm not sure this is a good idea but you can create an iframe and eval the file inside its 'window' object to avoid most of the undesired side effects (assuming it does not try to access its parent). Then you can access whatever function/variable you want via the iframe's window object.
Example:
function loadSomeJsInAFrame(url,cb) {
jQuery.get(url,function(res) {
iframe = jQuery('<iframe></iframe>').hide().appendTo(document.body);
iframe[0].contentWindow.eval(res);
if(cb) cb(iframe[0].contentWindow);
},'text');
}
loadSomeJsInAFrame('test/testApp_1.js',function(frameWindow) {
console.log(frameWindow.setAppLogo());
jQuery(frameWindow.frameElement).remove();
});
This will not guarantee that the sript in the file can not mess with your document, but not likely if it comes from a trusted source.
Also, don't forget to remove your iframe after you get what you need from it.
Ok, thanks everybody for all the input but i think that what I was trying to do is currently not possible, i.e. accessing a function from another file without loading that file.
I have however found a solution to my problem. I now query my server for a list of apps that are available, i then use this list to dynamically build the apps in a UI. when an app is then selected i can then call that file and the functions within. Its a bit more complex but its dynamic, has good performance and, it works. Thanks again for the brainstorming! ;)
It may be possible with the help of Web Workers. You would be able to run your script you've wanted to inject in kinda isolated environment, so it won't mess up your current page.
As you said, it is possible for setAppLogo to be global within "test/testApp_1.js", so I will rely on this statement.
In your original script you should create a worker, which references to a worker script file + listen to messages that would come from the worker:
var worker = new Worker('worker.js');
worker.onmessage = function (e) {
// ....
};
Then, in the worker (worker.js), you could use special function importScripts (docs) which allows to load external scripts in worker, the worker can also see global variables of these scripts. Also there is a function postMessage available in worker to send custom messages back to original script, which in turn is listening to these messages (worker.onmessage). Code for worker.js:
importScripts('test/testApp_1.js');
// "setAppLogo" is now available to worker as it is global in 'test/testApp_1.js'
// use Worker API to send message back to original script
postMessage(setAppLogo());
When it invokes you'll get the result of setAppLogo in you listener:
worker.onmessage = function (e) {
console.log(e.data); // "test/TestApp_1_Logo.png"
};
This example is very basic though, you should read more about Web Workers API and possible pitfalls.
Related
I believe that I've made a terrible mistake. I have a global JS file that I include via script tag on every single JSP page.
<script type="text/javascript" src="/myGlobalJS.js" ></script>
In that JS file I make a call to get JSON; but I only want that executed once. Right now it executes every time a new JSP page loads.
var myObj = (function(){
var myData = {};
return{
setData:function(data){
myData = data.response;
}
};
})();
(function(){
$.getJSON(jsonUrl)
.done(function(data){
myObj.setData(data.response.data);
});
})();
How can I correct this elegantly?
Update: I can see the error of my thinking now. I'm thinking about these JSP pages as an application and when the app loads then it should execute getting this JSON file only one time. There is no persisted UI state across these JSP pages. So I think that I'll have to load the file and call it on every page I need it, unfortunately. Is this true?
I've found the answer here:
Global vars across pages
HTML5 supports sessionStorage, which will store variables as long as the session is in use. Only shortcoming is that it doesn't seem to fully support storing objects across all browsers yet. But it's definitely a nifty solution.
I could not find out why this part of my code doesn't work:
var loc = window.location.pathname;
var dir = loc.substring(0, loc.lastIndexOf('/'));
var FilePath = dir + "/" + FileName;
var file = new File("FilePath");
var reader = new FileReader();
reader.onload = function(e) {FileText = reader.result;}
reader.readAsText(file);
alert (FileText);
The intention is, I think, clear: FilePath contains the filename of a file (passed via parameter FileName) containing logging data (a plain ASCII text file, with one line per log entry), the file is located in the same directory as the web page is (loc), and I want to embed the text into my html document somewhere further down the code.
Since the logged lines are of different kinds (e.g. errors, warning, other blabla ...) each line needs to be parsed and processed.
I intended to split FileText into an array, and loop through it. I cannot, however, get readastext to work. Though, according to FireFox debugger, FilePath does contain the correct string, I get the NS_ERROR_FAILURE, which I, according to the sparse documentation I found about it, must consider to be the 'zillionst stupid way to say "File not found".
I found tons of other posts from people messing with the file API, and tons of snippets taken from the mozilla docs which don't help me out. I read that there are maybe other ways to read a file, e.g. through Ajax, JQuery ... but before I go that way ... is it really, really absolutely impossible to accomplish what I want using just plain JavaScript, and if it is possible, who can provide a code snippet?
Thanks very much,
Armin.
You have quotes around "FilePath":
var file = new File("FilePath");
This means it's going to try to load a file with the path "FilePath".
Pretty sure this is what you want:
var file = new File(FilePath);
On the other hand, Quentin is absolutely right. You're not going to be able to access local files if this code is running in a web page.
Since you are using window.location.pathname i assume that you are in a browser and want to use that code to "navigate" to files on the server based on the URL path.
I think your whole approach is wrong, and it would be a security issue to have something like that possible.
The File API can be used strictly on files selected by the user, and not on any file. The MDN description is self-explanatory:
Using the File API, which was added to the DOM in HTML5, it's now possible for web content to ask the user to select local files, then read the contents of those files. This selection can be done by either using an HTML element, or by drag and drop.
Yes, you can specify a path to any file in the File constructor method, but that doesn't mean you can access any file. Another excerpt from MDN:
This only works from privileged code, so web content can't do it. This protects users from the inherent security risks associated with allowing web content free access to the contents of their disks. If you pass a path to the File constructor from unprivileged code (such as web content), an exception will be thrown.
This code did the trick:
var objXMLhttp = new XMLHttpRequest()
objXMLhttp.open("GET",strFileName,true);
objXMLhttp.send();
and, in addition, an objXMLhttp.onreadystatechange=function() ... event handler must be implemented, which is the code acutally receiving the data, like so:
objXMLhttp.onreadystatechange=function()
{
if (objXMLhttp.readyState==4 && objXMLhttp.status==200)
{
var arrContents = objXMLhttp.responseText.split("\n"); // gotcha!
....
}
}
Easy win is to do an ajax request for the path...you should have your page that contains the js and files served by a web server. Any other way needs other priveleges and if you were to get files from a users computer without an uploader or anything like that would be a security breach
objective
I need a function that simply takes all the code from another file and pastes the code in the calling file.
possible solution
a function that works like the include() function in PHP.
reason
When I had hosting, I used the php function include("filename.html") to include things like headers and footers, in all the files on the website. This made life a lot easier!
Now I don't have hosting, because I am working on another site, and I am using Github Pages and thus, I can't use PHP. I need to use only HTML, JS and jQuery etc. So, I need a function that simply takes all the code from another file and pastes the code in the calling file.
Already tried
load() in jQuery.
<pre><script>
$(document).ready(function(){
$("#topbar").load("menubar.html");
});
</script></pre>
This question. I tried the accepted answer, but that didn't work for me.
Kindly help me with this issue.
You should consider setting up a build environment where you can compile your content locally before publishing it. This way, you can organize your code in different files (like in your case, with a header/footer that will always be included with different content files), compile locally to have the files automatically combined into a publish directory, and upload that instead.
This way, instead of e.g. sending 3 requests for a header, content and footer file, the header and footer are pre-compiled into the content file which can then be served with 1 request.
Personally I use Grunt as a build tool for purely static sites, together with a concatenation task (such as grunt-contrib-concat). There are several tutorials on the Grunt website, but you can see an example of how to configure a task for your specific problem here:
https://stackoverflow.com/a/12749861/351435
I do something like that :
var template = {
get: function (url, callback) {
if (this[url]) return callback(null, this[url]);
var self = this;
$.ajax({
"url": url,
"type": "GET"
})
done(function (html) {
self[url] = html;
callback(null, html);
})
.fail(callback);
}
};
after you just need to do that :
template.get("/menu.html", function (err, html, reason) {
if (err) return alert('An error is append : '+reason);
$("#topbar").html(html) // or append
});
I am assuming your scripts run in a browser.
Try the $.getScript function in jQuery.
`$.getMyScript("script.js", function(){
alert("Script executed.");
});`
Depending on how complex you want your solution to get, you could also look at http://requirejs.org/ for incorporating files/scripts/modules.
I believe this question is answered in full on How do I include a JavaScript file in another JavaScript file?.
I am creating a page which calls dynamic content from the web with javascript and obviously run into a snag with the SOP (same origin policy) I know this would not work on a production page as the calls would need to be checked on the server but I have it at a point where it practically works but I cant access the data. I would really like to know if it is possible.
The code below adds the URL to the file:
var url = 'http://theSite.com/?query=' + userInput + '&othercode';
var script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', "application/json");
script.setAttribute('id', "special");
document.getElementsByTagName('body')[0].appendChild(script);
This adds the following to my html:
<script src="http://theSite.com/?query=userInput&othercode" type="application/json" id="special"></script>
Now the problem:
How do I access the resulting data?
I get a JSON script but dont know how to attach it to a variable in Javascript or use it!
the code arrives in this basic format:
{"count":5,"results":[{"name":"value","id":"value2", ..... }], ... "code":200}
How do I access the data or assign it to a variable?
(I have not yet tried jQuery and know it will probably be easier but if anyone can help with this code that would be great rather than trying to do this all again in jQuery).
You need to have control of the service you're calling (theSite.com). One option is to make it return JSONP instead of JSON:
dataCallback({"count":5,"results":[{"name":"value","id":"value2", ..... }], ... "code":200});
Or, make your script inside an iframe that is served from theSite.com, and use postMessage to communicate its result back to your window.
Otherwise... well, you're doing exactly what SOP is looking to prevent, so the chances are grim, AFAIK.
Oh, one more option: make an AJAX call to your server, which will make the service call and pass the data to you. Servers are not restricted like clients are.
With a single page app, where I change the hash and load and change only the content of the page, I'm trying to decide on how to manage the JavaScript that each "page" might need.
I've already got a History module monitoring the location hash which could look like domain.com/#/company/about, and a Page class that will use XHR to get the content and insert it into the content area.
function onHashChange(hash) {
var skipCache = false;
if(hash in noCacheList) {
skipCache = true;
}
new Page(hash, skipCache).insert();
}
// Page.js
var _pageCache = {};
function Page(url, skipCache) {
if(!skipCache && (url in _pageCache)) {
return _pageCache[url];
}
this.url = url;
this.load();
}
The cache should let pages that have already been loaded skip the XHR. I also am storing the content into a documentFragment, and then pulling the current content out of the document when I insert the new Page, so I the browser will only have to build the DOM for the fragment once.
Skipping the cache could be desired if the page has time sensitive data.
Here's what I need help deciding on: It's very likely that any of the pages that get loaded will have some of their own JavaScript to control the page. Like if the page will use Tabs, needs a slide show, has some sort of animation, has an ajax form, or what-have-you.
What exactly is the best way to go around loading that JavaScript into the page? Include the script tags in the documentFragment I get back from the XHR? What if I need to skip the cache, and re-download the fragment. I feel the exact same JavaScript being called a second time might cause conflicts, like redeclaring the same variables.
Would the better way be to attach the scripts to the head when grabbing the new Page? That would require the original page know all the assets that every other page might need.
And besides knowing the best way to include everything, won't I need to worry about memory management, and possible leaks of loading so many different JavaScript bits into a single page instance?
If I understand the case correctly, you are trying to take a site that currently has pages already made for normal navigation, and you want to pull them down via ajax, to save yourself the page-reload?
Then, when this happens, you need to not reload the script tags for those pages, unless they're not loaded onto the page already?
If that is the case, you could try to grab all the tags from the page before inserting the new html into the dom:
//first set up a cache of urls you already have loaded.
var loadedScripts = [];
//after user has triggered the ajax call, and you've received the text-response
function clearLoadedScripts(response){
var womb = document.createElement('div');
womb.innerHTML = response;
var scripts = womb.getElementsByTagName('script');
var script, i = scripts.length;
while (i--) {
script = scripts[i];
if (loadedScripts.indexOf(script.src) !== -1) {
script.parentNode.removeChild(script);
}
else {
loadedScripts.push(script.src);
}
}
//then do whatever you want with the contents.. something like:
document.body.innerHTML = womb.getElementsByTagName('body')[0].innerHTML);
}
Oh boy are you in luck. I just did all of this research for my own project.
1: The hash event / manager you should be using is Ben Alman's BBQ:
http://benalman.com/projects/jquery-bbq-plugin/
2: To make search engines love you, you need to follow this very clear set of rules:
http://code.google.com/web/ajaxcrawling/docs/specification.html
I found this late and the game and had to scrap a lot of my code. It sounds like you're going to have to scrap some too, but you'll get a lot more out of it as a consequence.
Good luck!
I have never built such a site so I don't know if that is nbest practice, but I would put some sort of control information (like a comment or a HTTP header) in the response, and let the loader script handle redundancy/dependency cheching and adding the script tags to the header.
Do you have control over those pages being loaded? If not, I would recommend inserting the loaded page in an IFrame.
Taking the page scripts out of their context and inserting them in the head or adding them to another HTML element may cause problems unless you know exactly how the page is build.
If you have full control of the pages being loaded, I would recommend that you convert all your HTML to JS. It may sound strange but actually, a HTML->JS converter is not that far away. You could start of with Pure JavaScript HTML Parser and then let the parser output JS code, that builds the DOM using JQuery for example.
I was actually about to go down that road for a while ago on a webapp that I started working on, but now I handed it over to a contractor who converted all my pure JS pages into HTML+JQuery, whatever makes his daily work productive, I dont care, but I was really into that pure JS webapp approach and will definitely try it.
To me it sounds like you are creating a single-page app from the start (i.e. not re-factoring an existing site).
Several options I can think of:
Let the server control which script tags are included. pass a list of already-loaded script tags with the XHR request and have the server sort out which additional scripts need to be loaded.
Load all scripts before-hand (perhaps add them to the DOM after the page has loaded to save time) and then forget about it. For scripts that need to initialize UI, just have each requested page call include a script tag that calls a global init function with the page name.
Have each requested page call a JS function that deals with loading/caching scripts. This function would be accessible from the global scope and would look like this: require_scripts('page_1_init', 'form_code', 'login_code') Then just have the function keep a list of loaded scripts and only append DOM script tags for scripts that haven't been loaded yet.
You could use a script loader like YUI Loader, LAB.js or other like jaf
Jaf provides you with mechanism to load views (HTML snippets) and their respective js, css files to create single page apps. Check out the sample todo list app. Although its not complete, there's still a lot of useful libraries you can use.
Personally, I would transmit JSON instead of raw HTML:
{
"title": "About",
"requires": ["navigation", "maps"],
"content": "<div id=…"
}
This lets you send metadata, like an array of required scripts, along with the content. You'd then use a script loader, like one of the ones mentioned above, or your own, to check which ones are already loaded and pull down the ones that aren't (inserting them into the <head>) before rendering the page.
Instead of including scripts inline for page-specific logic, I'd use pre-determined classes, ids, and attributes on elements that need special handling. You can fire an "onrender" event or let each piece of logic register an on-render callback that your page loader will call after a page is rendered or loaded for the first time.