Ext js dynamic loading of libraries when using iframes - javascript

Good day all.
I have a big Ext js application in which I have to insert a new section which will be loaded in iframes.
To maintain the routes of Ext js, each page of the new part will be a different view, with a different viewController in which will be only the iframe that will load the page of the new application.
Now I'd like to make some cleaning in the mess, for example, all the new application pages will use some shared libraries, which will be inserted in the app.json of Extjs so they will be available for each iframe page.
but most of the scripts of these new pages will be specific of that very page, so it will be a big waste of resources if all of those scripts have to be inserted in the app.json and so they are available for every part of the application.
The idea was, is there a clean way to specify some libraries used only by a single viewController (and its iframe) and when the user change the route, everything is destroyed and the memory is cleaned?

If you need to load a specific js only if a window or frame is called, the best way is to use an Ext.Loader method called loadScript
Ext.loader.loadScript(url)
Loads the specified script URL and calls the supplied callbacks. If this method is called before Ext#isReady, the script's load will delay the transition to ready. This can be used to load arbitrary scripts that may contain further Ext.require calls.
Also remember that your on html5! So you can use other methods to load a js script when you need it. An example:
System.import("yourScriptFile.js").then(function(){
// script loaded.
});
If you want to load multiple files you can also use it:
Promise.all(["url1", "url2"].map(System.import)).then(function(){
// loaded all here
});
If you need to destroy a window iframe you only need to use the method close, as you can se here the method close on the default case will delete the window from the dom and destroy the object from the memory.
So, in my opinion the best way is to use iframe windows, and use the close method to destroy them at the end of their life.
If you only need to use a container in which you're inserting your new html with your js scripts, you can instead call a simple .destroy() method.
Remember that there are other ways to split your project's app.js in different parts, have also a look here to have an idea.

Related

How to have one main JavaScript file for multiple pages?

I have a small application with two pages: the login page and the main page. My problem is that when I use just one main JavaScript file for both pages (like it's recommanded) I get a lot of ReferenceError because some variables on a page are not defined on the other one...
e.g:
Line of code for the login page
copyrightYear.textContent = new Date().getFullYear();
Main page complains
Uncaught TypeError: Cannot set property 'textContent' of null
How can I fix that? Don't tell me I have to say if(copyrightYear) { do stuff } everytime, it's just a nightmare if I have to do that for every variable
Two answers for you:
The recommendation isn't a dictate
My problem is that when I use just one main JavaScript file for both pages (like it's recommanded)
That's a very general recommendation. It doesn't apply to every situation. There's no point in loading code in a page that won't use that code.
If you have code that you use in both pages, and also page-specific code, it's absolutely fine to have a file that both pages share and also page-specific files:
<script src="main.js"></script>
<script src="page1.js"></script>
If you're really worried about the extra HTTP request (much less of an issue these days than it used to be), use modules and a bundler like Webpack or Rollup that will create a bundle combining the main module with page 1's module for page 1 and another bundle combining the main module with page 2's module for page 2.
But even then, the extra HTTP request may be better for your users, if you expect them to go from page1 to page2 in a reasonable timeframe. The reason is that if you have main.js and page1.js/page2.js and you allow caching of them, when the user goes to page1 they get main.js and page1.js, and then when they go to page2 main.js comes out of their local cache and they only have to load page2.js. In contrast, if you had a single bundle file for each page, they'd have to re-download all of that common code when going from page1 to page2 (or vice versa). But if you expect a visitor to visit either page1 or page2 but not the other one, you save the HTTP request by using page-specific bundles.
There's really no one-size-fits-all solution. :-) There are all sorts of considerations.
Note that HTTP/1.1 made additional HTTP requests more efficient and is nearly universally supported, and HTTP/2 makes them a lot more efficient, effectively eliminating the case for reducing the number of HTTP requests as part of the page startup. All major modern browsers support HTTP/2 as do up-to-date web servers.
Put the code for each page in a function
If you really want to keep a single page, put the code that's specific to each page in functions for those pages, and then have code in the main part of the file call the appropriate function based on location.pathname.
You figured it out, you have to check this for every variable. But generally it's much more convenient to use functions and only call these functions when you need them.
So for example, say you want to set some copyrightYear (even tough this shouldn't be set via JS, you should generate this on Backend side to have it in the source code)
You have something like this:
function updateYear() {
// here you do your magic of selecting the element, setting the year, whatever.
}
// another function, totally unrealted to updateYear()
function toggleMenu() {
// some function where you toggle the menu if you click somewhere
// like: button.addEventListener('click', () => {} );
}
And in your JS file you have one block where you call all these functions:
if (document.querySelectorAll('.elementForYear') {
updateYear(); // here we call it because we are sure this element exists... so everything inside function must work
}
if (document.querySelector('.myMenu') {
toggleMenu(); // if the element myMenu exists, we know we can add these toggle Functionality.
}
You can also add these if inside the function and call the function regardless of if it's needed or not, that's up to you and up to coding guidelines.
Generally I find it makes sense to have one function only rely on one (or max two to three elements if it's some toggling of other elements) ... And then you just check for one element. And if this one element exists you can go ahead and call the function.

AJAX ready event once HTML page and scripts are loaded

I'm creating a web application that has multiple pages of content that I'm loading dynamically with AJAX and the HTML5 History API. When a user attempts to change the page, the new content is loaded with a $.get and injected into the body, like so:
$.get("somepage.html", function (data)
{
$("body").html(data);
});
Most of these pages require additional scripts to be loaded. This wouldn't be an issue except for the fact that $(document).ready fires before these scripts are loaded. Somepage.html looks something like this.
<script src='http://getjquerysomewhere/'></script>
<script src='my_script_that_depends_on_jQuery'></script>
This issue is complicated by the fact that these pages must have the ability to be loaded on their own. I'm therefore unsure how I can eliminate the $(document).ready functions without affecting this behavior as well.
How should I approach this problem?
What you are trying to do is certainly possible, but it's not going to be very maintainable in the long-run.
One of the biggest issues you'll run into is properly injecting the code from the ajax loaded html into the current page. You can't just ignore it and let it all run because then you'll be including libraries multiple times (resulting in plugins getting overwritten/removed), and the code for the page you are loading may happen too soon due to the dom already being ready.
This pretty much leaves you with two options: dependency injection or front-loading.
Dependency injection will probably be the easiest of the two for you to implement because it requires the least amount of changes to your current code-base. All you would have to do is ensure that all pages requested with ajax only include the content of the <body> (which can be done with server-side code), and ensure that all page-specific code is included before the closing </body> of each page. Then you would just have to use the dependency-injection methods to run your code with the proper dependencies.
You could also have it only include <div id="#content">...</div> for your partials, which ever makes more sense for your use-case.
Front-loading would be a little more difficult because you'll have this one giant file that has all of your code for all of the pages, unless you use a build process (if you've never used a build-process before, you really should try it, even if you don't think you need it.) With front-loading, you'll either have to use event delegation, or have init methods for each page that you selectively execute as you load each page. This could become a maintainability nightmare without good build processes.
You can call functions from the original scripts on the page which you have loaded. For Instance you could do this in your main:
<script>
function ExternalScriptLoaded(){}
</script>
Then on your external page:
<script>
try{ ExternalScriptLoaded(); }catch(err){alert('This page was not loaded with ajax because i can't find the function');}
</script>
The alert will trigger if the script can't find the function on your main page.
i.e. call the function after you know the script has finished runnng.
Hope this helped.

How to handle dependencies when libraries are loaded asynchronously?

Google Pagespeed said I should load my JS files asynchronously, but this has introduced a problem for many of my pages with code using libraries and plugins.
For example, I have the following code on one page:
$(document).ready(function () {
var hound = new Bloodhound({ .......
});
So when the page loads, I am creating a Twitter Bloodhound (goes with Typeahead) object. The problem is, if Bloodhound and Typeahead are loaded asynchronously, then an error is thrown:
Uncaught ReferenceError: Bloodhound is not defined
This is because those scripts haven't been loaded yet.
I came up with this solution:
$(document).ready(function () {
createBloodhound();
});
function createBloodhound() {
if (typeof Bloodhound != "undefined") { // if bloodhound JS has loaded
var hound = new Bloodhound({ .......
}
else {
setTimeout(function(){
createBloodhound();
}, 10);
}
}
Is this a good practice, or is there a better way?
NOTE: I realize there are libraries like RequireJS out there to handle dependencies when loading files, but I don't think this type of solution will not work in my case because I load the libraries asynchronously in a wrapper file (since they're required for every page). The example code here would not be on every page, but only on a specific page on my website.
The best approach is to use a callback mechanism, which you can react to, rather than using a polling mechanism. I used script.js, which is simple and yet functional, and offers the callback mechanism.
Without that, you could implement something yourself. Performance-wise though, utilizing callback are better.
Depending on the complexity of your site, different options might be best. If...
All of your javascript is in JS files
Your above-the-fold content looks identical before and after the JS is loaded (or close enough to identical that the flash of change when your JS does load wouldn't distract your users)
The total file size is small (or most of your JS is needed on pages everyone will visit every time they visit your site)
... then combine them into one file and just server that instead of all the individual ones. Then you don't have to worry about dependencies at all. Include that script file at the bottom of your body tag (no need for async or defer attributes, but you can use them if you want).
If some of your javascript is necessary to make your above-the-fold content look correct, do the same thing, except split your JS into two files. One file contains only what is necessary to make the above-the-fold content look correct, and the other file contains everything else. Include the first one in your head tag (possibly inlining it), and include the second one at the bottom of your body tag. If the second one depends on the first tag, do not use the async attribute, because it might get executed first.
If you have some large JS files that are only used on some pages, and those files depend on other JS files, stick your scripts at the bottom of your body tag and use the defer attribute.
If you have javascript mixed in with your HTML, you can use a callback mechanism (like script.js), or you can build up execution queues like Google Analytics does, which the external script knows to look for when it first loads.

including javascript files in my page

I have many (almost 15) javascript files which I am using in my webpage.
What is the best way to include all of these files?
Simply at bottom or is there some other technique that should be used which can load javascript faster?
You could combine and minify them. YUI Compressor could do that.
You may consider using a parallel/non-blocking javascript loader.
Following are some great libraries that could do this for you..
http://headjs.com/
http://requirejs.org/
http://labjs.com/
Google Closure Compiler has a convient web api for merging and minifying all your js.
https://developers.google.com/closure/compiler/docs/api-ref
You can put a script tag with a direct link to the api.
I haven't quite mastered implementing all this yet I have found that a fair amount of minification & concatenation can be automated by servers, I have learned a fair amount from reading the (well-commented) .htaccess file in the HTML5 Boilerplate and associated docs.
Modernizr also features an asyncronous loader function and it was well enough documented that I was able to get it to work when I just couldn't quite figure out require.js
You could use a script loader library or you can do it yourself.
Where to include scripts and how the browser loads resources
Always at the bottom just before the closing <body> tag, to enhance the user experience. It ensures that the page content gets rendered first, which gives the impression that your pages load quicker.
The browser runs a single thread and will process the page content from top to bottom, if it happens on a resource, it first has to retrieve the resource, process it and then only continue with the next tag on the page. Since we generally want to wait for the DOM to be finished loading, before running any scripts in any case, this strategy is always a win win.
Size matters
For production you want to ensure that all your scripts are minified regardless of the strategy you are going to employ to get them loaded (smaller size == less to download == quicker)
The correct order
Most important is always the dependency order, if we call a function or access a variable and the browser doesn't know about it first, the script will fail. You want to ensure that a script is only included after any scripts they depend on. So the first thing to do is to work out the order by which you need to include and introduce functionality to the browser.
How to include scripts into a page
There is only one way to include a script in a page (not recommending same origin ajax/eval) and that is by means of the <script> tag.
There are only 2 ways to create a <script> tag:
Statically located in html or
dynamically added with JavaScript.
Statically added scripts
Since we know the order we need our scripts included we can add them one by one after each other like this
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.5.2/jquery.tablesorter.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.5.2/jquery.tablesorter.widgets.min.js"></script>
Dynamically added scripts
There are three ways to effectively create script tags in the dom, dynamically, which we will explore. These are certainly not the only ways...
document.write
The write method will append to the existing dom.
document.write('<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>');
But now we have a problem as there is no way of telling that the script is done loading, because we have to wait until it is complete before loading the next one or calling something that is defined there.
document.createElement
The createElement method creates a dom element and we can insert it anywhere in the dom we choose.
The following example checks if jQuery exists or creates a <script> element to go fetch it. In any event, the function ready_go is called and we should have jQuery.
(function() {
if (!window.jQuery) {
var jq = document.createElement('script');
jq.type = 'text/javascript';
jq.async = true;
jq.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js'
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(jq, s);
jq.onreadystatechange = jq.onload = ready_go;
} else ready_go();
})();
jQuery.getScript
Since we have jQuery now we don't have to do things the hard way, getScript takes as first argument the script url and will call the function as 2nd argument once the script is complete.
function ready_go() {
$.getScript('http://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.5.2/jquery.tablesorter.min.js', function () {
// script is done loading we can include the next
$.getScript('http://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.5.2/jquery.tablesorter.widgets.min.js', function () {
// script is done loading we can include the next or start using our stuff
});
});
}
Where to get the scripts from?
You either want to get the scripts from public CDN or host them yourself. An important concept to bear in mind is that scripts, as with any other resources get cached by the browser and you will not be required to repeatedly download the same scripts.
Scripts from a Content Delivery Network
The advantage of a CDN is twofold;
you get closer to the user, because a CDN consists of POPs distributed across the world and even though you are calling one url the server responding may be different
many sites may use the same CDN if your user already got jQuery from google's CDN when visiting my site his browser will simply use the cache copy already in its possession when requested by you.
Scripts served from origin (your server)
I agree with the strategy that #Mario and #Xharze suggests, beats including 18 scripts on every page.
Use CDNs where scripts are available but for the rest.
Concatenate all your scripts together, keeping in mind that the same dependency order applies, and minify them.
Having one script will simplify your includes and because the browser will cache the script no additional downloads will be required again, especially beneficial in application used for extended periods and accessing many pages.
The only advantage separate scripts hold is for users who exit from the landing page and why do we care to optimise for them they're not going to stay because of it.
nJoy!

How can I include an external JavaScript file exactly once per partial view?

I have a partial view (UserControl) that implements a simple pager in my Asp.Net MVC project. This pager needs access to a .js file that needs to be included exactly once, regardless of how many instances of the pager control are present on the parent page.
I've tried to use Page.ClientScript.RegisterClientScriptInclude, but it had no effect (I assume because the code nugget was evaluated too late to impact the head control). Is there any simple alternative?
Set a flag in HttpContext.Current.Items when you send the script in your partial - it's a dictionary that lingers for the whole request. E.g.:
if (!HttpContext.Current.Items.Contains("SentTheScript"))
{
HttpContext.Current.Items["SentTheScript"] = true;
Response.Write(Html.ScriptTag("Script"));
}
This way it'll be rendered at most once.
Add it to the master page.
Then you can guarantee that it'll only be included once, and you won't forget it.With modern caching, there is really no performance loss on including it on every page.
Page.ClientScript.RegisterClientScriptInclude is used with the traditional ASP.net.
Since you using asp.net-mvc, all you need is to add the script directive of the js file to the page that uses this user control.
Since it's a good practice to have only one minified css file to all the site, you probably will want to include this file in the main css minified file.

Categories

Resources