jQuery.strict = true

(originaly published 2009 Mar 20)
“strict” mode? Why? To make jQuery enabled web applications, faster and behave better, I suggest introduction of jQuery.strict. The purpose is to be able to impose strict logic of jQuery usage. Exactly the same intent as ECMA5 “use strict” directive

For example on jQuery forum, someone has asked How best to compare two jQuery objects?. A question which reveals a deep confusion. Mainly because fundamental OO concepts and thinking have NOT been applied.

In oo patterns parlance, jQuery is an implementation of the visitor pattern. And as its state it contains pointers to the elements of the hierarchical structure it operates on. References to dom elements. Why comparing two visitors? When are they equal? Why do we have two (or more) visitors at all? Even if one has two jQuery instances operating on the same set of elements , or even on different sets which are overlapping, then what happens if one jQuery instance removes the element from the dom tree? What happens is that tThe other could happily proceed to work on the removed element. Not good.

Ok, back to jQuery.strict. If I (we?) introduce this concept and variable we could impose some logic and develop some solutions to actually speed up sites based on jQuery.

For example we can imagine this already discussed, equality extension, being developed. To make this whole implementation less problematic, we can have embedded a less painful (read: faster) solution, with the proper logic imposed. If jQuery.strict == true. And vice versa, if one wants to have a slower code. Consider this:

// test the equality of two jQ instances.
// strict logic is that comparing references they hold is NOT logical, and it is slow.
// better approach is to compare just the initial selections
// BTW: the strictest would be to put a ban on this kind of extensions.
//
$.fn.equals = function(compareTo){      
// strict logic == fast code
     if ( jQuery.strict === true )              
     return  ((this.selector == compareTo.selector) &&
               (this.context == compareTo.context))  ;
// or proceed with no logic == slow code
if (!compareTo || !compareTo.length || this.length!=compareTo.length)
     return false;
   for (var i=0; i < this.length; i++) { 
    if (this[i]!==compareTo[i]) 
     return false;
   }
   return true;
}

Another usage of jQuery.strict , which immediately springs to mind, is with the context enforcer plugin, which if used can dramatically speed up pages using jQuery.

UPDATE 2009-03-26: after comment 1, my own re-think and discussion on JQ dev forum, I
have decide to introduce $scope() intead of replacing $(). This is much less intrusive but still
can be enforced and has a name which describes the intent 100%

if ( jQuery.strict === true ){    
// (c) 2009 by Dusan Jovanovic,
// implementation inspired by Balazs Endresz
// 2009-03-26: changed from $() to $scope 
function $scope (selector, scope) {

 function from(x) { try {var who = $.caller.toString().match(/\w+/g);
                    return who[0] + " " + who[1];
                  } catch (x) {return " GLOBAL namespace "; }
 }

var isHTML = /^[^<]*(<(.|\s)+>)[^>]*$/, isID = /^#([\w-]+)$/;        
// interested only is string selectors
if ("string" === typeof selector) {
// selector given is string and does NOT represent HTML            
if (null == isHTML.exec(selector)) {
// at this point there must be context given 
if (! scope )
throw new Error(0xFF, from() + ':STRICT ERROR: Selection Scope is missing. Provide an element or "#id" ');                
// and it can't be document or document.body
if (scope == document || scope == document.body)
throw new Error(0xFF, from() + ':STRICT ERROR: Selection Scope can not be an document or document.body. Provide an element or "#id"');                
// also jQ instance can not be passed as a context
if (scope.jquery)
throw new Error(0xFF, from() + ':STRICT ERROR: Slection scope can not be a jQuery instance.');                
// if context is ID string use it as argument to getElementByID                
// as a context for a (strictly ok) next call
// otherwise jQ 1.3.2 will just slap it to the front of the selector                
// and execute the lot in the document context. Which is slow.                
   if ("string" == typeof scope ) {                    
       if (null != (isID = isID.exec(scope)))
            // 2009-APR-08 DBJ removed this:
            // return $(selector, document.getElementById(isID[1]));
            // this is better, this is what jQuery does internally
            // anyway when second arg to $() is dom element
            // this also calls Sizzle direct
            return jQuery(document.getElementById(isID[1])).find(selector) ;
      else
           throw new Error(0xFF, from() + ':STRICT ERROR: If selection scope is a string it must be an "#id"');
  }
}
}            
return jQuery(selector, scope );
}

For the purpose of this example, we might Imagine that IE has no issues with getElementById, so we do not instantiate the whole jQuery just to select element by its ID. In reality we would need to use jQuery(“#id”)[0], which will make things slower.

In any case, I know companies and team managers, which would very gladly have jQuery.strict = true, always. And. Enforce ubiquotus  $scope() usage.

Update: 2009 Aug
Brandon ( jQuery core team) has posted this article :
http://brandonaaron.net/blog/2009/06/24/understanding-the-context-in-jquery
After which in essence I feel (sort-of-a) vindicated. Brandon in this article prescribes the “correct” usage of the context.
And it is very simple: just use the DOM node as the context. Not selctor string, not jQuery instance, just use the plain old DOM node !
My comment: They should have told us earlier. But at last we are out of the woods on this one.
Thus, my jQuery “context enforcer” now becomes much simpler, and more effective of course.

    jQuery.extend({ scope: function(selector, context) {
        ///$.scope( selector, dom_element_node )   // OK
        ///Every other $.scope() call format throws Error(0xFF).
        ///NOTE: http: //brandonaaron.net/blog/2009/06/24/understanding-the-context-in-jquery
        ///argument name="selector" type="string"
        ///Standard jQuery selector
        ///argument name="context" type="object"
        ///Must be given, and must be a legal dom node.
        if (!context)
            throw new Error(0xFF, ':STRICT ERROR: Scope Context is missing. Provide an element or element id.');
        if (context == document || context == document.body)
            throw new Error(0xFF, ':STRICT ERROR: Scope Context can not be an document or document.body.');
        if (context.jquery)
            throw new Error(0xFF, ':STRICT ERROR: Scope Context can not be a jQuery instance.');
        if (context.nodeType === undefined)
            throw new Error(0xFF, ':STRICT ERROR: Scope Context must be a dom node.');
        if (context.nodeType !== 1 && context.nodeType !== 9)
        // 1 == element
        // 9 == xml doc object
            throw new Error(0xFF, ':STRICT ERROR: Scope Context passed is nbeither an element(type:1) or xml doc object(type:9).');

        return jQuery(context).find(selector);
    }
    });

Voila!

Tagged on: ,

4 thoughts on “jQuery.strict = true

  1. Balazs Endresz

    I think generally this is a good idea but you may want to use some class selectors too, for which the context can be irrelevant. For example making rounded corners: $(“.cornerz”).rcorners() or something like that.

    An other technical issue is using the DOMready callback: $(function($){ $ != window.$ }) and as you noted $ != jQuery. To get around that it’d be better to replace the jQuery.fn.init function then restore the jQuery.prototype reference: jQuery.fn.init.prototype = jQuery.fn;

    Overall, I think this strictness can certainly make people use more reasonable selectors, and it’s enough to use this only in the development version, the public build will work without it as well — it will be just faster!

  2. Dusan

    Thanks Balazs,
    please see the amendments above. Now one just has to write:

    $scope( "[~top!=auto]","#container").hide().show();

    To have fast web app. Definitely faster than:

    // illegal but allowed
    $( "[~top!=auto]","#container").hide().show();

    And faster than :

    // legal but slower
    $( "[~top!=auto]",$("#container")).hide().show();

    Especially if there is a lot of container’s.

  3. Dusan

    After Aug 2009, and Brandon Aaron’s article, http://brandonaaron.net/blog/2009/06/24/understanding-the-context-in-jquery
    we now know it is only meaningful to pass a dom node as a context argument, to $(selector,context).
    So instead of :

    
    $scope( "[~top!=auto]","#container").hide().show();
    

    One would write:

    
    $scope( "[~top!=auto]", $("#container")[0]).hide().show();
    

    Let me repeat: By jQuery team members “admition”, this is the only meaningful usage of the context argument.

Leave a Reply