Skip to content Skip to sidebar Skip to footer

Is There Any Unobtrusive Way To Hook Into JQuery Methods For Triggers?

I was wondering if there was any unobtrusive way to hook into methods such as attr, data, css, etc and call custom triggers? Ideally, I could do something like this: $('.friend a')

Solution 1:

It's pretty easy to write a "hook" function:

function hook(original, wrapper) {
    return function() {
        wrapper.apply(this, arguments);
        return original.apply(this, arguments);
    }
}

The wrapper function you provide will be called with the same arguments as the original function, before the original function itself is called.

An example of use:

$.fn.attr = hook($.fn.attr, function(attribute, value) {
    alert('attribute: '+attribute+', value: '+value);
});
$('a.pie').attr('href', 'http://blueberry.pie');
// An alert says "attribute: href, value: http://blueberry.pie"

(Sidenote: It'd be an easy extension to let your wrapper cancel the call to the original function, as well, but that's getting more featureful than you wanted.)

You could either use this directly to do what you want, or you could make the wrapper function just fire custom jquery events which you listen for in the standard jquery ways.


Solution 2:

Turns out this hack will work; I use data's built-in setData triggers (and the aforementioned 'hook' to add functionality atop jQuery's attr and removeAttr) to duplicate attrs within the data object on the jQuery object. Although it's somewhat dirty, I get the functionality to hook into data-change triggers for data, attr, and, if written, any other jQuery methods that track key/value pairs.

(function($) {
  var binder = function(e, dataKey, dataValue) {
    return (function(dataKey, dataValue, self) {
      var $this = $(self),
          oldValue = $this.data(dataKey),
          newValue = dataValue,
          passed = {
            attr: dataKey,
            from: oldValue,
            to:   newValue
          };

      var isAttribute = !!($this.data(dataKey + "-attribute"));

      if(oldValue !== newValue) { 
        var attrPrefix = isAttribute ? "attr-" : "";
        $this.trigger(attrPrefix + dataKey + "-changed", passed); 
        $this.trigger(attrPrefix + "data-changed", passed); 
      }
    })(dataKey, dataValue, this);
  };

  var hook = function(original, wrapper) {
    return function() {
      wrapper.apply(this, arguments);
      return original.apply(this, arguments);
    };
  };

  $.fn.attr = hook($.fn.attr, function(attr, val) {
    if(val) {
      $(this).data(attr + "-attribute", true);
      $(this).data(attr, val);
    }
  });

  $.fn.removeAttr = hook($.fn.removeAttr, function(attr) {
    $(this).removeData(attr + "-attribute");
    $(this).removeData(attr);
  });

  $.fn.observeData = function() {
    $(this).bind("setData", binder);
  };
})(jQuery);

Solution 3:

There is no event for this currently existing within jQuery.

The livequery plugin may provide something of what you need, such as changing classes. Basically the selector is saved and then any changes in the DOM that change what the selector affects then calls the passed functions, etc.


Solution 4:

And why don't you use the simplest approach:

jQuery.fn.changeAttr = function(attrName,attrValue) {
  return this.each(function(){
    this.attr(attrName,attrValue);
    alert('The att: '+attrName+ ' is now '+attrValue);
  });
};

And then just:

$('.friends a').changeAttr('rel','friend')

Sorry if it doesn't work, I don't remember exactly the syntax for extending jQuery.


Solution 5:

Here's a modification to attr() along the lines of what you're talking about.

(function(attr) {

    jQuery.attr = function(elem, name, value) {
        var current = attr(elem, name, undefined); // read current value
        var retval = current;

        if (value !== undefined) { // writing
            retval = attr(elem, name, value); // call original
            jQuery.event.trigger('attr.changed', {
                attribute: name,
                from: current,
                to: value
            }, elem)
        }

        return retval; // return original
    }

})(jQuery.attr);

Usage should be:

$(".friend a").bind("attr.changed", function(e, changed) {
    alert("The " + changed.attribute + " attribute changed from " + changed.from + " to " + changed.to + "!");
});

$(".friends a").attr("class", "foo"); // triggers alert

I don't believe css() is possible as the .style property can't trigger events. data() I don't know.

Note I don't condone its usage, this was just an academic effort. What you want incurs a performance penalty on attr() in order to populate the from property of the event argument. We have to read the old value before we write the new one.


Post a Comment for "Is There Any Unobtrusive Way To Hook Into JQuery Methods For Triggers?"