The problem is that what you are trying to do is unsafe. There are two factors that, combined, work against you:
Scripts are loaded asynchronously. The only thing you control is the relative order in which your widget loads jQuery and jQueryUI. However, the page in which your widget operates also load its own version of jQuery. Your code cannot coerce the order in which scripts loaded by the partner code are going to load.
jQuery is not a well-behaved AMD module. A well-behaved AMD module calls define to gets its dependencies and it does not leak anything into the global space. Unfortunately, jQuery does leak $ and jQuery into the global space.
With these two factors combined, you are facing a race condition depending on which order the two versions of jQuery are loaded: it is generally impossible to know which version of jQuery the global symbols $ and jQuery are referring to. Consider your code:
jQuery.getScript('https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js', function () {
jQueryWidget = jQuery.noConflict(true);
setHandlers(jQueryWidget);
});
You cannot know whether jQuery refers the version you asked be loaded or to the version that the partner code wanted to load. The only thing .getScript guarantees is that the callback will be called after the script is loaded, but it does not prevent other scripts from loading between the script that .getScript loads and the time the callback is called. The browser is absolutely free to load the script you give to .getScript, load some other script that was requested through RequireJS, and then call your callback.
If you want your widget to be something that can be plopped into a page without having to change any of the existing code, then there's no simple fix. You cannot just change the logic you show in your question, nor can you just add RequireJS to your widget. RequireJS cannot by itself fix this. Contrarily to what the other answer suggest, the context configuration option for RequireJS is not a fix. It would be a fix if there were no scripts that try to access jQuery through the global $ or jQuery, but there are a dozens of plugins for jQuery that do just that. You cannot ensure that the partner code does not use them.
And beware of proposed fixes that seem to fix the problem. You can try a fix, and it seems to work, and you think the problem is solved but really the problem is not manifesting itself because, well, it is a race condition. Everything is fine, until one month later, another partner loads your widget and boom: their page creates just the right conditions to cause things to load in an order that screws up your code.
There is an additional complication which you may not have run into yet but is bound to happen from time to time. (Again, you are dealing with race conditions, so...) You code is loading jQuery and jQuery UI through script elements. However, they both check whether define is available, and if so, they will call define. This can cause a variety of problems depending on the order in which everything happens, but one possible issue is that if RequireJS is present before your widget loads, jQuery UI will call define from a script element and this will give rise to a mismatched anonymous define error. (There's a different issue with jQuery, which is more complicated, and not worth getting into.)
The only way I can see to get your widget to load without interference from the partner code, and without requiring the partner to change their code would be to use something like Webpack to build your code into a single bundle in which define should be forced to be falsy in your bundle so that any code that tests for the presence of define is not triggered. (See import-loader, which can be used for this.) If you load your code as a single bundle, then it can initialize itself in a synchronous manner, and you can be sure that $ and jQuery refer to the jQuery you've included in your bundle.
If you are going to follow my advice here is a nice example, that takes full advantage of Webpack, includes correct minification, and eliminates some artifacts from your code that are no longer needed with this approach (for instance the IIFE, and some of the functions you had). It is runnable locally by saving the files, running:
npm install webpack jquery jquery-ui imports-loader lite-server
./node_modules/.bin/webpack
./node_modules/.bin/lite-server
And there's something I did not realize when I first wrote my explanation but that I noticed now. It is not necessary to call noConflict when you wrap your code with Webpack because when it is wrapped by Webpack, jQuery detects a CommonJS environment with a DOM and turns on a noGlobal flag internally which prevents leaking into the global space.
webpack.conf.js:
const webpack = require('webpack');
module.exports = {
entry: {
main: "./widget.js",
"main.min": "./widget.js",
},
module: {
rules: [{
test: /widget\.js$/,
loader: "imports-loader?define=>false",
}],
},
// Check the options for this and use what suits you best.
devtool: "source-map",
output: {
path: __dirname + "/build",
filename: "[name].js",
sourceMapFilename: "[name].map.js",
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
include: /\.min\.js$/,
}),
],
};
Your widget as widget.js:
var $ = require("jquery");
require("jquery-ui/ui/widgets/datepicker");
var css_link = $("<link>", {
rel: "stylesheet",
type: "text/css",
href: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css"
});
css_link.appendTo("head");
$(document).ready(function() {
console.log("jQuery compare (we want this false)", $ === window.$);
console.log("jQuery version in widget", $.fn.jquery);
console.log("jQuery UI version in widget", $.ui.version);
$("#end-date").datepicker();
});
index.html:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script>
<script>
require.config({
paths: {
jquery: "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min",
"jquery-ui": "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui"
}
});
require(["jquery", "jquery-ui"], function(myJQuery) {
console.log("jQuery compare (we want this true)", myJQuery === $);
console.log("jQuery version main", $.fn.jquery);
console.log("jQuery ui version main", $.ui.version);
})
</script>
</head>
<body>
<input id="end-date">
<script src="build/main.min.js"></script>
<!-- The following also works: -->
<!--
<script>
require(["build/main.min.js"]);
</script>
-->
</body>
</html>
window.jQuery.fn.jquery !== '3.2.1'condition in your jQuery-loadingif()— and when there are more than one version of jQuery on a page,.noConflict()returns the globally available one (which might not be the one that jQuery UI extended).pathproperties there, then conditionallyrequire()them as needed (making sure to set the$within your module's scope to the RequireJS-managed version of jQuery — that has the added benefit of obviating the need for the.noConflict().