How to have one main JavaScript file for multiple pages? - javascript

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.

Related

Is this method of conditional javascript loading of any value?

I am building a multi-page front end for a web application. There is minimal javascript for the UI, but I know there will a great deal of javascript to handle the back-end, so I am doing my best to keep it as sparse and limited as possible.
I want to conditionally execute javascript synchronously while using only one minified js file. I have some global functions, but most of the code is page specific. Each page has the id of the page in the body element. For example, about.html has the following code <html>...<body id="about">...</body></html>.
I am aware of various async methods to load javascript, such as require.js or even $.getScript, but for various reasons, I am trying to avoid that route. Because I am preprocessing the HTML, I also don't want to break the scripts up per page (though I could), because it is a small file, and I don't want to have to create a bunch of different <script> tags. My current solution is to conditionally execute the required javascript per page, and I am doing it like the example below:
$(document).ready(function(){
//global.js code
if(document.body.id==='about'){
// about.js code
}
if(document.body.id==='home'){
//home.js code
}
// etc.
});
During the build process, all js is stored in seperate files, so it's not as visually asinine as it looks here, because there is only a single include filename.js inside the body of the conditional.
The conditionals prevent unnecessary code from executing on each page. However, it dawned on me while I was doing this that there might not actually be any benefit to doing it this way. I read that $(window).load or $('body').load events might cause conflicts with $(document).ready, and they are both definitely slower - as is window.onload (I tested). That would've been my preferred way of going about this I suppose. Is what I read true? And / or, is what I'm doing in anyway useful?
For page specific events, why not just add the body ID in the element reference?
$( '#about .do-something' ).on()...
And if you're actually executing non event based code on a per page basis
if( $( '#about' ).length ) { // trigger your code here }
It's not too dissimilar to your solution except it doesn't require you to keep a manifest of sorts about what code to execute where. Either way, both solutions get cached.

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 do I execute JavaScript, page specifically, from one file?

I have a single file, 'core.js', with a collection of my sites JavaScript. I like to combine my script this way so I can cut down on HTTP requests. My problem is, page specific code's, obviously, getting run on pages it needn't.
Without splitting 'core.js' into separate scripts, what's the best solution for ensuring my page specific code only gets run on the page it's supposed to be run on?
Many thanks!
The simplest way is to make everything in core.js be functions and then put one inline function call in each given page to call the code specific to that page. That makes core.js a resource and each page decides what functions in that resource to call.
I've also seen it done where you put a trigger class name on the body tag and then the code in core.js examines the classes on the body tag to decide what initialization code to run. This works best when you have a small number of types of pages and you want to run the same initialization code on all pages of the same type, but I don't think it's all that good if each page has different JS. In that case, I think it's better to use the first technique of let the page decide what JS initialization function to call.

Where to put JavaScript configuration functions?

What is the general developer opinion on including javascript code on the file instead of including it on the script tag.
So we all agree that jquery needs to be included with a script file, like below:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"
type="text/javascript"></script>
My question is, in order to get functions on a page that is not on all pages of a site. Do we include the functions like below in the same page or in a global include file like above called mysite.js.
$(document).ready(function(){
$(".clickme").click(function(event){
alert("Thanks for visiting!");
});
});
ok. So the question is: if the code above is going to be called in every class="clickme" on a specific pages, and you have the ability to call it either from an include separate file called mysite.js or in the content of the page. Which way will you go?
Arguments are:
If you include it on the page you will only call it from those specific pages that the js functionality is needed.
Or you include it as a file, which the browser cached, but then jquery will have to spend x ms to know that that function is not trigger on a page without "clickme" class in it.
EDIT 1:
Ok. One point that I want to make sure people address is what is the effect of having the document.ready function called things that does not exist in the page, will that trigger any type of delay on the browser? Is that a significant impact?
First of all - $("#clickme") will find the id="clickme" not class="clickme". You'd want $(".clickme") if you were looking for classes.
I (try to) never put any actual JavaScript code inside my XHTML documents, unless I'm working on testing something on a page quickly. I always link to an external JS file to load the functionality I want. Browsers without JS (like web crawlers) will not load these files, and it makes your code look much cleaner to the "view source".
If I need a bit of functionality only on one page - it sometimes gets its own include file. It all depends on how much functionality / slow selectors it uses. Just because you put your JS in an external JS file doesn't mean you need to include it on every page.
The main reason I use this practice - if I need to change some JavaScript code, it will all be in the same place, and change site wide.
As far as the question about performance goes- Some selectors take a lot of time, but most of them (especially those that deal with ID) are very quick. Searching for a selector that doesn't exist is a waste of time, but when you put that up against the wasted time of a second script HTTP request (which blocks the DOM from being ready btw), searching for an empty selector will generally win as being the lesser of the two evils. jQuery 1.3 Performace Notes and SlickSpeed will hopefully help you decide on how many MS you really are losing to searching for a class.
I tend to use an external file so if a change is needed it is done in one place for all pages, rather than x changes on x pages.
Also if you leave the project and someone else has to take over, it can be a massive pain to dig around the project trying to find some inline js.
My personal preference is
completely global functions, plugins and utilities - in a separate JavaScript file and referenced in each page (much like the jQuery file)
specific page functionality - in a separate JavaScript file and only referenced in the page it is needed for
Remember that you can also minify and gzip the files too.
I'm a firm believer of Unobtrusive JavaScript and therefore try to avoid having any JavaScript code in with the markup, even if the JavaScript is in it's own script block.
I agreed to never have code in your HTML page. In ASP.net I programmatically have added a check for each page to see if it has a same name javascript file.
Eg. MyPage.aspx will look for a MyPage.aspx.js
For my MVC master page I have this code to add a javascript link:
// Add Each page's javascript file
if (Page.ViewContext.View is WebFormView)
{
WebFormView view = Page.ViewContext.View as WebFormView;
string shortUrl = view.ViewPath + ".js";
if (File.Exists(Server.MapPath(shortUrl)))
{
_clientScriptIncludes["PageJavascript"] = Page.ResolveUrl(shortUrl);
}
}
This works well because:
It is automagically included in my files
The .js file lives alongside the page itself
Sorry if this doesn't apply to your language/coding style.

Categories

Resources