I have several javascript (coffee script) files in my Rails javascripts directory. The code is logically divided into different files. However, each file starts with $(document).ready -> and some files share common helper functions.
What is the best way to factor out the helper functions? Should I just put them all in some other file that gets included earlier in application.js?
Also, is it normal to have the code divided up the way mine is, where every page does $(document).ready ->? Doesn't this mean that all of my code is called on every page, regardless of whether or not it is relevant? Are there alternatives to this organization?
I'm not sure if this question is specific to Rails or relevant to javascript and jQuery in general.
I do think this is a Rails question, and a good one.
The normal paradigm in Rails, where "global" stuff goes in application.* is a little messed up with the asset pipeline, since the application.js really acts as a manifest, rather than a common file. Of course you could add stuff there, or even create an application.js.coffee for your common code. I decided to create a file called common.js.coffee (and in another case shared.js.coffee), which in my case was automatically handled by the require_tree . directive.
(Update based on comment from #jonathan-tran) In some cases, you may just want methods called on document ready for all pages -- for example, I used this to make a datepicker available to any field in any view. If, instead you want methods (actually global functions) available to be callable, you'll need to export the coffeescript functions to variables, for example by attaching to the window object. You can do both in a shared file.
It is true that if you use generators you'll end up with files for every controller which, if there's no specialized code result in a series of redundant $(document).ready -> statements when assets are compiled. So just get rid of the ones you don't use. But following the pattern of separating functionality specific to a page makes good sense to me and works well -- I know where to look to find stuff and that's worth a lot as a project grows.
And another rule I have learned with Rails: go with the flow. If that's how Rails does it, it's probably a good way. They do indeed think about these things. Don't fix what works :-)
Related
For the first time as a jnr progammer I'm looking at how to write my js code so that it's isolated to certain pages within my Rails app. I figure this is a sensible thing to do for the purposes of;
a) certain js i write I only want to target a certain element on an individual page, not other elements on other pages within the same controller that may get picked up due to using a class selector for example and marking every single thing with unique id's doesn't seem too neat. (I see that rails already has controller isolation through the naming of js files by controller, but further dividing things up into action/page names js files doesn't sound nice either!).
b) perhaps in finding a solution to only running js on certain pages it could mean that the js only needs to be loaded on certain actions thereby saving on load times.
There's a well attended question here with folks approaches here but most answers are 6 years old now and another nice post about the so called 'Garber Irish method of Dom ready execution' here that sounds quite nice, but again this is quite old now and all based on Rails 3...
So without wanting to create duplicate content I'm seeking to get a refreshed answer to this same question and find out whether there's a best practice now established these days.
Thanks
Convention:
Rails when using scaffold generator by default creates corresponding
app/assets/javascripts/MODEL_NAME.coffee
app/assets/stylesheets/MODEL_NAME.scss
which separates JS and CSS logic by "controller" name
Personal Preference:
Rather than controller-based segregation, I prefer layout-component-based segregation. Best explained through a sample code below:
app/assets/javascripts/application.js
//= require_tree ./layouts/
app/assets/javascripts/layouts/__shared.coffee
// WRITE GLOBAL JS LOGIC HERE FOR ANY LAYOUT
app/assets/javascripts/layouts/application/__shared.coffee
// WRITE GLOBAL JS LOGIC HERE ONLY FOR THIS "application" LAYOUT
app/assets/javascripts/layouts/blog/__shared.coffee
// WRITE GLOBAL JS LOGIC HERE ONLY FOR THIS "blog" LAYOUT IF YOU HAVE ANOTHER layout such as "layouts/blog.html.erb"
app/assets/javascripts/layouts/application/components/header.coffee
// WRITE JS code here for the header component
app/assets/javascripts/layouts/application/components/footer.coffee
// WRITE JS code here for the footer component
app/assets/javascripts/layouts/application/components/users/__shared.coffee
// You can also divide a component into subcomponents just like this
// WRITE JS code shared amongst all users subcomponents
app/assets/javascripts/layouts/application/components/users/form.coffee
// WRITE JS CODE HERE regarding the form that creates or updates a User
app/assets/javascripts/layouts/application/components/users/list.coffee
// WRITE JS CODE HERE regarding the table listing all users
You also structure the CSS files in the same way, matching the components
I use require.js to do lazy loading for a Javascript app. I would love to switch to a meteor stack but right now it looks like Meteor sends the whole app (all the templates) through on the initial load. Has anyone had success with require.js and meteor or any other implementation?
You're asking different questions, but certainly they are connected. The first is about loading additional javascript code into your meteor app. Of course you can use thing like requirejs. This should work fine supposing your lazy code is located in the public directory of your meteor project. However, my experience is that requirejs goes mad when the contents of public gets updated often, so for example in the development environment. Maybe it's a matter of customizing the library, but I would rather recommend using some lightweight homebrewed package. Look here, if you need some inspiration.
The second question is about lazy template definition. Each template consists of two parts. The first is its html code, written in handlebars syntax, the second is all the javascript code which you write to define how your template should behave (e.g. helpers, event handlers). The second part is easy, as long as we assume that we already know how to load the lazy code (see the above paragraph) and the template, lets call it myLazyTemplate, is already defined, so basically speaking Template.myLazyTemplate is not undefined. So how to achieve the latter?
To dynamically define a new template you'll need to call
Template.__define__(name, raw_func)
on the client. So the last question is "what is raw_func?". This is a compiled version of your html code which is normally created automatically on the server and then sent down the wire to the client when the app gets loaded (look here to see how it's done in meteor). But we want to do it dynamically, right?
So the idea is to compile the template code manually with a help of the Handlebars.to_json_ast routine. You can feed it with your template html code, and the output is some javascript array that can be sent to the client anytime by the method we've already talked about. The last thing you need to do is to call Handlebars.json_ast_to_func on the client, using the data sent from the server as the only argument. The output produced by Handlebars.json_ast_to_func is the raw_func you can use to produce myLazyTemplate template.
I'm aware that this is only a rough idea, not the whole solution to your problem. I hope this will help you to figure out the final solution on your own.
Scenario:
A web site with x number of pages is being served with a single, concatenated JavaScript file. Some of the individual JavaScript files pertain to a page, others to plugins/extensions etc.
When a page is served, the entire set of JavaScript is executed (as execution is performed when loaded). Unfortunately, only a sub-section of the JavaScript pertains directly to the page. The rest is relevant to other pages on the site, and may have potential side-effects on the current page if written poorly.
Question:
What is the best strategy to only execute JavaScript that relates directly to the page, while maintaining a single concatenated file?
Current solution that doesn't feel right:
JavaScript related to a specific page is wrapped in a "namespaced" init function for that page. Each page is rendered with an inline script calling the init function for that page. It works hunky-dory, but I would rather not have any inline scripts.
Does anyone have any clever suggestions? Should I just use an inline script and be done with it? I'm surprised this isn't more of an issue for most developers out there.
Just use an inline script. If it's one or two lines to initialize the JavaScript you need that's fine. It's actually a good design practice because then it allows re-use of your JavaScript across multiple pages.
The advantages of a single (or at least few) concatenated js files are clear (less connections in the page mean lower loading time, you can minify it all at once, ...).
We use such a solution, but: we allow different pages to get different set of concatenated files - though I'm sure there exists different patterns.
In our case we have split javascript files in a few groups by functionality; each page can specify which ones they need. The framework will then deliver the concatenated file with consistent naming and versioning, so that caching works very well on the browser level.
We use django and a home-baked solution - but that's just because we started already a few years ago, when only django-compress was available, and django compress isn't available any more. The django-pipeline successor seems good, but you can find alternatives on djangopackages/asset-managers.
On different frameworks of course you'll find some equivalent packages. Without a framework, this solution is probably unachievable ;-)
By the way, using these patterns you can also compress your js files (statically, or even dynamically if you have a good caching policy)
I don't think your solution is that bad although it is a good thing that you distrust inline scripts. But you have to find out on what page you are somehow so calling the appropriate init function on each page makes sense. You can also call the init function based on some other factors:
The page URL
The page title
A class set in the document body
A parameter appended to your script URL and parsed by the global document ready function.
I simply call a bunch of init functions when the document is ready. Each checks to see if it's needed on the page, if not, simply RETURN.
You could do something as simple as:
var locationPath = window.location.pathname;
var locationPage = locationPath.substring(locationPath.lastIndexOf('/') + 1);
switch(locationPage) {
case 'index.html':
// do stuff
break;
case 'contact.html':
// do stuff
break;
}
I'm really confused exactly why it doesn't feel right to call javascript from the page? There is a connection between the page and the javascript, and making that explicit should make your code easier to understand, debug, and more organized. I'm sure you could try and use some auto wiring convention but I don't think it really would help you solve the problem. Just call the name spaced function from your page and be done with it..
I recently read that for a faster web page load it's a good practice to put the JavaScript links at the end. I did, but now the functions of the referenced file doesn't work. If I put the link at the beginning of the page, everything is fine.
Does this thing of putting JavaScript at the end work only under certain circumstances?
I went through some testing with this as well. If you are loading a Javascript file it is faster to put it at the end BUT it does come with some important caveats.
The first is that doing this often made some of my visual effects noticeable. For example, if I was using jQuery to format a table, the table would come up unformatted and then the code would run to reformat it. I didn't find this to be a good user experience and would rather the page came up complete.
Secondly, putting it at the end made it hard to put code in your pages because often functions didn't exist yet. If you have this in your page:
$(function() {
// ...
});
Well that won't work until the jQuery object is defined. If it's defined at the end of your page the above will produce an error.
Now you could argue that all that styling code could be put in the external file but that would be a mistake for performance reasons. I started off doing that on a project and then found my page took a second to run through all the Javascript that had been centralized. So I created functions for the relevant behaviour and then called the appropriate ones in each page, reducing the Javascript load run time to 50-200ms.
Lastly, you can (and should) minimize the load time of Javascript by versioning your Javascript files and then using far-futures Expires headers so they're only loaded once (each time they're changed), at which point where they are in the file is largely irrelevant.
So all in all I found putting putting the Javascript files at the end of the file to be cumbersome and ultimately unnecessary.
You do have to pay attention to the ordering, but libraries like JQuery make it easy to do it right. At the end of the page, include all the .JS files you need, and then, either in a separate file or in the page itself, put the Jquery calls to act on the page contents.
Because JQ deals with css-style selectors, it's very easy to avoid any Javascript in the main body of the page - instead you attach them to IDs and classes.
This is called Unobtrusive Javascript
Every Yahoo YUI example file I remember has almost all the JavaScript at the end. For example,
Simple Event Handling
Basic Drag and Drop
JSON: Adding New Object Members During Parsing
It looks like Yahoo Practice is roughly "library code at the beginning of <body>, active code at the end of <body>."
Beware, though, this may result in the Flash of Unstyled Content syndrome.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
How do you guys organise your javascript code? I know that it is good practice to store the code in an external .js file and this is fine for code that is run in multiple pages but how do you organise if you have, say 20 pages, and only 1 of them uses a particular function. Do you create a new external file for that 1 page or create the code inline?
I do two things:
I put all my site's Javascript in one or more files;
I include that file or files on every page that uses any Javascript;
Those files are cached effectively such that they are only ever downloaded once (until they change); and
Pages call the functions they need from those external files.
If your site is one page then put it inline.
If your site is 20 pages and they all use a little bit of Javascript, put it all in one files, include it on every page and call the functions with inlien Javascript as necessary in each file.
I write about this and more in Supercharging Javascript in PHP. Sure it's PHP-specific but the principles are universal.
Basically every extra HTTP request is a problem. So if you have 20 pages each with a different Javascript file then that's a problem, even if those files are small. It's better to combine all that Javascript in one file, download it just once (with effective caching) and just use what you need.
To give you an example. External JS file contains:
function delete_user(evt) { ...}
function suspend_user(evt) { ... }
function unsuspend_user(evt) { ... }
One of your Web pages contains:
$(function() {
$("#delete").click(delete_user);
$("#suspend").click(suspend_user);
$("#unsuspend").click(unsuspend_user);
});
This way you get an external JS that contains all your site's Javascript but none of it is actually used. Use comes from inline code in the pages. This way there is no overhead of having the larger JS file.
Whatever you do, don't put in ALL initialization in your Javascript file. I once made this mistake and put a huge $(function() { ... } function into the external file on the grounds that if the relevant IDs weren't in the page, nothing would happen. There ended up being enough of this code to add nearly half a second to the page load time (and the site wasn't that big).
The browser shouldn't redownload the javascript file once it has it, so I would put it into the single Javascript file. That saves another connection/request to the webserver, and it keeps the code in one place rather than having script tags and code in your HTML/JSP/PHP/etc files. I.e., it's more maintainable, and it's very little overhead to get the code (unless it's a 1000 line monster! but that's another problem entirely) even if it isn't used.
Not that I don't have script blocks in some files, for very very specialised cases. In the end it comes down to what you are happy with - but consistency across the project is what is most important, so don't have a one off script in one place, and then do something different elsewhere.
ALWAYS put JavaScript in a file external to HTML. The problem with JavaScript that exists in page is that it typically exists in the global namespace, which could easily cause namespace collisions that makes code crash.
So, always put JavaScript in an external file.
With that said you should organize your code in an object-oriented manner. Try to capture an entire application representing a single point of execution to a single named function. Always write your code using a single var command per function, which should go at the top of the function, so that your code is easier to read. Ensure all variables and functions are declared with a var command or they will into the global namespace, which is potential failure. Put sections of execution of a giant function into smaller child functions, because this makes code easier to maintain when you can point to a particular named block when debugging or writing enhancements.
Also, always run your code through JSLint to verify syntax accuracy.
If you only have a small amount of code I don't believe it is worth the effort to split it up.
Depending on how much JS you have, consider a build process that can concatenate your separate JavaScript files into one, minified single download.
YUI is incredibly modular. They have a 'core' set of includes and then you can supplement these with the widgets you actually use. For instance, I have no interest in using their file uploader widget so never include the JS for it.
Cache the JS for a date far in the future. If you need to make a change, append a version stamp to the end of the SRC attribute, i.e. my-code.js?v=91
Use namespaces to avoid polluting the global scope. Again, YUI is very organised in this regard - see the YAHOO.namespace() function.
I would say put it in an external file. Chances are you will need to add new functions to it anyway, but also, from an SEO point of view, keeping it in an external file is preferable.
For only one function it would be better to code inline.
I won't include a 100KB Util script file for just calling a Trim function inside it.
If the file is already cached then it won't be a problem if you refer this in a page which calls only one function inside the file.
If I have a choice, I put the JS functions in external files, but not all in one file. Cache or no cache, I group files by their functionality and organize them similar to Java packages. If I have an utility function, say something generic like trim(), that goes to the top most JS file and all pages can use it. If I have something specific to a part of the site (unused in other parts) that goes in something like a sub-package, just for that specific part… and so on.
Of course you must use common sense so you don’t overdo it, and have let’s say a function per JS file. If you have fine grained JS files, that will affect you when your site evolves and you find yourself moving functions from sub-packages to an upper package or the other way around. I think one parent utility JS and one JS per site functionality should, most of the times, suffice.