2

I am developing an app on a page that uses jQuery 1.2.6, but I would like to use jQuery 1.4.2 for my app. I really don't like to use multiple versions of jQuery like this but the copy on the page (1.2.6) is something I have no control over. I decided to isolate my code like this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<script type="text/javascript" src="jquery-1.2.6.min.js>
<script type="text/javascript" src="pageStuff.js"></script>
</head>
<body>
Welcome to our page.
<div id="app">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="myStuff.js"></script>
</div>
</body></html>

The file myStuff.js has my own code that is supposed to use jQuery 1.4.2, and it looks like this:

(function($) {  //wrap everything in function to add ability to use $ var with noConflict
    var jQuery = $;
    //my code
})(jQuery.noConflict(true));

This is an extremely simplified version, but I hope you get the idea of what I did. For a while, everything worked fine. However, I decided to use a jQuery plugin in a separate file. I tested it and it acted funny. After some experimentation, I found out that the plugin was using the old version of jQuery, when I wanted it to use the new version. Does anyone know how to import and run a js file from the context within the function wrapping the code in myStuff.js?

In case this matters to anyone, here is how I know the plugin is using the old version, and what I did to try to solve the problem: I made a file called test.js, consisting of this line:

alert($.fn.jquery);

I tried referencing the file in a script tag the way external Javascript is usually included, below myStuff.js, and it came up as 1.2.6, like I expected. I then got rid of that script tag and put this line in myStuff.js:

$.getScript("test.js");

and it still came back as 1.2.6. That wasn't a big surprise -- according to jQuery's documentation, scripts included that way are executed in the global context. I then tried doing this instead:

var testFn = $.proxy($.getScript, this);
testFn("test.js");

and it still came back as 1.2.6. After some tinkering, I found out that the "this" keyword referred to the window, which I assume means the global context. I am looking for something to put in place of "this" to refer to the context of the enclosing function, or some other way to make the code in the file run from the enclosing function. I noticed that if I copy and paste the code, it works fine, but it is a big plugin that is used in many places, and I would prefer not to clutter up my file with their code. I am out of ideas. Does anyone else know how to do this?

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
  • To be short, so many, *many* things are not designed with this in mind, you can't run 2 versions of jQuery and have you plugins work out of the box, whoever owns `window.jQuery` is going to win that fight. Also...you're doubling the javascript payload for your client, you should find a different way to solve this problem. – Nick Craver May 14 '10 at 02:35
  • You should close all ``. Just in case you're having some syntax problems (just based on the code you provided above) – sirhc May 19 '10 at 05:00
  • which plugin are you trying to insert – Starx May 19 '10 at 05:08
  • I added the closing script tags in my example above. I have been using closing script tags in my project. The problem has nothing to do with that. I am trying to use the jCarousel plugin. You can see it at http://sorgalla.com/jcarousel/. – Elias Zamaria May 19 '10 at 17:21

5 Answers5

3

When you load jQuery, all it does is to create a root jQuery object and set window.jQuery and window.$ to point to it. So you can simply load the old jQuery, copy window.jQuery to window.jQuery126, load the new jQuery, copy window.jQuery to window.jQuery142, and then change window.jQuery and window.$ whenever you want a plugin to use the other version.

This is an ugly and unstable hack but it should work as long as

  1. all code written for the non-default version is encapsulated with (function($){...})($) (proper jQuery plugins should do this, and you can easily ensure it for your own code)
  2. scripts used with different jQuery versions don't mess with each other's stuff (such as one binding an event handler and the other trying to unbind it)

Another, simpler but less maintainable solution is to simply edit the plugin to look like (function($){...})(jQuery142) instead of (function($){...})(jQuery)

Tgr
  • 27,442
  • 12
  • 81
  • 118
  • I tried your solution and it works. Soon I am going to post the details of how I did it. I think I will give you the bounty unless something better comes along in the next few days. – Elias Zamaria May 19 '10 at 19:19
2

Versions

There is a script that specifically attempts to fix this problem: Versions . It's rather old, though.

It's just a neat helper for doing the switching of versions.

While I have not tested this script myself, you can do something like this based on the GitHub repository for the project:

<!-- Old jQuery and it's plugins -->
<script type="text/javascript" src="../jquery/jquery-1.2.6.js"></script>
<script type="text/javascript" src="test-plugin-for-old.js"></script>

<!-- Include the Versions helper -->
<script type="text/javascript" src="versions.js"></script>

<!-- Include a new jQuery -->
<script type="text/javascript" src="../jquery/jquery.js"></script>
<script type="text/javascript">
Versions.add('jquery', 'latest', jQuery.noConflict(true));
Versions.add('jquery', 'default', jQuery);
</script>

<!-- Load a plugin into the new jQuery -->
<script type="text/javascript">jQuery = Versions.use('jquery', 'latest');</script>
<!-- Do whatever you need with the new jQuery -->
<script type="text/javascript" src="test-plugin-for-new.js"></script>
<!-- Finish up and use the old jQuery back -->
<script type="text/javascript">jQuery = Versions.use('jquery', 'default');</script>

On-Demand JavaScript Loading (Lazy Loading)

With that out of the way, there are some ways to do on-demand JavaScript loading, but I don't think they will work well with loading of jQuery since jQuery modifies and requires the presence of the window object.

Also, you might want to read about script loading. There's more details about it in this StackOverflow question and the article pointed out.

RequireJS

In the list, RequireJS is not mentioned. It's rather new and you can do things like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
  <script type="text/javascript" src="pageStuff.js"></script>
</head>
<body>
Welcome to our page.
<div id="app">
    <script type="text/javascript" src="require.js"></script>
    <script type="text/javascript">
      require({context: "1.4.2"}, 
        ["http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"],
        function() {
          console.log($.fn.jquery); // returns 1.4.2
          // myStuff.js
        });
      require({context: "1.2.6"}, 
        ["http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"],
        function() {
          console.log($.fn.jquery); // returns 1.2.6
        });
    </script>
</div>
</body></html>

I always go for the newest stuff, haha, so I'm recommending this. However, due to the nature of loading the jquery (it modifies window as mentioned above), all references to $ and jQuery outside and beyond the last require in the HTML code block above, will refer to the jQuery 1.2.6.

Community
  • 1
  • 1
sirhc
  • 6,097
  • 2
  • 26
  • 29
0

jQuery is actually a variable in the window context. So whenever you load a jQuery library it will create that variable in the window context if you load it outside any function.

I believe you could do something like this to achieve what you want. Although somewhat restrictive, theoretically it should work:

<script type="text/javascript">
  // Get new jQuery code
  $.get( "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", function (data, textStatus, XMLHttpRequest){
    if(data){
      // data should contain the file content. eval() it
      eval(data);

      // Use jQuery 1.4.2 from here, only inside this callback function.
    }
  }, "html" );
</script>

This is ugly, but should do the trick.

Seb
  • 24,920
  • 5
  • 67
  • 85
  • I tried that and it didn't work. Nothing I put in the callback function would execute. I don't know if the $.get function exists in jQuery 1.2.6. I may try to find out or I may try something else. – Elias Zamaria May 19 '10 at 01:01
  • $.get exists since 1.0. Try putting `alert(1)` inside the callback function, and only that so see if it executes. If not, then there's probably something wrong in your syntax. – Seb May 19 '10 at 01:31
  • I tried this: and none of the alert statements executed. – Elias Zamaria May 19 '10 at 17:18
  • $.get does exist on 1.2.6. Just create a plain HTML file including 1.2.6 and do this: alert($.get). You should get the function code. If you don't, then there's something wrong elsewhere. – Seb May 19 '10 at 17:47
0
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" />
<script type="text/javascript">
    $new = $.noConflict();

    document.write("$new = " + $new.fn.jquery + "<br/>");
    document.write("$ = " + $.fn.jquery + "<br/>");

</script>

I just tried this and it seems to work, $ reports to be the old version and you just have to make sure to use $new when invoking jquery for the stuff you want to do with 1.4.2

  • I know about this. I was just hoping I could use the new version of jQuery without adding a new variable to the global namespace. I don't know if the code on the page hosting my app will change, and use the name $new (or whatever I pick) for some purpose. Also, what if I am trying to include a plugin in an external file? Do I need to change all the occurrences of $ and jQuery to $new? I was trying to avoid doing that. I don't like to change the source of complex plugins. Also, what if the plugin is on some other server and I can't directly modify the source? – Elias Zamaria May 19 '10 at 17:25
0

For now, I am going along with Tgr's solution. Here are the details of how I implemented it:

page.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<script type="text/javascript" src="jquery-1.2.6.min.js>
<script type="text/javascript" src="pageStuff.js"></script>
</head>
<body>
Welcome to our page.
<div id="app">
    Here is our app.
    <script type="text/javascript">
        var oldJQuery = $;
    </script>
    <script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="myStuff.js"></script>
</div>
</body></html>

myStuff.js:

var jq142 = jQuery.noConflict(true);
(function($) {
    var jQuery = $;
    $(function() {
        //init app stuff
        includeScript("jCarousel.js", function() {
            //init carousel stuff
        });
    });
})(jq142);

function includeScript(URL, callback) {
    window.$ = window.jQuery = jq142;
    window.$.ajax({
        url: URL,
        dataType: "script",
        error: function() {
            window.$ = window.jQuery = oldJQuery;
        },
        success: function() {
            if (typeof callback != "undefined")
                callback();
            window.$ = window.jQuery = oldJQuery;
        },
        async: false
    });
}

I still don't like the idea of putting another variable (jq142) into the global namespace, but I couldn't think of any reasonable way around it. If I come up with something better, I will post it here.

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148