I'm having real problems with my application due to the order I render the .js scripts. For example in http://jsfiddle.net/qCAW3/1, the items will not become draggable until I change the order in which the js scripts are loaded.
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
Can anyone shed some light as to what causes this. If I change the order to get the draggable items to work, then other javascript parts of my application stop functioning.
Dependencies, that's when some code depends on other code. And on a webpage, JavaScript files are executed in the order they are declared. Which is important for dependencies!
Lets say you have two .js files that look like this:
// person.js
var Person = function(name) {
this.name = name;
};
Person.prototype.greet = function() {
alert("Hi, " + name);
}
And another file:
// roster.js
var roster = [
new Person("Alex"),
new Person("Eddy")
];
for (var i = 0; i < roster.length; i++) {
roster[i].greet();
}
If we load roster.js first, it will explode. The Person constructor would not yet be available, and calling it anyway would raise an expection.
If you load person.js, and then afterward load roster.js, then you have Person available when the roster code runs.
In your case, you need to load jQuery before any code that uses jQuery, for exactly the same reason.
Typically when one file depends on another, it has to be loaded after. That's in a very general sense. Since your problem is centered around load order and timing, I'd suggest looking here. Basically, make sure not to execute anything before all files being depended upon finish loading.
Related
I would like to use Plupload in an Angular2 component and access the Plupload JavaScript file from a CDN. I want it specific to a component so that it is not downloaded if it is not required - I want it to be in a lazy loaded module. How can I do this?
Now fully answered on this page!
The result of this quest, which included offering and awarding bounties to two people who worked hard with me on it, is as follows:
Example of using Plupload with Angular 2 and TypeScript
How to Lazy load a script from a CDN in Angular 2
Example of how to use Plupload in a lazy loaded module
How to use a lazy loaded script in Angular 2
(See edit history for the ugly details that used to make up this question.)
Here's the overview of what you need to do to create a lazy-loaded Plupload feature while loading Plupload from a CDN:
When the feature is needed (e.g. user clicks a button or visits a page), dynamically add a <script> tag to the page to load the Plupload library from a CDN.
Wait until the library is loaded to proceed (or you could get a "plupload is undefined" error).
Display the UI to interact with Plupload in one of your Angular templates. In its simplest form, this UI consists of two buttons: "Select files" and "Upload files".
Initialize Plupload and wire it up to the UI.
Complete, working code: https://plnkr.co/edit/4t39Rod4YNAOrHmZdxqc?p=preview
Please take note of the following points in my implementation:
Regarding #2. A better way to check whether Plupload has finished loading would be to poll the global namespace for the existence of the plupload variable. As long as window.plupload does not exist, it means the library hasn't been loaded yet and that we should NOT proceed. For simplicity my code just waits for one second and proceeds.
Number 4 can prove a bit tricky. Plupload makes a heavy use of direct DOM access to wire its API to the HTML (e.g. document.getElementById('filelist')). This is something Angular discourages and that you should try avoiding whenever possible. More specifically direct DOM access is used in the following places:
To tell Plupload which DOM element should trigger the "Select files" dialog (what they call the browse_button config option). For this I could not avoid the direct DOM reference and I used the #ViewChild decorator to get a hold of the "Select Files" button.
To display selected files in the template. For this I converted the Plupload syntax into the regular Angular syntax. I push selected files to a class property called fileList which I display in the template using a standard *ngFor.
The "Upload Files" button triggers some code that does the actual uploading and refreshes the UI to show upload progress. Once more, I converted this to regular Angular syntax using event binding and data binding.
Let me know if you have any questions.
In this approach no need for any extra loader modules.
See example (check console for Woohoo): http://plnkr.co/edit/gfUs4Uhe8kMGzPxwpBay?p=preview
updated plunker: https://plnkr.co/edit/leG062tg7uX8sLrA0i2i?p=preview
You can lazyload some js by adding the script url to you document:
Create a my-lazy-load.function.ts:
export function lazyload(url) {
// based on https://friendlybit.com/js/lazy-loading-asyncronous-javascript/
let scripts = document.getElementsByTagName('script');
for (let i = scripts.length; i--;) {
if (scripts[i].src.match(url)) return true;
}
let s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = url;
let x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
return true;
}
In your component that you want to add plupload:
import {lazyload} from "./my-lazy-load.function.ts";
export class MyComponent implements OnInit {
pluploadInterval:number = null;
hasPlupload: boolean = false;
ngOnInit() {
lazyload("https://cdnjs.cloudflare.com/ajax/libs/plupload/2.3.1/plupload.full.min.js");
this.pluploadInterval = window.setInterval(()=>{
if(window.plupload) { // you can check existence of plupload object any way you want
// Woohoo, I am ready to be used
this.hasPlupload = true; // everything is run outside ngZone, wrap it if angular2 is not reacting to changes, or change window.setInterval to setInterval
window.clearInterval(this.pluploadInterval); // don't forget to clean interval
}
}, 100); // timeinterval can vary
....
The browser will load this automatically.
Notice if(plupload) it assumes that there is global object plupload that the script adds (I do not know if it truely added, check your working example in pure javascript). As it is jquery extension you can check it's prototype like this: jQuery test for whether an object has a method?
OLD HISTORICAL:
#Reid here is plunker: https://plnkr.co/edit/zDWWQbTQUSHBqCsrUMUi?p=preview the plupload is actually loaded, but added to require with define("plupload", ['./moxie'], extract); I am not sure at the moment how to extract from there and which package require is belong to... the code for finding correct module loader belongs to plupload itself, here it is (from plupload.dev.js):
if (typeof define === "function" && define.amd) {
define("plupload", ['./moxie'], extract);
} else if (typeof module === "object" && module.exports) {
module.exports = extract(require('./moxie'));
} else {
global.plupload = extract(global.moxie);
}
I think that your best bet is to use the Require.js Library so that you can dynamically load your scripts from within your components.
The small trade off is that you will have to add this 18KB library to your index.html page (CDN), however this could save you huge amounts of loading if your 3rd party libraries are massive.
I have no experience with using plupload, so instead I put together the following plunkr which uses an external animation library, drawn from a CDN. The plunkr animates a number from 0 - 100.
https://plnkr.co/edit/fJCtezsERYHOYplLh7Jo?p=preview
index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.3/require.min.js"></script>
component.ts
ngOnInit(){
// Dynamically loads the framework from the CDN.
require(["https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenLite.min.js"],
// This function is then called ONCE LOAD IS COMPLETE
function (common) {
// Do greensock things
var demo = {score:0},
scoreDisplay = document.getElementById("scoreDisplay");
//create a tween that changes the value of the score property of the demo object from 0 to 100 over the course of 20 seconds.
var tween = TweenLite.to(demo, 20, {score:100, onUpdate:showScore})
//each time the tween updates this function will be called.
function showScore() {
scoreDisplay.innerHTML = demo.score.toFixed(2);
}
});
}
What I like about this approach, is that in the onLoad callback from require, the syntax is unchanged from a normal implementation of the library, so you can just copy paste your currently working code into the callback.
I've noticed that RequireJS creates script tags in the tag as it loads modules.
Is there anyway to configure RequireJS to "tag" those elements w/ a class or an attribute of some kind that I could later target w/ jQuery later on?
e.g.:
var $requireJsScripts = $('script.require-script');
--UPDATE--
Ok.. I think I can get by on this little workaround for now. Thanks to this answer for the breadcrumb on require.s.contexts._.defined. I'd still like to hear if anyone knows of a way to configure RequireJS to do something similar to what was laid out in the original question...
var loadedRjsModules = Object.keys(require.s.contexts._.defined);
var $scripts = $('script');
$scripts.each(function () {
if ($(this).data('requiremodule') && $.inArray($(this).data('requiremodule'), loadedRjsModules)) {
console.log(this);
}
});
Looking at the source code, I don't see how RequireJS would allow adding anything custom to the script nodes at creation. The routine that creates them has no provision for it. The code that fleshes them out upon creation does not support it either.
There's an onResourceLoad hook considered part of the internal API. It could be used with the code you've put in your question instead of relying on require.s.contexts._.defined, which as far as I know is fully private and subject to change without notice.
First of all: 'Locally' neither means "localhost", nor "local folder". It means a code area or a code space or a code region.
I have two JS (*.js) files for my site. One is to show a news ticker and other is to load something on hover. They are conflicting, and I can't remove any one of 'em because I need 'em.
So a thing comes up to my mind is: as I can make many things locally, why not I load a js file locally? Suppose:
<?php
if('condition') {
DO IT ONCE;
}
?>
<?php
if('other_condition') {
DO STH ELSE ONCE;
}
?>
In such case, the first condition doesn't bother the second condition. Even though the first one is doing, the second one is also doing well. No conflict, nothing.
If I can load a JS locally for a specific purpose and then break the JS loading further, then if I load other JS, she won't find any JS before, because that's for a specific purpose for the specific region only.
I think I'm clear with my idea. I'm here with a WordPress site, loading code specifically for home page using is_home() function. I want such a way to load a JS file for a region, and then break it to let the other JS function properly.
If you've designed your Javascript well, you can have two scripts that don't interfere. Without seeing the actual scripts, it's hard to recommend improvement. You could introduce new scopes for each of the scripts:
script1.js
(function() {
var script_variable = document.getElementById("my_form");
script_variable.onchange = function() { /* ... */ };
})();
script2.js
(function() {
// Same name!
var script_variable = document.getElementById("other_element");
script_variable.onclick = function() { /* ... */ };
});
Load each in a separate iframe.
Is it possible to load the functions from separate Javascript files dynamically when a window loads, by setting an array and loader function?
The easiest way is to simply add <script> elements to the DOM dynamically.
<script>
var scripts = [ 'a.js', 'b.js', ... ];
for (var i = 0; i < scripts.length; ++i) {
document.write('<script src="' + scripts[i] + '"></' + 'script>');
}
</script>
Note that you can only do this before the DOMContentReady event is fired.
BTW event Google Closure Library uses this technique. And as a sidenote, you probably want to concatenate the scripts into a single file so that you save yourself (and your users) HTTP requests and get faster page loads.
What's cool about this is that the scripts are downloaded and evaluated right after the closing </script> tag so you don't have to wait for anything else before you can use them:
<!-- Let's pretend that a.js defines a function A() -->
<script>document.write('<script src="a.js"></' + 'script>');</script>
<script>
var a = new A();
</script>
Actually I may as well make this an answer rather than just a comment, require.js has exactly the functionality you're looking for, specifically look at this docs page to see it's use in-browser.
I have the issue of loading the same scripts across all my pages without having to define them all over and over.
I've came up with a half complete solution which looks like...
var scriptLocs = [];
function loadScripts() {
for (var i = 0; i < scriptLocs.length; i++) {
var crtElement = document.createElement('script');
crtElement.src = scriptLocs[i];
var hh = document.getElementsByTagName('head')[0];
// Getting head tag
hh.appendChild(crtElement);
//Adding it to head
}
}
The array can be very big e.g. around 120 scripts (2.5MB) these include such things as jQuery and Knockout etc...
The problem im having is this can be unreliable my function seems to miss out, or load in a different order than defined in my array. The order is very important as some things may rely on jQuery so it must be loaded before other scripts.
Also its differnt errors on every refresh ... E.g. maybe differnt loading order?
Is there away of define all the scripts in an element then appending the lot of them at once? would this work?
UPDATE:
Using this test.js file
for (var i = 0; i < scriptLocs.length; i++) {
document.write("<script type=\"text/javascript\" src=\"" + scriptLocs[i] + "\"><\/script>");
}
And including it as the very first thing in the head like..
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="test.js"></script>
Seems to solve this problem
with that many scripts you should maybe consider compressing and concatenating them and serve them as one minified script.
You could do this with Googles closure compiler or the YUI compressor for example:
This is what document.write() is for. Frequently it is misused, but in this case, it's a good choice:
var scriptLocs = [...];
for (var i = 0; i < scriptLocs.length; i++) {
document.write("<script type=\"text/javascript\" src=\""+scriptLocs[i]+"\"><\/script>");
}
But do not put this in a function or call it from an event handler. This script should run immediately while the page is loading. Calling document.write() after the dom has loaded causes problems.
It sounds like you are looking for a way to declare dependencies and specify loading order which is something that require js handles quite well. Additionally,
RequireJS has an optimization tool that ... [c]ombines
related scripts together into build layers and minifies them via
UglifyJS (the default) or Closure Compiler (an option when using
Java).
This would allow you to to have a single minified script to include across all pages in your application.
did you check the sizes of the scripts you are calling ?
the array will follow you order and element creation will follow your order, but remember you are calling them in javascript, so it'd never wait for a script to load and then load the other one, i.e. if you are loading jquery which is 100 kb for example and you are calling some native script which is 10 kb, so your script will be loaded first this the jquery.
you add attribute async to make the dom don't wait for your scripts.