jQuery Hover Swap Text

Things have been busy! I’m working on a new book and site and having a blast. I’ll share more on that later, but for now I just want to get back into posting at Perishable Press. To kick it into gear, here is one of the jQuery snippets I’m using at the new book site.

jQuery Hover Swap Text

There is probably a better way to do this, but I needed a way to swap link text with the title attribute on hover. Nothing fancy, and I thought for sure there would be an easier/existing way of doing this with jQuery, but didn’t see anything so came up with this lil’ snippet:

Note: see the updates appended to this article for better ways of doing this :)

// jQuery hover swap text @ http://perishablepress.com/jquery-hover-swap-text/
function xycss_swap_text(){
	$('a').hover(function(){
		var title = $(this).attr('title');
		var text  = $(this).text();
		$(this).text(title).attr('rel', text).removeAttr('title').wrapInner('<span />');

	},function(){
		var title = $(this).text();
		var text  = $(this).attr('rel');
		$(this).text(text).attr('title', title);
	});
}
$(document).ready(function(){
	xycss_swap_text();
});

Just drop into your JavaScript file and edit the $('a') selector with your choice. Going thru, this is just a jQuery hover function that swaps the contents of the title attribute with the anchor text. I also wrap the hover text with a <span> (for styling purposes), but you can yank that out of there if it’s not needed.

Update:

Bryan Watson extends this snippet into more of a plugin format:

(function($){

    // Swap text with title attribute
    $.fn.swapTitleAttr = function() {

        var title = this.attr('title');
        var text  = this.text();

        $(this).wrapInner('<span />');

        $(this).hover(function(){
            $(this).text(title).attr('rel', text).removeAttr('title');
        },function(){
            $(this).text(text).attr('title', title).removeAttr('rel');
        });
    };

})(jQuery);

$(document).ready(function(){
    $('a').swapTitleAttr();
});

As Bryan explains, with this version “the vars are outside of the hover function (and don’t need to be redefined). Plus, it makes the function reusable for any selector with a title attribute.”

Update 2:

Jack Rugile refines the previous method even further “with a temporary switch to inline-block with a specified width:

(function($){

    $.fn.txtSwap = function() {
        var $this = $(this);
        var title = $this.attr('title');
        var text  = $this.text();
        var origWidth = $this.width();

        $this.hover(function(){
            $this.text(title).attr('rel', text).removeAttr('title');
            if($this.width() < origWidth){
                $this.css({'display' : 'inline-block', 'width' : origWidth+'px'});
            }
        },function(){
            $this.text(text).attr('title', title).removeAttr('rel');
            $this.css({'display' : 'inline', 'width' : 'auto'});
        });
    };

})(jQuery);
$(document).ready(function(){
    $('a').txtSwap();
});

Check out Jack’s comment for further info.

Update 3:

Bryan Watson improves the previous version by making it work with any element and any attribute:

// jQuery Plugin
(function($){

    // Swap text with any attribute
    $.fn.swapAttr = function(attribute) {

        if (!attribute) { var attribute = 'title'; }

        $(this).hover(function(){

            var $this           = $(this);
            var text            = $this.text();
            var attributeValue  = $this.attr(attribute);
            var defaultWidth    = $this.width();

            if (attributeValue) {
                $this.text(attributeValue).attr('data-defaultText', text);

                if(defaultWidth > $this.width()) {
                    $this.width(defaultWidth);
                }
            }

        },function(){

            var $this           = $(this);
            var defaultText     = $this.attr('data-defaultText');

            $this.text(defaultText).removeAttr('data-defaultText').width('auto');
        });
    };

})(jQuery);

$(document).ready(function(){
    $('a').swapAttr();
    $('#link2').swapAttr('rel');
});

Check out Bryan’s follow-up comment for the scoop :)

Update 4:

Nukleo further improves the plugin with better chainability:

// jQuery Plugin
(function($){

    // Swap text with any attribute
    $.fn.swapAttr = function(attribute) {

        if (!attribute) { var attribute = 'title'; }

        return this.each(function(){

            $(this).hover(function(){

                var $this           = $(this);
                var text            = $this.text();
                var attributeValue  = $this.attr(attribute);
                var defaultWidth    = $this.width();

                if (attributeValue) {
                    $this.text(attributeValue).attr('data-defaultText', text);

                    if(defaultWidth > $this.width()) {
                        $this.width(defaultWidth);
                    }
                }

            },function(){

                var $this           = $(this);
                var defaultText     = $this.attr('data-defaultText');

                $this.text(defaultText).removeAttr('data-defaultText').width('auto');
            });

        });
    };

})(jQuery);

$(document).ready(function(){
    $('a').swapAttr().css({'color':'#ccc'});
    $('#link2').swapAttr('rel');
});

Check out Nukleo’s comment for more info.

Update 5:

(martin) tweeted this alternate way of swapping text using only CSS:

“@perishable I once did it by using the CSS display property (inline & none). An example is on my website’s sidebar: (404 link removed 2014/04/18).”

Update 6:

No idea why I didn’t think of this before.. but after reading martin’s tweet about using CSS, I remembered that swapping anchor text with title text is easily accomplished with CSS by combining the content property and attr() selector:

a:hover { content: attr(title); }

The jQuery method are far more flexible, but for simple cases, this should do the trick just fine.

Better way?

Drop some hints if you know an easier way of doing this — I’m sure there’s a better way :)