Using require.js to load something before onload - javascript

I see this question was asked here
load javascript file before onload with requirejs
But the solution there doesn't fit my situation and so I'm wondering if there is a different solution. I can't build with deps and make my code come last because I'm making a library/utility, not an app.
I'm working on the WebGL-Inspector. It works by wrapping HTMLCanvasElement.prototype.getContext and if it sees a "webgl" it then does its thing wrapping the context and allowing you to inspect it.
The WebGL-Inspector works in 3 modes
As a browser extension
As a loader + large compiled script
As a loader + original source (many many scripts)
To use it in modes #2 or #3 above you just insert
<script src="core/embed.js"></script>
Somewhere in the top of your HTML. Because it loads synchronously~ish it will have wrapped HTMLCanvasElement.prototype.getContext before whatever scripts come after it.
These last 2 modes are mostly for debugging/development of the WebGL-Inspector itself. Especially mode #3 because we can edit the inspector's code and refresh the page immediately to see the result, no build step.
I'm in the process of switching to using AMD for all of the WebGL-Inspector. We're using this because we can use webpack to make #2 but still follow the same dev workflow allowing us it use mode #3 above just change the script tag to
<script src="core/require.js" data-main="core/embed.js"></script>
The problem is this no longer works because whatever other code unrelated to the WebGL-Inspector itself runs before core/embed.js has loaded and so calls someCanvas.getContext before we've had a chance to wrap it.
My current solution is sadly to hack in a delay of 1.5 seconds on whatever demo we're using
<script src="core/require.js" data-main="core/embed.js"></script>
<script>
// wait 1.5 seconds for embed.js to load and pray :(
window.onload = setTimeout(reallyRunWebGLCode, 1500);
...
</script>
The previous non-AMD loader doesn't have this async issue. Somehow it manages to load 60 or so .js files before window.onload fires. Is there a way to get require.js to do the same? Or maybe I need to write my own loader?
To put it another way, the issue is as it is now the user adds a single <script> line and makes no other changes to their code and it works. When they're done they remove the single script line.
Switching to AMD/require.js the user is now required to add a single script and re-write code. In my tests went from
window.onload = myapp;
to
require.config({ baseUrl: "/core" });
require("./embed", myapp);
It's minor but now the app is broken when you remove the <script> tag and has to be put back as it was. Requiring those changes is what I'm trying to avoid if possible. In fact with the original style you don't even have to remove the script tag, just run your app in an environment where the script doesn't exist, it will fail to load and your app will run as normal where as with the require method it will fail if the script doesn't exist.
I can require even more code
if (typeof require === 'function' && typeof define === 'function' and define.amd) {
require.config({ baseUrl: "/core" });
require("./embed", myapp);
} else {
window.onload = myapp;
}
But that's even uglier. We went from adding a single script to requiring modifying your app.
Actually it gets even worse in the app I'm currently testing with. It uses a different loader for itself. When I run require version of the code above it fails because in this case require runs myapp before window.onload (strange). I end up having to do this
var tryRunCount = 0;
function tryRunApp() {
++tryRunCount;
// check that tryRunApp has been called twice so we know
// both onload and require have returned
if (tryRunCount === 2) {
myApp();
}
}
window.onload = tryRunApp;
require.config({ baseUrl: "/core" });
require("./embed", tryRunApp);
That's way way more changes than I want to require users to make.

Related

Include a javascript file only once in Joomla

now, this question has been asked and answered successfully many times, yet none of the things i try work.
I have tried head.js & require.js libraries
I have also tried
if (!window.unique_name) {
unique_name = true;
//code here..
}
none of which I can get to work (the global variable is always undefined)
the script I am trying to include runs something like this:
//clock.js
clockyTick = function() {
//my code here
}
setInterval(clockyTick, 1000);
the apps that call this script, standalone, work fine.
only when both apps are included on the same page (via calls to PHP require()) they break.
Here is the cause of the problems (I think):
I am building custom web apps on a (Joomla) site and have the requirement of displaying two of my apps on the same page.
Both apps need the same .js file to operate correctly, which works fine when they run standalone, but as soon as both apps are running on the same page (in the admin section) the scripts conflict and stop each other from working
(the script in question is a dynamic clock script that grabs the specialised contents of a div and modifies it to something else)
I think the reason I cannot get aforementioned libraries to work, is the fact that they also are being included twice on the admin page.
is there any way around this, or do I have to bite the bullet and integrate a library into the main Joomla template? (meaning the library is uselessly loaded on every single page, yet only used on 3 of hundreds)
jQuery is also required, separately, on each app..but thankfully I am able to use noConflict to avoid problems there (not ideal)
The joomla way would be to instantiate the document inside your module and unset only the conflicting script as described in this question here just before you load the module's script:
1) get an instance if the document object and remove the js files (you
could do that in a plugin) :
<?php
//get the array containing all the script declarations
$document = JFactory::getDocument();
$headData = $document->getHeadData();
$scripts = $headData['scripts'];
//remove your script, i.e. mootools
unset($scripts['/media/system/js/mootools-core.js']);
unset($scripts['/media/system/js/mootools-more.js']);
$headData['scripts'] = $scripts;
$document->setHeadData($headData);
?>
Or in your case, I think you could try the dirty solution below inside your js files:
//1st module script
var unique_name;
if (unique_name == false || unique_name == null) {
unique_name = true;
//code here..
alert("Included 1st script");
}else{
//do nothing
alert("Not included 1st script")
}
//2nd module script
var unique_name;
if (unique_name == false || unique_name == null) {
unique_name = true;
//code here..
alert("Included 2nd script");
}else{
//do nothing
alert("Not included 2nd script")
}
Here is a DEMO
If you are having conflicts with PHP require(), you can try require_once(). However, as mentioned, that’s not the Joomla way of doing things.

Simplest way to selectively activate generic jQuery/javascript modules for a specific page?

It's oft recommended optimization to consolidate your javascript into fewer parts and reduce the number http requests. On a smaller site you could easily arrive at structure like:
/js/jQuery-xx.js
/js/plugins.js
/js/app.js
Even in a modest site (with just some jQuery polish) that app.js can get pretty heavy. For organization it's easy to break apart the implementations into logcal bits:
..
/js/app-global.js
/js/app-contact.js
/js/app-products.js
/js/app-splash.js
Now as separate files it's trivial to require them for a specific context, though their individual sizes are quite small, around 1–3 kb a piece, so it occurs to me it'd be pretty smart to recombine them back into a single file simply served as app.js.
But it occurs to me that in app.js file with about 20 or so jQuery selectors and misc javascript that goes unused on most pages. Seems like wasted CPU cycles to have these jQuery selectors firing when they target elements that don't exist in most contexts.
Which is the more pressing optimization: • reducing http requests? • reducing unused jQuery selectors?
I'm using Chrome's Audit tool to profile and it recommends fewer network request for javascript files. Any guidelines for measuring bloat in jQuery.ready(…);?
Update
Weighing the evidence presented the simplest thing to do is to serve the implementation code as a single file and then activate different modules using a routing system. In my case I just hooked into the simple php router that I've already built:
root.php
<script type="text/javascript">
var myApp = {
modules: {}
bootstrap: function()
{
<?php
if ( $pageId == 'contact' )
echo 'myApp.modules.contact();';
if ( $pageId == 'index' )
echo 'myApp.modules.splash();';
if ( preg_match( '~^help/~iu', $pageid ) )
echo 'myApp.modules.faq();';
?>
}
}
</script>
<script type="text/javascript" src="/js/jquery1.9.1.js"></script>
<script type="text/javascript" src="/js/jquery-plugins.js"></script>
<script type="text/javascript" src="/js/myApp.js"></script>
myApp.js
$(document).ready(function()
{
myApp.bootstrap();
//… global jQuery stuff …
});
// Define modules
myApp.modules.splash = function() {
$('#splash').click(
//…etc…
);
}
myApp.modules.faq = function() {
$('.faq').show(
//…etc…
);
}
myApp.modules.contact = function() {
$('.contact').bind(
//…etc…
);
}
Serving your static content in as few files as possible is always the recommendation. If you're worried about executing on content that doesn't exist then you should organize your code in such a way that it doesn't all automatically fire on page load, but fires when you tell it to. That way you only execute the code you want to on any given page. The easiest way would be to wrap each pages content in it's own function:
window.aboutPage = function() {
$('').css(); //some stuff
}
then at the bottom of the about page, after your load all your external scripts fire off the appropriate function
<script>$(window.aboutPage) //fire aobutPage stuff on ready</script>
There are frameworks that can accomplish this modularization of code also
If you're using modules (I'm guessing RequireJS?) you can avoid putting a bunch of functions on the global/window object by implementing a simple "router". Have your main app module define a hash of "routes" something like:
var routes = {
'/contact': contact,
'/products': products,
'/splash': splash
};
Where each of contact, products, and splash, is the function you want to call "on DOM ready". Then elsewhere in your same app module pass the appropriate function as jQuery's ready handler something like:
$(routes[window.location.pathname])
Note that:
You might need to do something fancy to make sure you're only using the part of the URL/path that you really want.
You could drop the jQuery dependency (if you want) and use RequireJS's domReady. They apparently work about the same.

how should I write my define to work with curl.js?

I'm reading Addy Osmani's excellent blog post about writing AMD modules. I start with a simple chunk of js that I lifted from his post:
define('modTest', [],
// module definition function
function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
I took out the dependencies on foo and bar. Just want a simple object that logs to the console.
So I save that in /js/modTest.js and then try to load it:
curl(['/js/modTest.js'])
.then(function(mt) {
console.log("Load complete");
console.log("mt:");
console.log(mt);
mt.doStuff()
}, function(ex) {alert(ex.message);})
Result: error: Multiple anonymous defines in URL. OK that didn't work. Tried adding in a namespace: define('myCompany/modTest', [],, same result. Tried adding an empty string in the dependency array, same result.
Also tried curl(['modTest.js'], function(dep){console.log(dep)}); with the same result.
Is the code in Addy's blog post incorrect? Am I doing something wrong? Maybe a bug in curl?
Update 5/24: I ditched curl.js in favor of require.js. Zero odd errors, very little work to change over. I did have to deal with amdefine a bit to get my code running client and server side (one object is in both places, so grunt had to be configured to take care of that). My defines generally look like:
define(->
class AlphaBravo
...
And never have any trouble loading.
You asked curl() to fetch a module called "/js/modTest.js". It found the file and loaded it and found a module named "modTest", so it complained. :) (That error message is horribly wrong, though!)
Here's how you can fix it (pick one):
1) Remove the ID from your define(). The ID is not recommended. It's typically only used by AMD build tools and when declaring modules inside test harnesses.
2) Refer to the module by the ID you gave it in the define(). (Again, the ID is not recommended in most cases.)
curl(['modTest'], doSomething);
3) Map a package (or a path) to the folder with your application's modules. It's not clear to me what that would be from your example since modTest appears to be a stand-alone module. However, if you were to decide to organize your app's files under an "app" package, you packages config might look like this:
packages: [ { name: 'app', location: 'app' } ]
Then, when you have code that relies on the modTest module, you can get to it via an ID of "app/modTest".
curl(['app/modTest'], doSomething);
I hope that helps clear things up!
Fwiw, Addy's example could actually work with the right configuration, but I don't see any configuration in that post (or my eyes missed it). Something like this might work:
packages: [ { name: 'app', location: '.' } ]
-- John
I've just had a similar problem which turned out to be the include order I was using for my other libraries. I'm loading handlebars.js, crossroads.js, jquery and a few other libraries into my project in the traditional way (script tags in head) and found that when I place the curl.js include first, I get this error, but when I include it last, I do not get this error.
My head tag now looks like this:
<script type="text/javascript" src="/js/lib/jquery.js"></script>
<script type="text/javascript" src="/js/lib/signals.js"></script>
<script type="text/javascript" src="/js/lib/crossroads.js"></script>
<script type="text/javascript" src="/js/lib/handlebars.js"></script>
<script type="text/javascript" src="/js/lib/curl.js"></script>
<script type="text/javascript" src="/js/main.js"></script>
You have a problem with your define call. It is NAMED
See AMD spec for full story on how to write defines, but here is what I would expect to see in your js/modTest.js file:
define(/* this is where the difference is */ function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
Now, the boring explanation:
CurlJS is awesome. In fact, after dealing with both, RequireJS and CurlJS, I would say CurlJS is awesome-er than RequireJS in one category - reliability of script execution ordering. So you are on the right track.
On of the major things that are different about CurlJS is that it uses "find at least one anonymous define per loaded module, else it's error" logic. RequireJS uses a timeout, where it effectively ignores cases where nothing was defined in a given file, but blows up on caught loading / parsing errors.
That difference is what is getting you here. CurlJS expects at least one anonymous (as in NOT-named) define per loaded module. It still handles named defines fine, as expected. The second you move the contents of "js/modTest.js" into inline code, you will have to "name" the define. But, that's another story.

How can I combine my JavaScript files and still have my callbacks wait for a ready state?

I have lots of functions and event handlers that are split across multiple javascript files which are included on different pages throughout my site.
For performance reasons I want to combine all of those files into 1 file that is global across the site.
The problem is I will have event handlers called on elements that won't necessarily exist and same function names.
This is an example of a typical javascript file...
$(document).ready(function(){
$('#blah').keypress(function(e){
if (e.which == 13) {
checkMap();
return false;
}
});
});
function checkMap() {
// code
}
function loadMap() {
// code
}
I would need to seperate this code into an object that is called on that specific page.
My thoughts are I could re-write it like this:
(function($) {
$.homepage = {
checkMap: function(){
// code
},
loadMap: function(){
//code
}
};
})(jQuery);
And then on the page that requires it I could call $.homepage.checkMap() etc.
But then how would I declare event handlers like document.ready without containing it in it's own function?
First of all: Depending on how much code you have, you should consider, if serving all your code in one file is really a good idea. It's okay to save http-requests, but if you load a huge chunk of code, from which you use 5% on a single page, you might be better of by keeping those js files separated (especially in mobile environments!).
Remember, you can let the browser cache those files. Depending on how frequent your code changes, and how much of the source changes, you might want to separate your code into stable core-functionality and additional .js packages for special purposes. This way you might be better off traffic- and maintainance-wise.
Encapsulating your functions into different objects is a good idea to prevent unnecessary function-hoisting and global namespace pollution.
Finally you can prevent calling needless event handlers by either:
Introducing some kind of pagetype which helps you decide calling only the necessary functions.
or
checking for the existence of certain elements like this if( $("specialelement").length > 0 ){ callhandlers}
to speed up your JS, you could use the Google Closure Compiler. It minifies and optimizes your code.
I think that all you need is a namespace for you application. A namespace is a simple JSON object that could look like this:
var myApp = {
homepage : {
showHeader : function(){},
hideHeader : function(){},
animationDelay : 3400,
start : function(){} // the function that start the entire homepage logic
},
about : {
....
}
}
You can split it in more files:
MyApp will contain the myApp = { } object, maybe with some useful utilities like object.create or what have you.
Homepage.js will contain myApp.homepage = { ... } with all the methods of your homepage page.
The list goes on and on with the rest of the pages.
Think of it as packages. You don't need to use $ as the main object.
<script src="myapp.js"></script>
<script src="homepage.js"></script>
<-....->
<script>
myApp.homepage.start();
</script>
Would be the way I would use the homepage object.
When compressing with YUI, you should have:
<script src="scripts.min.js"></script>
<script>
myApp.homepage.start();
</script>
Just to make sure I've understood you correctly, you have one js file with all your code, but you want to still be in control of what is executed on a certain page?
If that is the case, then the Terrific JS framework could interest you. It allows you to apply javascript functionality to a module. A module is a component on your webpage, like the navigation, header, a currency converter. Terrific JS scans the dom and executes the js for the modules it finds so you don't have to worry about execution. Terrific JS requires OOCSS naming conventions to identify modules. It's no quick solution to your problem but it will help if you're willing to take the time. Here are some more links you may find useful:
Hello World Example:
http://jsfiddle.net/brunschgi/uzjSM/
Blogpost on using:
http://thomas.junghans.co.za/blog/2011/10/14/using-terrificjs-in-your-website/
I would use something like YUI compressor to merge all files into one min.js file that is minified. If you are looking for performance both merging and minifiying is the way to go. http://developer.yahoo.com/yui/compressor/
Example:
Javascript input files: jquery.js, ads.js support.js
run yui with jquery.js, ads.js, support.js output it into min.js
Javascript output files: min.js
then use min.js in your html code.

How to create AJAX semi-synchronous behaviour

I spent the better part of last month beating my head against the wall before I came up with an easy way to dynamically load, and chain together HTML canvas classes which are stored on the server, but, obviously, initialized on the client (harder than it sounds when the ordering is important in an asynchronous environment).
I was wondering if someone could help me find a way to load simple javascript scripts. Lets define a load('foo.js') function which instructs the client to load script foo.js from the server and execute it as javascript code.
Given the three files, stored on the server:
A.js
a = 10;
B.js
load('A.js');
b = a + 10;
C.js
load('B.js');
c = b + 10;
If the client issues the command load('C.js'); what's the easiest/most reliable way to implement this. One idea I had was to scan the code serverside and return all the scripts at once. This requires the minimal amount of php requests. However, if the client has already requested C.js before, the script should exist client side, and this would be inneficient, especially if C.js and all its dependent files are large. Another option I considered was to wrap all of these serverside scripts in an object like so, for C.js above:
{
depenencies: ['B.js'] ,
code : 'c.age = b.age + 10;'
}
I just don't know how to 'pause' execution of script C.js after the load('B.js') statement, and then resuming it after B.js has been loaded.
EDIT Thanks to redsqaure for suggesting yepnope and requirejs. Unfortunately, I do not like them for several reasons. For one, requirejs is difficult (I am sure I will come under criticism for this one). My main gripe with this is that, if it is so difficult to learn, I might as well recreate it myself, learning it in the process, AND having greater control over it. Second, it requires you to change your style of writing. Switching to Dojo and having to use dojo.declare("ClassName", [ParentA,ParentB], {...}); to declare classes is one thing, but wrapping every snippet of code in require(['A','B',...], function(){}); is another. Finally, I don't know how simple it will be to instruct where to look for files. I want the user to be able to define a 'PATH' variable server side, and have the search occur in each of the folders/subfolders of the 'PATH'
Depends on how optimized you want it to be. Either you can go the route of synchronous XHR or use a callback (async and recommended). If you were to go the second route your code would look something like:
// Say C.js is dependent on A.js and B.js..
load(["A.js","B.js"], function() {
// code goes here
});
EDIT
Taking a second look after you feedback what you want is somewhat possible, but would be brittle and hard to write in javascript. Below i have a sample/untested implementation of a dependency loader where a file can only have one call to load("file.js") possible. This would be more complex for multiple possible dependencies. Also I'm assuming these files are coming from the same domain.
// Usage: load("A.js")
// A.js -> B.js -> C.js
window.load = (function() {
var loaded = {};
return function(str, /* internally used */ callback) {
if(!loaded[str]) {
loaded[str] = true;
$.get(str, function(data) {
var matches = data.match(/load\(['|"](.*)["|']\)/);
if(matches.length > 1) { // has deps
window.load(matches[1], function() {
window.eval(data);
if(!!callback) callback();
});
} else { // no deps
window.eval(data);
}
});
}
}
})();
Why not look into a script loader like yepnope.js or require.js

Categories

Resources