views:

128

answers:

5

I'm developing JS that is used in a web framework, and is frequently mixed in with other developers' (often error-prone) jQuery code. Unfortunately errors in their jQuery(document).ready blocks prevent mine from executing. Take the following simple sample:

<script type="text/javascript">
    jQuery(document).ready(function() {
        nosuchobject.fakemethod();       //intentionally cause major error
    });
</script>
<script type="text/javascript">
    jQuery(document).ready(function() {
        alert("Hello!");                 //never executed
    });
</script>

Shouldn't the second ready block execute regardless of what happened in the previous? Is there a "safe" way to run jQuery(document).ready that will run even in the case of previous errors?

EDIT: I have no control/visibility over the error-prone blocks as they're written by other authors and mixed in arbitrarily.

A: 

Have you attempted wrapping the error-prone commands in try...catch brackets?

$(function(){
    try {
     noObject.noMethod();
    } catch (error) {
     // handle error
    }
});

$(function(){
    try {
     alert("Hello World");
    } catch (error) {
     // handle error
    }
});

To avoid potential confusion, $(function(){ ... }); is functionally the same as $(document).ready(function(){ ... });

Jonathan Sampson
The problem is that I don't have control/visibility over the blocks that throw the errors. In the sample above, the first jQuery(document).ready block comes from someone else's code. Thus unfotunately I don't have a way of wrapping them in try/catch blocks.This is part of a web framework where arbitrary plugins can load together in the head of the document.
3hough
You should mention in your question that you don't have access to their code.
Jonathan Sampson
+5  A: 

I haven't tried this code, but it should work (at least, the idea should anyway). Make sure you include it AFTER jquery, but BEFORE any potentially buggy scripts.

var oldReady = jQuery.ready;
jQuery.ready = function(){
  try{
    return oldReady.apply(this, arguments);
  }catch(e){
    // handle e ....
  }
};
jvenema
This seems to work quite well, regardless of whether it goes before or after the potentially buggy scripts. I think what I'll do is move the contents of my ready block (alert("Hello!") in the sample) to just after the catch block. That way my ready block can "append" itself to the end of the previous ready blocks.
3hough
Great solution. Never even thought of that.
Mark Ursino
Not sure what I was thinking, but this'll work no matter where it goes, as long as it's in the <head> tag and not in some onload event, since we're hijacking the ready method before it gets invoked on dom load.
jvenema
A: 

You could re-reference jQuery before each of your code blocks. Your code would then use the fresh instance of the library, and it would execute. I've tested this in Safari 4.0.3 on OSX.

<html>
<head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"&gt;&lt;/script&gt;
    <script type="text/javascript">
     jQuery(document).ready(function() {
            nosuchobject.fakemethod();       //intentionally cause major error
        });
    </script>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"&gt;&lt;/script&gt; 
    <script type="text/javascript">
        jQuery(document).ready(function() {
            alert("Hello!");                 //executed!
        });
    </script> 

</head>
<body>

<p>hello world</p>

</body>
</html>
simeonwillbanks
Terrible idea...this'll at the very least force the user to download the library twice, and could also cause issues with static variables and objects being overwritten.
jvenema
The user's browser only re-downloads the remote file when a cache buster is put in place. In my example, the browser reads the second jQuery reference from cache. It isn't pretty, but it helps solve the original problem. Regarding issues with static variables and objects, do you mean within the jQuery library itself, or user defined static variables and objects? In case of an overwrite, jQuery maps over itself. In my example, the correctly written second block would reference the fresh instance of jQuery. Can you please elaborate on the issues you mention? Thanks!
simeonwillbanks
Right at the beginning of jquery, you'll see this: jQuery.fn = jQuery.prototype = {...}. Since those objects aren't extended but are instantiated, anything that modifies or touches those properties (and indeed, anything else set statically at the jquery level) will be trashed. And even if the browser did cache the js, it will still have to re-parse it and re-load all the declared elements, degrading the user experience (not to mention it'd probably introduce memory leaks because there's no cleanup of any dom events).
jvenema
+1  A: 

To answer your question, both of the ready blocks are essentially combined into one given the way jQuery works:

<script type="text/javascript">
  jQuery(document).ready(function() {
      nosuchobject.fakemethod();       //intentionally cause major error
      alert("Hello!");                 //never executed
  });
</script>

So that's why its not alerting per the error above. I don't believe there is a way to fix this to make every ready function run regardless of former failures.

Mark Ursino
A: 

Unfortunately, that is unavoidable. It's JavaScript's counterpart of breaking the build.

Dave Ward