I have a userscript containing this:
// userscript header
(function() {
// here is jquery source code
var $ = $.noConflict();
})();
The site I'm using it on, is using mootools, so the site's code depends on $
. The noConflict
doesn't help in Firefox (23.0.1) for some weird reason. The site still gets jQuery in $, which breaks site's original functionality.
However, when I change it to: var $ = jQuery.noConflict();
It works. Why?
I can't put userscript on jsfiddle, so here is a gif with all the code (HTML on left, userscript on right), showing the problem:
Versions: everything is latest, Firefox 23.0.1, Greasemonkey 1.11, jQuery v1.10.2, Mootools 1.4.5-nc
"Bug" does not happen in: Chrome 29.0.1547.66m, Opera 12.16
Let's see how noConflict()
is actually implemented...
if ( window.$ === jQuery ) {
window.$ = _$; // where $_ is window.$ before jquery reassigns it.
}
Now we need to remember that the window
the user script operates on is not actually the same thing as the window
the site sees. It is a sandboxed wrapper instead (at least in Greasemonkey and Scriptish). That wrapper actually hides all "expandos", i.e. added or overwritten properties on the original object.
Hence in your user script window.$ === undefined
while in the actual page it is defined as that mootools helper. unsafewindow.$
is also the mootools helper, as unsafeWindow
is the unwrapped page window
.
Now, when your userscript includes jQuery, $
will on be set on the wrapped window
. The original page window.$
is still mootools from the perspective of the website.
Next, the call to .noConflict()
, as implemented above, will revert window.$
back, but on the sandbox wrapper. Hence window.$
in the user-script sandbox becomes undefined
again, while the page window.$
(aka. unsafeWindow.$
in the user script sandbox) still is the moo helper (and was actually never changed).
Update: Greasemonkey actively disables these wrappers explicitly in their "no-grants" branch of createSandox()
by setting wantXRays = false
. I'd consider this to be a bug.
Now, that's the reason why you need .noConflict()
in the first place in GM
.
var $ = $.noConflict()
cannot work because it is an error. The var $
will be hoisted and hence it is immediately undefined. jQuery will not actually set it (it just sets window.$
, not local-scope $
), and hence the $.noConflict()
call becomes undefined.noConflict()
.