Mar 6, 2010

Easy Setter Functions in jquery

One of the new feature in jQuery 1.4 is setter function. For a while now, you’ve been able to pass a function into .attr() and the return value of that function is set into the appropriate attribute. This functionalilty has now been extended into all setter methods: .css(), .attr(), .val(), .html(), .text(), .append(), .prepend(), .before(), .after(), .replaceWith(), .wrap(), .wrapInner(), .offset(), .addClass(), .removeClass(), and .toggleClass(). Addtionally, for the following options, the current value of the item is passed into the function as the second argument: .css(), .attr(), .val(), .html(), .text(), .append(), .prepend(), .offset(), .addClass(), .removeClass(), and .toggleClass().

$('a[target]').attr("title", function(i, currentValue){
  return currentValue+ " (Opens in External Window)";
});

$("#xx").text(function (i, currentValue) { 
    return currentValue+ "modified by fred";
});

In jQuery 1.32, the attr is defined as follow

attr: function( name, value, type ) {
 var options = name;

 // Look for the case where we're accessing a style value
 if ( typeof name === "string" )
  if ( value === undefined )
   return this[0] && jQuery[ type || "attr" ]( this[0], name ); //jQuery.attr(this[0], name);

  else {
   options = {};
   options[ name ] = value;
  }

 // Check to see if we're setting style values
 return this.each(function(i){
  // Set all the styles
  for ( name in options )
   jQuery.attr(
    type ?
     this.style :
     this,
    name, jQuery.prop( this, options[ name ], type, i, name )
   );
 });
},

But in 1.4.2, it is defined as followed.

attr: function( name, value ) {
 //the access function is defined in core module
 return access( this, name, value, true, jQuery.attr );
},

We can see that a protected access function really does the job.

// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
// elems is an array
// key, value is dictionary pair
// exec is an boolean, put it true if you want to call the value as a function 
// fn is a function used to access the element
// passe is usually false, I am not sure it is intention 
function access( elems, key, value, exec, fn, pass ) {
 var length = elems.length;
 
 // Setting many attributes
 //if key is complext object like { name: "fred", age: 18 }
 if ( typeof key === "object" ) {
  for ( var k in key ) {
   access( elems, k, key[k], exec, fn, value );
  }
  return elems;
 }
 
 // Setting one attribute
 if ( value !== undefined ) {
  // Optionally, function values get executed if exec is true
  //if value is a function which dyanmically return a true value
  //and pass is null 
  //and exec is true
  //then set exec to true
  //if caller pass is undefined and caller want to evalueate the value function and value is a function
  exec = !pass && exec && jQuery.isFunction(value);
  
  //your callback function is like
  //function( elem, name, value)
  //for example
  //Query.fn.css = function( name, value ) {
  // return access( this, name, value, true, function( elem, name, value ) {
  //or function( elem, name, value, pass ) 
  //for example attr: function( elem, name, value, pass )
  for ( var i = 0; i   length; i++ ) {
   //fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
   //exec means your value to set is need to be determined by the callback functio
    var elem = elems[i];
    var valueToSet;
    if (exec)
    {
    //get the current value first
    var currentValue = fn(elem, key);
    //then call the value function to get the new value
    valueToSet = value.call(elem, i , currentValue);
    }
    else
    {
    valueToSet = value;
       }
    
    fn(elem, key, valueToSet, pass);
  }
  
  return elems;
 }
 
 // Getting an attribute
 return length ? fn( elems[0], key ) : undefined;
}

jQuery.fn.css method also used this protected method.

jQuery.fn.css = function( name, value ) {
 //rather call function(elem, name, value) call
 //access function, and let access function to call back function(elem, name, value)
 return access( this, name, value, true, function( elem, name, value ) {
  if ( value === undefined ) {
   return jQuery.curCSS( elem, name );
  }
  
  if ( typeof value === "number" && !rexclude.test(name) ) {
   value += "px";
  }

  jQuery.style( elem, name, value );
 });
};