Can I provide default “context” to jQuery?

2020-07-18 03:06发布

问题:

Background:

The second "context" argument to the jQuery selector call (e.g: jQuery(selector, context)) can be provided to give the selector engine a starting point from which to descend.

This is often useful if you need to control content in an IFRAME (in the same domain). You simply pass iframe.contentWindow.document as the "context" argument.

If any JavaScript code is loaded in the IFRAME which makes use of jQuery, and is CALLED FROM THE SCOPE OF THE OUTER WINDOW, then any reference to $ or jQuery in that code will actually be the instance of jQuery from the outer window.

The problem comes when that JavaScript code in the IFRAME (say Bootstrap.js) does something like $(document) (or does some other selector without a "context" argument). When that code (defined inside the iframe) is called from the outer window, document refers to the HTMLDocument element from the outer window - which is usually not the desired outcome.

Question:

It would be super useful to be able to create a lexically-scoped copy/wrapper of jQuery that has a default "context" argument, provided by whomever creates it.

Example:

// jQuery already exists out here
var iframe = document.createElement('IFRAME');
iframe.addEventListener('DOMContentLoaded', function(){

    // code in here can already refer to $ for 'outer' jQuery

    // code in here can refer to $local for 'inner' jQuery by virtue of...
    var $local = jQueryWithContext($, iframe.contentWindow.document);

    // code loaded with IFRAME will use $local by virtue of ...
    iframe.contentWindow.jQuery = iframe.contentWindow.$ = $local;

});
iframe.src = '/path/to/iframe/content.html';

The question is, is it possible to write something like jQueryWithContext above?

Why?

Sometimes you want to isolate 3rd party HTML components which (while you trust them from a security perspective) are misbehaved from a CSS / JavaScript pollution perspective.

Bootstrap.js is a good example. It calls $(document) a fair bit, and does other similar context-less selector calls. If jQuery could be re-scoped in the way I describe, then this 'not optimally' written libraries could be isolated quite easily.

Additionally, it can be very helpful to use the same $.data(el, ...) collection from both frames, and this is quite tricky without some context management.

回答1:

Actually, it would be rather simple:

function jQueryWithContext( selector, context ) {
  // I added the possibility to overwrite the context here, but you could delete
  return $( selector, context || iframe.contentWindow.document );
}
jQueryWithContext( '#main' ).show();

But to force it to plugins, you'd probably need to go like this:

jQuery.noConflict(); // keep the real jQuery for now
$ = function( selector, context ){
  return new jQuery.fn.init( selector, context || iframe.contentWindow.document );
};
$.fn = $.prototype = jQuery.fn;
jQuery.extend( $, jQuery ); // copy static method
// Then override default jQuery
jQuery = $;

This kinda work, but it could break some usage of $() (Maybe not now, but it's possible in future jQuery version, or anytime the presence of a context parameter break the normal behavior).