managing document.ready event(s) on a large-scale website

空扰寡人 提交于 2019-12-04 07:39:38

问题


NOTE: I have now created a jQuery plugin which is my attempt of a solution to this issue. I am sure that it could be improved and i've probably overlooked lots of use cases, so if anyone wants to give feedback feel free :-) https://github.com/WickyNilliams/ReadyBinder

I don't have a problem as such, but thought this would be an interesting point for discussion, and i hope people have some interesting ideas for this.

Basically, i work on a large-scale website and increasingly we are writing more and more JavaScript. This is fine, i enjoy JS' unique approach and i find the quirkiness in some of the darker annals of the language to be endearing ;-) However one thing that has always bugged me is how to manage document ready events as they get increasingly large over time (and as a result less focused/specific to the page being served)

The problem is that we have one JS file (merged & minified, though that's kind of inconsequential for my questions). Most of the JS is written using the revealing module pattern, and jQuery is our framework of choice. So all our JS funcitonality is logically grouped into methods, namespaced, and then right at bottom of the script file we have this

$(function(){
    //lots of code here, usually calling encapsulated methods 
    //on our namespaced revealing module
});

The problem is that not all of the code in this document ready handler pertains to every page. For instance, on one page only 10% of it might be relevant, on another perhaps 80% might be relevant. To me, this feels incredibly wrong, i feel i should only execute the code i need per page, mainly for efficiency, but also maintainability.

I've searched google for approaches to this issue, but cannot find anything, maybe i'm just searching for the wrong thing!

Anyway, so my questions are:

  • Has anybody ever thought about this issue?
  • Is it actually an issue in other people's opinion?
  • Do you have a large, all-encompassing document ready handler in your code or is it more focused for the type of page being served?
  • If the latter, how do you manage this? Multiple handlers which get switched in JS or dynamically spitting out the document ready handler server-side?

Look forward to people's thoughts on the matter.

Cheers


回答1:


This is what i have done in my rails mvc project with heavy javascript, i have created a separate namespace for the controllers in js which resembles the rails controller

class BusinessController
   def new
   end  
   def index
   end
end

and

Var Business =  {
      init : function(action) {
         //code common to the Business module
         //even add the common jquery doc ready here, its clean
         //and call the page specific action
         this[action]();
      },
      new : function() {
             // check jquery document ready code here, thats relevant to this action
             //New rental page specific code here
      },
      index : function() {
             //  check jquery document ready code here, thats relevant to this action
             //index rental page specific code here 
      }
}

and on the view code(server side) just initiate the page specific js code by

<script type="text/javascript"> 
 <%= controller_name %>.init('<%= controller.action_name %>'); 
//which will render to
//  Business.init(index);
</script>

You can pretty much tweak this to make it work in any language. And this approach doesn't care whether you have a single file or multiple files.




回答2:


I think the must intuitive solution to this problem is, simply, to reduce the amount of work performed at load time.

If your code looks something like:

$(function () {
    // Binds a single click event (which will never be triggered if the hypothetical
    // widget is never used), regardless of how many widgets are used on the page.
    $(document).delegate('.rating-widget', 'click', namespace.rating.handler);
    // and so on for similar things which simply require event handler registration

    // Initializes each of the forms on the page, letting the initializer take care
    // of any details (datepicker widgets, validation, etc) based on the root element
    $('form.fancy').each(namespace.fancyform.initializer);
    // and so on for similar things that require initialization

    // ... (for each type of thing on the page requiring initial setup,
    //      find the most general/high level pattern that sufficient)
});

things should be relatively maintainable, even if there are a few dozen lines. There's no complicated/interesting logic at this level to update/remember when working with the component code and, because everything at this level is trivial, it's easy to read. At this point, there's no need to invent some complicated registration system; it's not going to be any simpler than .delegate and .each.

Note as well that all of the above gracefully handles the case where the component is not used. In those cases, little or no work is done and no conditional logic is necessary.

For interesting ideas about how you might implement the handlers/initializers, you might want to take a look at, for example, the "Contextual jQuery in practice" talk Doug Neiner gave at jQuery Boston last week.




回答3:


Great question. I actually handle this by using custom page load events. So in my core .js file I have a class like the following:

var Page = {
    init: function(pageName) {
        switch (pageName)
        {
            case: 'home': {
                // Run home page specific code
            }
            case: 'about': {
                // Run about page specific code
            }
            ...
        }
    }
}

You can call this a bunch of ways, either in a page-specific $(document).ready() or from the core script using some kind of URL parser (literally anything is possible with a URL parser):

$(document).ready(function() {
    // http://www.mydomain.com/about
    var currentPage = window.location.pathname;   // "about"
    Page.init(currentPage);    
});

window.location DOM reference: https://developer.mozilla.org/en/DOM/window.location




回答4:


First I put specific classes on the body or on specific containers e.g. articles, form-validation, password-meter, … . If I have an MVC app, I prefer to put the controller name into a body class. This does not hurt a lot and can be useful for styling, too.

Then on each document.ready block I check for the existence of such a class, and if it does not exist I return from this function. This approach is simpler as it does not have the main code inside an if clause. This approach resembles to assertions that are used to check prerequisites of a function.

Example:

$(function(){
    if($('body').hasClass('articles') === false){ return; }

    //body of the articles code
});

$(function(){
    if($('body').hasClass('password-meter') === false){ return; }

    //include password meter in page
});

The bonus of this approach is that no unwanted code can sneak into a ready callback, which makes it easier to reuse. The downside is that you get many callback blocks and if you do not pay attention duplicate code can easily sneak into your app.




回答5:


I know where you're coming from. Right webapps for mobile use it can feel silly making the user download a massive JS file full of irrelevant code (the JQuery API for one is a bad start!).

What I do, which may be right or wrong but certainly seems to work is include required functions on a page by page basis before the tag. (This position for the most part, gets around the document.ready issue). Yet can't help but feel this approach is too simple.

I am interested in hearing other answers though so it's a good question, +1.




回答6:


"The problem is that we have one JS file"

I'm not sure how you can avoid it as long as you are including the same (merged) JS file for all pages. You could use some server-side logic to merge different pieces to go with different pages, but then you presumably lose the benefits of client-side caching of the JS, so...

Projects I've worked on have (typically) used multiple JS files, a couple being common to every page, and the others being included or not as appropriate - some JS files included by only one page. This means that pretty much every page includes the common library functions whether they use them or not, but the common JS file gets cached so it doesn't really matter. Page-specific code really is page-specific.

I can't tell from your question whether you are aware of it, but you can have multiple document ready handlers (I believe jQuery executes them in the order they were bound?). So if you do split the JS up each JS file can include its own document ready. Of course that approach has its own overhead, but I think it's better than putting everything in the same file.




回答7:


Firstly, if you're using jQuery, document.getElementsByTagName isn't really necessary, you can just use $("tagname"). Have a look at the jQuery Selectors documentation for even more magic!

Your work-in-progress solution is the way I would go about it, but you don't need to go to the trouble of defining extra functions, getting bodyClasses, running a loop, or any of that jazz... why not just use jQuery's .hasClass() function?

$(function() {
    if($("body").hasClass("article")) {
        alert("some article specific code");
    }
    if($("body").hasClass("landingPage")) {
        alert("some landing specific code");
    }
});

Bam. All you need. If you want to be even more efficient, you can reuse one body query:

$(function() {
    var body = $("body");
    if(body.hasClass("article")) {
        alert("some article specific code");
    }
    if(body.hasClass("landingPage")) {
        alert("some landing specific code");
    }
});

Here it is on jsfiddle: http://jsfiddle.net/W8nx8/6/




回答8:


Having less javascript files minimizes the number of requests sent to the server and caching the files makes the second and third pages load faster. Usually, I think about the likelihood of some parts of a js file will be needed in the following pages (and not just the current page). This makes me categorize my js code into two types: code that goes in my "main.js", and even if not used, it is going to be downloaded by the client (but not necessarily executed, I will explain), and the second category of code goes in separate files. You could actually create multiple files grouped by probability. This depends on the details of your project. If it's a large scale project, running some statistical analysis on user behavior might reveal interesting ways of grouping js code. Now that the code has been downloaded to the client, we do not want to execute all of it. What you should do is have different layouts or pages. Place a partial in the layout or page. Inside the partial set a javascript variable. Retrieve the javascript variable in the javascript code and determine whether you want to execute some code or not.

i.e:

Create partial "_stats.html.erb", place this code in it:

<script type="text/javascript">
    var collect_statistics = <%= collect_statistics %>;
</script>

Place the partial in the pages you would like to collect statistics: show.html.erb

<%= render "_stats", :collect_statistics => false %>

Now in your js file, do the following:

$(document).load( function() {
 if (typeof collect_statistics != "undefined" && collect_statistics) {
  // place code here
 }
});

I hope this helps!




回答9:


I like the RameshVel approach. I also found the Paloma gem to create page-specific JavaScript. For instance:

Paloma.controller('Admin/Users', {
  new: function(){
    // Handle new admin user
  }
});


来源:https://stackoverflow.com/questions/7582176/managing-document-ready-events-on-a-large-scale-website

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!