I currently have page-specific javascript files that look something like:
$(document).ready(function ()
{
namespace.home.list = {};
namespace.home.list.Update = function ()
{
var element = $(namespace.ElementIds.LIST);
//some code that pulls a list from the server and dumps the data into our element
});
};
By my own convention, this .js file is meant to belong to Controller Home/List (and correspondingly the View Home/List.cshtml).
Furthermore, namespace.ElementIds has a list of Ids that are manually kept in sync with a (nearly) identical .cs file, so that server-generated html creates elements with the same Ids that I reference in my js files.
I am unhappy with this as a solution for keeping my elementId and js namespaces refactor-friendly. Is there a better way?
Currently my javascript is loaded via script tags with src attributes, but I've considered having them in .cshtml files that have nothing but a script tag with some razor markup that would let me define namespaces + elementIds using only my C# classes. I have a feeling this is not a good idea, but I don't actually know why. It would increase my ability to refactor and link my server code to my client code, but what are the drawbacks? Lack of caching? Decreased performance?
Generally you do not want to have that many javascript files linked to your document. Each uses up a round trip's worth of time and overhead in the HTTP Protocol.
What you should do is build/use a system that collects all your JS files in your project and crunches them into one minified js file (or 2-3 if you need lighter loads in places). This will make sure they load quickly and in one go.
Then you link the one file in your document and it doesn't matter what you rename the original js file to. Or where it lives.
As for namespacing, something you should note:
You should probably wrap up most of your modules in a scope to keep from polluting your global namespace, this has the addded benefit of giving you a shortcut for your namespacing mechanism:
;(function($, $namespace) {
var $module = $namespace.list = {};
$module.Update = function ()
{
var element = $($namespace.ElementIds.LIST);
//some code that pulls a list from the server and dumps the data into our element
};
})(jQuery, namespace.home);
This allows you to have private helper functions with useful (generic) names. Since they're only accessible from within that scope and won't conflict with other modules. Additionally this makes sure your jQuery never conflicts with another js library that may take the $ identifier in the global namespace.
Related
I am using a ASP.NET route (to intercept the call to the .js) and controller to generate some JS I want to use on my client. The reason I'm doing this is so as to not have to duplicate id's or constants on the client. Here's the output of my JS:
app.serviceRootURL = 'http://localhost:65211/'; // set in my web.config
app.ajaxResponseStatuses = [
{ "status":"Success", "id":0 }, // set in my C# DTO
{ "status":"Failure", "id":1 },
];
First of all, I am not sure if this is the best approach, so other suggestions to doing this would be beneficial.
More importantly though, I'm wondering how I can bundle and minify this. As I understand it, even if I could minify the JS at compile or run-time, minification will change the names of my variables. So in the above JS, app.ajaxResponseStatuses could get changed to a.bc, and then in the actual JS files where I'm trying to access that variable, they could be looking for x.yz.
Can I minify this code and get it to the server?
Will I still be able to use the above properties in other minified files?
(bonus points) Is this a good aproach to pass server-side-only values to be used on the client?
Part 1
If you are generating the js at runtime, bundling isn't possible (at least not efficiently). You would have to create a new bundle for every request which isn't terribly quick. Plus, you wouldn't be able to cache the regular, constant script bundle.
EDIT: While bundling server-generated js isn't practical, rendering the values into a script tag in the page can achieve the same benefit of bundling, fewer HTTP calls. See the edit in Part 3 for more.
Minifying the server generated js however, is totally possible. This question should have the answer you're looking for. However, I'd recommend you cache this on the server if possible, as the minification process itself could take longer than simply sending down the extra bits.
Part 2
In most minifiers, global variables (those accessible on the window object) are skipped during the name mangling. With the same respect, variables that are accessed in other files that are not defined within that file are not renamed.
For example, if you have the following file...
// outside of a closure, so globally accessible
var foo = 1;
function bar() {
// within a closure, and defined with `var`, not globally accessible
var bar;
// reference to variable declared in another file
baz = null;
}
it would be minified as follows (with whitespace included for readability
var foo = 1;
function bar() {
var b;
baz = null;
}
This is one reason it is important to always declare your variables using the var keyword, otherwise they are assumed to be references to global variables and will not be minified.
Also, JSON (not Javascript object literals!!!) will never be distorted by minifiers, because it consists of string literals for all keys, and all values that aren't of another literal type.
Part 3
Not a bad way, and at my job we do use this approach. For small files though, or simple config values, we have transitioned to rendering server values in a script tag using ASP.NET in the actual view. i.e.
Default.aspx
<script> window.globals = <%= JsonConvert.SerializeObject(new AppGlobals(currentUser)) %>; </script>
We rip this out into a code behind, but the premise is the same.
EDIT:
Server-Generated JS (at it's own uri)
Pros
Cacheable by browser (if fresh values aren't needed on every request)
Cons
Extra round trip
Use when:
Your generated files are large, but rarely change or are the same for multiple users. These scripts can be treated the same as other static assets. To give an example, we serve a js file containing all the text in our app for localization purposes. We can serve a different js file based on the language set in the user's settings, but these values only change once at most with every release, so we can set aggressive cache headers and use a hash in the uri, along with a query string for the locale, to leverage browser caching and download each language file only once per client. Plus, if this file is going to be the same for every user accessing the same uri, you can cache it at the web server (IIS, Apache, etc.).
Ex: /api/language.v1-0-0.js?locale=en
Your js is independent from the rest of your app and not having it won't delay rendering. In this case, you can add the async attribute to your script tag, and this file will be downloaded asynchronously and executed when it is received without preventing the execution of other javascript.
Server-Rendered JS (within the page in a script tag)
Pros
No extra HTTP calls
Cons
Can add extra weight to your HTML, which may not be cacheable or minified depending on your circumstances
Use when:
Your values change often. The weight added to the page should be negligible unless you have a huge number of values (in that case, you might consider splitting them up and adding API endpoints for these values, and only getting them when you need them). With this, you can cut out the extra HTTP call as the js is injected into a script tag on a page the user would already have to retrieve.
But...
Don't waste too much time worrying about it. The differences in these two approaches is almost always negligible. If it becomes a problem, try both and use the better option for your case.
Currently I am using a single .js file for a whole project (plus included libraries). Only occasionally I split the file into multiple files (i.e. front vs back end sections). In the file(s) I attach events (and other functionality) inside of a single jQuery ready event handler using jQuery selectors:
$(document).ready(function() {
$('#an_element_on_homepage').click(function() {
// do something
});
// ...
// A lot of similar code here
// ...
$('.elements_on_homepage_and_contact_page').click(function() {
// do something
});
});
This is perfectly fine and working, but on a larger project there could be a lot of code executing unnecessarily as some events are needed only at specific pages or there could be id/class collisions between different pages. What is the best practice to avoid the problems and still preserve easy maintainability of the JavaScript and HTML code?
I can think of 2 solutions:
Split .js file into multiple files and on every page choose only the files needed. However, it could be hard to distribute the event attachments properly and could cause problems in caching the scripts on client side.
Wrap the event attachments in functions and call them from a HTML code only where needed. Something like:
function attachClickOnElementOnHomepage() {
$('#an_element_on_homepage').click(function() {
// do something
});
}
And after in HTML:
<div id="an_element_on_homepage"></div>
<script type="text/javascript">attachClickOnElementOnHomepage();<script>
However, I have a feeling this is also not the best solution possible.
Can you think of another/better solutions?
This is a classical issue of code structure.
Separate your views into widgets (think plugins). If you have models in your app, separate their declaration from the main onReady event, fire events in your model and allow UI widgets to interact with your models.
Mostly, separate your files in order to:
* have a clearer view of your work
* separate concerns, and expose only what is required
For instance, consider that nothing exists appart from what is located in your file. If you do any use of variables that are not declared in-file, consider you've broken the principle of separation of concerns.
As an example:
var myModel = function () {/**...**/};
var myModel.prototype.save = function () {/**...**/}
var myUIElt = function (model) {
this.render(); //Do whatever required
this.bind('onchange', function() { model.update();});
};
With, such, in your onReady callback, you'll have:
$(function() { var elt = new myUIElt(); }); // Much cleaner, huh ? ;)
In the real world, things may be a little more complicated, but the main idea is the one above.
Don't hesitate to ask for more details,
HTH,
Solution 1. is, as you say, not particularly efficient with regards to HTTP roundtrips, although you can probably choose to ignore that concern if you're making an internal application.
Solution 2. just looks like a roundabout way of inline event handler registrations.
The pattern I use is one JS file that has any code I want to reuse as a project-specific "library" – there's rarely enough of it to warrant splitting it up. And one JS file per unit of server-side code – MVC controller, stand-alone Wicket control – that implements only page-specific behaviour, or wires up to the reusable code. This file is usually named after the controller.
The advantages:
There's clear association of what JS behaviours happen where
You get rid of inline Javascript in pages – I dislike it if only because it makes setting JS breakpoints a chore
If you combine your JS libraries, you only include 2-3 script files on any given page, of which only one isn't reused on every single page
Less risk of conflicts where a selector that's too broad could match elements on a different page than it was intended to and attach unintended behaviour to them.
The JS code is still reasonably straightforward - scripts included on pages. No need to devise a module structure.
A possible downside is that some code may be repeated between the page-specific scripts, but that's the cost of separating mechanism from policy. This mostly happens to me for trivial code like setting up jQuery UI buttons or Chosen selectboxes; since they share a common theme, my solution was to roll these up into a single JS file for "ui enhancements".
You could also look into tooling that will combine fine-grained maintainable javascripts into easier to load ones as a build step; for a large project where the performance gains matter, the effort in maintaining this might not be overkill. The specifics would depend on what your other tooling is, a fallback solution could be as simple as a shellscript that calls the command-line version of jsmin.
you can organize your code in modules with init function defined inside each module
and then call the moduleName.init() function only if needed element is present on currently loaded page
something like
var homePageModule = {
init: function() {
$('#an_element_on_homepage').click(function() {
});
anotherFunction();
},
anotherFunction: function() {
//do something
}
};
//check for elements
$(function() {
if ($('#an_element_on_homepage').length) {
homePageModule.init();
}
});
P.S.: or use classes and instantiate them accordingly to element presence
If it's goint to be a large web project then i would recommend you Backbone.
I want to push my Django project with some JavaScript/jQuery. To make it right from the beginning on I'd like to know, which way of organizing the .js-files ist the optimal one.
For loading one big file includes less overhead than loading many small ones and also because it looks cleaner in the code I considered to make one global .js-file and include that with the base.html (from which every template inherites). However, the result would be, that JavaScript would try to assign all the event-binings, even if the elements which the events should be bind to aren't in the current document. With all the jQuery-selectors which then would have to do their work that can't be too efficient. From earlier web-development experience I know that one can do something like if(location.href == '/some/url/') { (JavaScript code) ... }. That seems not practicable for me in this case, for with changing URLs, I'd have to change the URLconf and the .js-file (while using reverse() and {% url %} to prevent that elsewhere). I guess there is no possibility to make use of the named URLs here?
Has anyone an idea how to organize the JavaScript without having a file for every single template on the one hand and without killing performance unnecessarily?
I don't know that this question is specific to Django - similar issues come up managing Javascript in all sorts of systems.
That said, I usually try to tier my Javascript files, so that truly global scripts and libraries are included in one file, scripts specific to a section of the site are included in a set of section-specific files, and scripts specific to a single page are included in yet another site of page-specific files (or in inline code, depending on the context).
Django has good support for this approach, because you can tier your templates as well. Include the global script in your base.html template, then create a mysection-base.html template that inherits from base.html and just adds the Javascript (and CSS) files specific to that section. Then subpages within that section can inherit from mysection-base.html instead of base.html, and they'll all have access to the section-specific scripts.
I find django-compressor invaluable as it automatically compresses and minifies your JavaScript and CSS pre-deployment. It even automatically handles SASS, LESS and CoffeeScript if they float your boat.
Apps from http://djangopackages.com/grids/g/asset-managers/ may help.
You use modular javascript.
Choose your packager of choice (mine is browserify) that packages all your modules into one package that you minify and gzip. You send this file to the client and it is cached.
This means you have all your code cached, minimize HTTP requests and stay lean and efficient.
And since you have modular code you just load your code as you would normally.
Personally I would use some form feature detection to load modules. You can choose to feature detect on almost any feature (some css selector, routes, url segments).
Feature detection would look like this :
var Features = {
"class": "name",
"class2": "name2",
"dynamic-scroll": "dynamic-scroll",
"tabstrip": "tabstrip",
...
}
for (var key in Features) {
require(Features[key]);
}
Where as routing with davis would look like
Davis(function() {
this.get("blog", function(req) {
require("blog")(req);
});
this.get("blog/:post", function(req) {
require("blog-post")(req);
});
this.get("shop", function(req) {
require("shop")(req);
});
...
});
Alternatively you can try an event driven architecture. This means each module binds to events
// some-module
mediator.on("blog-loaded", function() {
// load in some libraries
// construct some widgets
mediator.emit("blog-ui-build", widgets);
});
And you would need some bootstrapping in place to kick off the event loop. Feel free to look at an EDA demo
I have created the PHP side of a modular AJAX/PHP framework and now I am trying to implement the client side.
From my previous experience with modular web applications I know that sometimes multiple instances of one particular module are needed. For example, a web based two player game with page parts for each user.
On PHP side I have assigned a unque ID to each constructed instance of the module and I can pass this UID to the browser but I have no idea how to implement the Javascript side of this module instance.
Modules can be loaded all in one go or loaded separately through AJAX (I am using jQuery).
Now I am using a modular approach that I found in some article, but I can redesign it in some other way if that would help to solve this issue without sacrifising modularity and private/public code separation. For now let's say I have a js file with the following:
//Self-Executing Anonymous Func
(function( MyModule, $, undefined ) {
// My Uid
MyModule.UID = "";
//Public Method
MyModule.onLoad = function() {
alert("Hey, you loaded an instance of MyModule with UID " + MyModule.UID);
};
//Private Methods follow
function somethingPrivate( ) {
}
}( window.MyModule = window.MyModule|| {}, jQuery ));
I am using Smarty for templates. Let's say, I have a simple module template like this:
<div id="{$contents.moduleuid}">
here goes the contents of the module which can be accessed from MyModule Javascript code by using this unique moduleuid
</div>
I have set up the server side so each module automatically appends additional template with Javascript:
<script type="text/javascript">
/*
TODO: here I have access to the {$contents.moduleuid}
But I have no idea what to put here to create a unique instance of MyModule
(also it might need loading js file if it was not loaded yet) and I should also set for
this instance MyModule.UID to {$contents.moduleuid}
and also call MyModule.onLoad for this instance after it has loaded its Javascript.
*/
</script>
I am not experienced with advanced Javascript topics so it is unclear to me how I can create a separate instance of MyModule for each module which gets construced server-side? Is it possible at all to create instances of self-executing anonymous functions? If not, then how can I implement and clone Javascript objects with separated private/public code?
My recommendation is to keep the client side and server side loosely coupled. Try to build your modular client application completely with HTML/JS without PHP tricks on it. As I understand, each of your module (or UI component) need to be loosely coupled from the others. In such case there are several other concerns you might need to look for:
How to keep your UI component structure (html), presentation (css) and behavior (JS) self contained (for example in a single folder), so that it can live or die independently
How these self contained components interact with each other
How to manage the configurations/settings of your UI components
Should you be using MVVM or MVC pattern to organize and bind the view to your PHP model
Who decides when to create/show/hide your UI components (for example based on URL for bookmarking)
If your client is a large and complex application, you might need to look for other concerns such as JS optimization, unit testing, documentation, product sub modules, etc.
Have a look at the BoilerplateJS Javascript reference architecture we put forward at http://boilerplatejs.org. It suggests ways to address all concerns I discussed above.
Since you are already using jQuery, you could create a jQuery plugin. The plugin should behave the way you need, and I believe you won't even need a unique ID. Considering each of your module's instance is contained in a div with class module-container, your jQuery code for adding client-side behavior to the divs would be something like this:
$(function(){
// DOM content is loaded
$('.module-container').MyPluginName();
});
The minimal plugin code would be (considering it's in a separate .js file):
(function($){
$.fn.MyPluginName = function() {
// Return this.each to maintain chainability
return this.each(function() {
// Keep a reference to your unique div instance.
var $this = $(this);
// Plugin logic here
});
};
})(jQuery);
If you are using jQueryUI, I also recommend you also look into the "widget factory" (intro, docs), which serves as a base for building powerful, normalized jQuery plugins.
When developing JavaScript, I tend to separate JavaScript code out into different files and then run a script to concatenate the files and compress or pack the resulting file. In the end, I have one file that I need to include on my production site.
This approach has usually worked, but I've started to run into a problems with prototypal inheritance. Specifically, if one class inherits from another class, the file for the parent class needs to be included already for the inheritance to work. If the concatenation script I'm using is simply concatenating a directory full of files, the child class might occur in the code before the parent class. Like this:
parent_class.js
var Namespace = Namespace || {};
Namespace.Parent = function () { };
Namespace.Parent.prototype.doStuff = function () { ... };
child_class.js
var NameSpace = Namespace || {};
Namespace.Child = function () { ... };
Namespace.Child.prototype = new Namespace.Parent();
The only way this works is if parent_class.js is included before child_class.js, which might not happen if the concatenation script places the child code before the parent code.
Is there a way to write this code so that the functionality is the same, but the order in which the code is written no longer matters?
Edit: I forgot that I'm using namespaces as well, so I added that to the code as well, which might change things a little bit.
When I've worked on large apps, I've use requireJS to divide my code into modules, and make sure those modules are loaded in the correct order. Then I just mark my derived class as depending on the parent class.
RequireJS comes with a tool that will combine and minify all your modules for production deployment as well.
If the concatenation script I'm using is simply concatenating a directory full of files, the child class might occur in the code before the parent class.
Is it too simple a solution to prepend a sort order value to each filename, and sort by name before performing the operation?
eg:
01_parent.js
02_child.js
Otherwise, perhaps maintaining a preordered list of files in a separate file. Maybe even go one step further and provide a hierarchical dependancy structure in xml to parse? This might be over-engineering the solution a bit :-D
Some options for ways to deal with the problem if you had no way of determining how the javascript files are written.
I suppose you could do something really hackish like this:
setTimeout(function() {
if(typeof(Parent) != "undefined" && typeof(Child) != "undefined") {
Child.prototype = new Parent();
} else {
setTimeout(arguments.callee, 50);
}
}, 50);
That code could also be anywhere, and would continually run until both the Parent and the Child were loaded...But I wouldn't do that.
Personally, I would just make sure your files are combined in the correct order.
If the objects are "namespaced" then you either have to switch to a build solution that incorporates in-order concatenation or use something like dojo.require (but obviously not dojo specific). Basically you want a framework that will provide you with the ability to check if a given identifier is available and pause execution and include it (via evaled ajax or an inserted script tag) if it isn't.