On my website I load most JavaScript asynchronously using RequireJs. Please see the following for my RequireJs configuration:
require.config({
paths: {
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery'
},
shim: {
'jquery.accordion': {
deps: ['jquery']
}
}
});
Say I have the following code defined within the body to load a file asynchronously:
require(['DisplayAccordion']);
Where DisplayAccordion.js contains the following:
define(['jquery', 'jquery.accordion'], function($) {
$(function() {
$('.xyz').accordion();
});
});
Note: jquery.accordion is simply a jQuery plugin which doesn't have AMD support and requires the global jQuery variable to be defined.
This works fine but now say I drop a script reference on my page to a third party library. For example:
<script src="//example.com/ThirdParty.js"></script>
Where the third party library loads it's own version of jQuery. Now I am getting the error:
Object doesn't support property or method 'accordion'.
After stepping through the code I found that it executes in the following order:
- ThirdParty.js
- jquery.min.js - third party version
- jquery.min.js - my version
- jquery.accordion.js - where $ points to my version reference of jQuery
- DisplayAccordion.js (callback function) - where $ points to the third party version of jQuery
Now I can see why I get the error because the plugin is attached to a different object. However I'm not sure why this would do this.
The information below will simply explain why using $.noConflict(true) will not work.
After some research on the issue. I modified my config to:
require.config({
paths: {
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery'
},
map: {
'*': { 'jquery': 'jquery-private' },
'jquery-private': { 'jquery': 'jquery' }
},
shim: {
'jquery.accordion': {
deps: ['jquery']
}
}
});
Where jquery-private.js is defined as:
define(['jquery'], function($) {
return $.noConflict(true);
});
Please note that this was taken from http://www.requirejs.org/docs/jquery.html#noconflictmap
Now it executes in the following order:
- ThirdParty.js
- jquery.min.js - third party version
- jquery-private.js (callback function)
- jquery.min.js - my version
- jquery.accordion.js - where $ is undefined
- DisplayAccordion.js (callback function) - where $ points to the third party version of jQuery
As you can imagine this wouldn't work either since $ is undefined within the jquery.accordion.js file.
After abit of further debugging I discovered the third party library also calls:
$.noConflict(true);
I think I understand what's going on here. When it calls $.noConflict(true) within the third party library it tries to set the global variables $ and jQuery to the previous version. However since no previous version has loaded it is set to undefined.
Now when it calls jquery-private.js and returns $.noConflict(true) it will return the global jQuery variable which has been set to undefined. However it will now set the global jQuery variable to the third party version of the library.
So when it loads jquery.accordion $ is undefined. But when it next calls DisplayAccordion.js it is now referencing the third party version of the jQuery library.
I'd appreciate it if someone could suggest a fix. Thanks