Allowing developers to quickly create image hover effects I've created a simple jQuery plugin to allow developers to quickly create image hover effects with very simple markup. I also created an option to automatically create the mouseover or mouseout versions of the image via TimThumb.

Here is a link. for more information and a demo.

(function($){
$.fn.hovr = function(options) {

    // Default Options
    var defaults = {
        speed:           'normal',
        animateOver:     {'opacity': '0'},
        animateOut:      {'opacity': '1'},
        timThumb:        false,
        timThumbInverse: false,
        timThumbPath:    'images/img.php',
        timThumbParams:  'f=2'
        //http://www.binarymoon.co.uk/2012/02/complete-timthumb-parameters-guide/
    };
    var options = $.extend({}, defaults, options);

    // Create Images
    if (options.timThumb) {
        this.each(function() {
            var strOrig = $(this).prop('src');
            var strNew  = options.timThumbPath + 
                '?src=' + strOrig + 
                '&amp;w=' + $(this).prop('width') + 
                '&amp;h=' + $(this).prop('height') + 
                '&amp;' + options.timThumbParams;

            if (options.timThumbInverse) {
                $(this)
                    .prop('src', strOrig)
                    .attr('data-hovr', strNew);
            }
            else {
                $(this)
                    .prop('src', strNew)
                    .attr('data-hovr', strOrig);
            }
        });
    }

    // Create Rollovers
    return this.each(function() {
        $(this).hover(
            function() {
                $(this).stop().animate(options.animateOver, options.speed);
            },
            function() {
                $(this).stop().animate(options.animateOut, options.speed);
            }
        ).each(function() {
            var intWidth  = ($(this).attr('width'))  ? $(this).attr('width')  : $(this).prop('width');
            var intHeight = ($(this).attr('height')) ? $(this).attr('height') : $(this).prop('height');
            var strAlign  = ($(this).attr('align'))  ? $(this).attr('align')  : '';
            var strClass  = ($(this).attr('class'))  ? $(this).attr('class')  : '';
            var strStyle  = ($(this).attr('style'))  ? $(this).attr('style')  : '';

            $(this).wrap('&lt;div class="' + strClass + '" style="position:relative; display:inline-block; ' + 
                'width:' + intWidth + 'px; height:' + intHeight + 'px; ' + 
                'float:' + strAlign + '; ' + strStyle + '"&gt;&lt;/div&gt;');

            $(this).before($(this).clone(true))
                .attr('style', 'position:absolute; left:auto; top:auto;')
                .prop('src', $(this).attr('data-hovr'))
                .removeAttr('data-hovr')
                .removeAttr('class');

            $(this).prev('img')
                .attr('style', 'position:absolute; left:auto; top:auto; z-index:10;')
                .removeAttr('data-hovr')
                .removeAttr('class');
        });
    });

};
})(jQuery);
As a rule of thumb, when you use a jQuery selection more than once, you should cache its value. When you do $(".someElement"), jQuery now has to go find that element in the DOM, wrap it in the jQuery object and return it. If you save what it returns, then you only do the search once - not every time you use it.

The hard part is caching this. This is relative to where you are in the code, function, callback, etc. and changes to provide context. For example:

var Object = {
    init: function() {
        //this in here references Object
        //You could call this function elsewhere by doing: "this.init();"
    },

    bindEvents: function() {
        $(".someElement").hover(function() {
            //This is a callback function
            //In here "this" refers to the element your .hover() was called on - ".someElement"
        });

        //However, outside the callback, "this" now refers to Object again

        $(".someOtherElement").each(function(){
            //This is another callback
            //Here "this" refers to the element in question
        });
    }
}


The reason the plugin is breaking when you cache it is because you have to cache it at each point the reference changes. Watch this next example:

var Object = {
    init: function() {
        //Here "this" refers to Object
        //You don't need to cache this here because the Object is already saved in a variable
    },

    bindEvents: function() {
        $(".someElement").hover(function() {
            //In here however, the reference changed
            //In here "this" refers to the element your ".hover()" was called on. ie.: ".someElement"
            //You would cache it in here
        });

        //However, outside the callback, "this" now refers to Object again so you don't need to cache it

        $(".someOtherElement").each(function(){
            //This is another callback
            //The reference has changed again, so now we cache it again
        });
    }
}


From what I could understand from reading your plugin, I've cached your this references for you. I didn't change anything else except for .removeAttr() calls at the end. You can use a space and put in several attributes to be removed at once.

Anyways here's your code:

(function($){
$.fn.hovr = function(options) {

    // Default Options
    var defaults = {
        speed:           'normal',
        animateOver:     {'opacity': '0'},
        animateOut:      {'opacity': '1'},
        timThumb:        false,
        timThumbInverse: false,
        timThumbPath:    'images/img.php',
        timThumbParams:  'f=2'
        //http://www.binarymoon.co.uk/2012/02/complete-timthumb-parameters-guide/
    };
    var options = $.extend({}, defaults, options);

    // Create Images
    if (options.timThumb) {
        this.each(function() {
            var $this = $(this),
                strOrig = $this.prop('src'),
                strNew  = options.timThumbPath + 
                '?src=' + strOrig + 
                '&amp;w=' + $this.prop('width') + 
                '&amp;h=' + $this.prop('height') + 
                '&amp;' + options.timThumbParams;

            if (options.timThumbInverse) {
                $this
                    .prop('src', strOrig)
                    .attr('data-hovr', strNew);
            }
            else {
                $this
                    .prop('src', strNew)
                    .attr('data-hovr', strOrig);
            }
        });
    }

    // Create Rollovers
    return this.each(function() {
        $(this).hover(
            function() {
                $(this).stop().animate(options.animateOver, options.speed);
            },
            function() {
                $(this).stop().animate(options.animateOut, options.speed);
            }
        ).each(function() {
            var $this = $(this),
                intWidth  = ($this.attr('width'))  ? $this.attr('width')  : $this.prop('width'),
                intHeight = ($this.attr('height')) ? $this.attr('height') : $this.prop('height'),
                strAlign  = ($this.attr('align'))  ? $this.attr('align')  : '',
                strClass  = ($this.attr('class'))  ? $this.attr('class')  : '',
                strStyle  = ($this.attr('style'))  ? $this.attr('style')  : '';

            $this.wrap('&lt;div class="' + strClass + '" style="position:relative; display:inline-block; ' + 
                'width:' + intWidth + 'px; height:' + intHeight + 'px; ' + 
                'float:' + strAlign + '; ' + strStyle + '"&gt;&lt;/div&gt;');

            $this.before($this.clone(true))
                .attr('style', 'position:absolute; left:auto; top:auto;')
                .prop('src', $this.attr('data-hovr'))
                .removeAttr('data-hovr class');

            $this.prev('img')
                .attr('style', 'position:absolute; left:auto; top:auto; z-index:10;')
                .removeAttr('data-hovr class');
        });
    });
};
})(jQuery);