Example: Simple Tooltip

This example demonstrates a lightweight and flexible setup for display of tooltips.

This displays the tooltip adjacent to the cursor and takes advantage of the shim capabilities of Overlay, allowing the tooltip to properly display over select elements in IE.

  • Tooltips cover elements in IE.

Simple Tooltips

The Tooltip Markup

We just need a div for the tooltip, and some elements that want to have tooltips.

<div id="tooltip"></div>

<ul class="list">
    <li class="wrench"><img src="../assets/overlay/img/wrench.png"/></li>
    <li class="calipers"><img src="../assets/overlay/img/calipers.png"/></li>
    <li class="drill"><img src="../assets/overlay/img/drill.png"/></li>
    <li class="ohm"><img src="../assets/overlay/img/ohm.png"/></li>
    <li class="level"><img src="../assets/overlay/img/level.png"/></li>
</ul>

Setting Up the YUI Instance

We'll use the Overlay module to provide shimming for correctly covering <select> elements in IE. We'll use Event for its onmouseleave event, and widget-anim as a plugin to provide the fade-in for the tooltip.

YUI().use('overlay', 'event', 'widget-anim', function (Y) {
    // code will go here.
});

Declare some variables

These variables include an array of strings for various tooltips. If a DOM element has a class matching one of the name values in the tipText array, it will display the corresponding text value in its tooltip when the cursor moves over it.

var waitingToShow = false,
    
    // array for text to be displayed in tooltips
    tipText = [
        {'name': 'wrench', 'text': "Avoid dropping on toe."},
        {'name': 'calipers', 'text': 'Dial calipers: +- .001,<br>human hair .004'},
        {'name': 'drill', 'text': 'Variable-speed and cordless too.'},
        {'name': 'ohm', 'text': 'Never test microwaves with a volt-ohm meter.'},
        {'name': 'level', 'text': 'Unreliable in zero gravity conditions.'}
        // the array continues...
    ];

Instantiating The Tooltip

To create an overlay instance for the tooltip, we use the overlay constructor Y.Overlay, specifying the source node as the selector of the tooltip element.

var tooltip = new Y.Overlay({
    srcNode: "#tooltip",
    visible: false
}).plug(Y.Plugin.WidgetAnim);
tooltip.anim.get('animHide').set('duration', 0.01);
tooltip.anim.get('animShow').set('duration', 0.3);
tooltip.render();

Event Handlers

Create event handlers for the mouse events and place them above the listeners code.

// handler that positions and shows the tooltip 
var onMousemove = function (e) {
    var i;
    if (tooltip.get('visible') === false) {
        // while it's still hidden, move the tooltip adjacent to the cursor
        Y.one('#tooltip').setStyle('opacity', '0');
        tooltip.move([(e.pageX + 10), (e.pageY + 20)]);
    }
    if (waitingToShow === false) {
        // wait half a second, then show tooltip
        setTimeout(function(){
            Y.one('#tooltip').setStyle('opacity', '1');
            tooltip.show();
        }, 500);
        
        // while waiting to show tooltip, don't let other
        // mousemoves try to show tooltip too.
        waitingToShow = true;
        
        // loop through the tipText array looking for a match between
        // the class name and the array literal `name`
        for (i = 0; i < tipText.length; i += 1) {
            if (e.currentTarget.hasClass(tipText[i].name)) {

                // found a match, so set the content in the tooltip's body
                tooltip.setStdModContent('body', tipText[i].text);
                break;
            }
        }
    }
}

// handler that hides the tooltip
var onMouseleave = function (e) {

    // this check prevents hiding the tooltip 
    // when the cursor moves over the tooltip itself
    if ((e.relatedTarget) && (e.relatedTarget.hasClass('yui3-widget-bd') === false)) {
        tooltip.hide();
        waitingToShow = false;            
    }
}

Event Listeners

Add listeners for mousemove and mouseout events to whatever elements you want to have tooltips. Using mousemove instead of mouseover, and using a delay in the transition makes the tooltip fade in adjacent to the cursor. See event delegation for efficient event subscribing to multiple elements.

Y.delegate('mousemove', onMousemove, '.lists', 'li');
Y.delegate('mouseleave', onMouseleave, '.lists', 'li');
Y.one('#tooltip').on('mouseleave', onMouseleave);

This example demonstrates how you can use the existing Overlay component to create a one-off Tooltip for your page. If you plan to re-use the Tooltip on multiple pages, or in a variety of use cases, it's a good idea to encapsulate all the code which makes a tooltip work into a reusable Widget class. The example, Creating a Simple Tooltip Widget with Extensions, shows how you can create a reusable, flexible Tooltip class.

Complete Example Source

<style>
    .example .lists {
        margin: 10px auto;  /*center in viewport*/
    }

    .example .list {
        -moz-padding-start: 0;
        -webkit-padding-start: 0;
        width: 300px;
        margin: 0;
    }

    .example #tooltip .yui3-widget-bd{
        text-align: left;
        max-width: 15em;
        *width: 10em;
        background-color: #FFF6D5;
        border: solid 1px #aa8;
        padding: 0.2em 0.5em 0.3em;
        -moz-border-radius: 2px;
        -webkit-border-radius: 2px;
        -moz-box-shadow: 3px 3px 5px rgba(0,0,0,0.2);
        -webkit-box-shadow: 3px 3px 5px rgba(0,0,0,0.2);
    }

    .example .list li{
        list-style: none;
        margin: 0 0 20px;
        border: 1px solid #C9C9C9;
        width: 300px;
    }

    .example .list img{
        vertical-align: bottom;
    }

    .example .list .select{
        padding: 1.5em 0 0.5em;
        text-indent: 0.5em;
        background-color: #F7DBB2;
    }

</style>

<body class="yui3-skin-sam">
    <div id="tooltip"></div>
    
    <div class="yui3-g lists">
        <div class="yui3-u-1-2"> <!-- see CSS Grids -->
            <ul class="list">
                <li class="wrench"><img src="../assets/overlay/img/wrench.png"></li>
                <li class="calipers"><img src="../assets/overlay/img/calipers.png"/></li>
                <li class="drill"><img src="../assets/overlay/img/drill.png"/></li>
                <li class="ohm"><img src="../assets/overlay/img/ohm.png"/></li>
                <li class="level"><img src="../assets/overlay/img/level.png"/></li>
            </ul>
        </div>
        <div class="yui3-u">
            <ul class="list">
                <li class="endwrench"><img src="../assets/overlay/img/endwrench.png"/></li>
                <li class="knife"><img src="../assets/overlay/img/knife.png"/></li>
                <li class="scissors"><img src="../assets/overlay/img/scissors.png"/></li>
                <li class="screwdriver"><img src="../assets/overlay/img/screwdriver.png"/></li>
                <li class="tape"><img src="../assets/overlay/img/tape.png"/></li>
                <li class="select">Tooltips cover <select><option>select</option></select> elements in IE.</li>
            </ul>
        </div>
    </div>

<script>
YUI().use('overlay', 'event', 'widget-anim', function (Y) {

    var waitingToShow = false,
        
        // array for text to be displayed in tooltips
        tipText = [
            {'name': 'wrench', 'text': "Avoid dropping on toe."},
            {'name': 'calipers', 'text': 'Dial calipers: +- .001,<br>human hair .004'},
            {'name': 'drill', 'text': 'Variable-speed and cordless too.'},
            {'name': 'ohm', 'text': 'Never test microwaves with a volt-ohm meter.'},
            {'name': 'level', 'text': 'Unreliable in zero gravity conditions.'},
            {'name': 'endwrench', 'text': '11/16 box-end wrench A.K.A spanner - (British)'},
            {'name': 'knife', 'text': 'Wants to roll off table and stick in foot.'},
            {'name': 'scissors', 'text': "Don't run with these."},
            {'name': 'screwdriver', 'text': 'Not intended for garden weeding.'},
            {'name': 'tape', 'text': 'Remove before cutting.'},
            {'name': 'select', 'text': 'Covering select'}
        ];

    var tooltip = new Y.Overlay({
        srcNode: "#tooltip",
        visible: false
    }).plug(Y.Plugin.WidgetAnim);
    tooltip.anim.get('animHide').set('duration', 0.01);
    tooltip.anim.get('animShow').set('duration', 0.3);
    tooltip.render();

    // handler that positions and shows the tooltip 
    var onMousemove = function (e) {
        var i;
        if (tooltip.get('visible') === false) {
            // while it's still hidden, move the tooltip adjacent to the cursor
            Y.one('#tooltip').setStyle('opacity', '0');
            tooltip.move([(e.pageX + 10), (e.pageY + 20)]);
        }
        if (waitingToShow === false) {
            // wait half a second, then show tooltip
            setTimeout(function(){
                Y.one('#tooltip').setStyle('opacity', '1');
                tooltip.show();
            }, 500);
            
            // while waiting to show tooltip, don't let other
            // mousemoves try to show tooltip too.
            waitingToShow = true;
            
            // loop through the tipText array looking for a match between
            // the class name and the array literal `name`
            for (i = 0; i < tipText.length; i += 1) {
                if (e.currentTarget.hasClass(tipText[i].name)) {

                    // found a match, so set the content in the tooltip's body
                    tooltip.setStdModContent('body', tipText[i].text);
                    break;
                }
            }
        }
    }

    // handler that hides the tooltip
    var onMouseleave = function (e) {

        // this check prevents hiding the tooltip 
        // when the cursor moves over the tooltip itself
        if ((e.relatedTarget) && (e.relatedTarget.hasClass('yui3-widget-bd') === false)) {
            tooltip.hide();
            waitingToShow = false;            
        }
    }

    Y.delegate('mousemove', onMousemove, '.lists', 'li');
    Y.delegate('mouseleave', onMouseleave, '.lists', 'li');
    Y.one('#tooltip').on('mouseleave', onMouseleave);

});
</script>
</body>