jQuery tooltip issue

I built a tooltip, but the hover effect isn’t quite working properly.

Basically, hover on the “?” icon, and a tooltip appears.

  • If the mouse leaves the “?”, the tooltip fades out after a moment.
  • If the mouse slides over to hover on the tooltip itself, the tooltip is prevented from fading out (until the mouse leaves the tooltip, or until the tooltip is clicked).
  • Only one tooltip at a time can appear (if the mouse slides from one “?” to another, then the old tooltip fades out).

The problem is that when the mouse slides from the “?” to the tooltip, the tooltip blinks. I’ve been trying to fix it, and I suspect it has something to do with the jQuery selector I’m using for determining when to hide the tooltip. It’s not that the delay time is too short either. I can set it for 2000, and the tooltip would still blink.

Here’s the code:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>tooltip test</title>
<style type="text/css">
/* start reset */
body,div,span,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,button,p,blockquote,table,th,td {
    margin: 0px;
    padding: 0px;
}
body {
    color: black;
    font-family: Verdana,Geneva,Arial,Helvetica,sans-serif;
    font-size: 100.01%;
}
a img { border: none; }
/* end reset */

/* start form styling */
form {
    width: 600px;
    margin:0 auto;
}
form fieldset {
    position: relative;
    margin: 20px 0px 20px 0px;
    padding: 0px 12px 0px 12px;
    background-color: #ebeef3;
    border: 2px solid #4f53bc;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
}
form .container {margin: 12px 12px 18px 30px;}
form .question {padding: 14px 0px 16px 0px;}

form .question label {
    margin-right: 8px;
    margin-bottom: 6px;
    color: #303030;
    font-weight: bold;
}
/* end form styling */

/* start tooltip */
a.tooltip img {
    position: relative;
    top: .3em;
}

a.tooltip {
    cursor: help;
    margin: 0px 12px 0px 12px;
    font-size: 99.9%; /* IE fix for hover tooltip */
}

a.tooltip,a.tooltip span.tt,a.tooltip span.p {text-decoration: none;}

a.tooltip span.tt {
    display: none;
    position: absolute;
    z-index: 25;
    font-size: .7em;
    color: #666;
    border: 1px #d4d5aa solid;
    border-right-color: #b8b58f;
    border-bottom-color: #b8b58f;
    background-color: #FFFBC6;
    padding: 5px 8px 0px 8px;
    margin: -10px 0px 0px 25px;
    max-width: 225px;
    min-width: 150px;
    -moz-border-radius: 4px;
    -webkit-border-radius: 4px;
    border-radius: 4px;
    -moz-box-shadow: 3px 3px 5px #BCBCBC;
    -webkit-box-shadow: 3px 3px 5px #BCBCBC;
    box-shadow: 3px 3px 5px #BCBCBC;
}

a.tooltip span.tt span.p:first-child {font-weight: bold;}
a:hover.tooltip_hover span.tt {display: inline;}

a.tooltip span.tt span.p {
    display: block;
    margin: .2em 0 .8em 0;
}

/* end tooltip */

</style>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<script type="text/javascript">
    $().ready(function(){
    
        $('fieldset a.tooltip').removeClass('tooltip_hover'); //remove CSS hover fallback
        
        $('fieldset a.tooltip, fieldset a.tooltip span.tt').hover(
            function(){
                var tooltip = $(this).find('span.tt');
                
                hide_tooltip($('fieldset a.tooltip span.tt').filter(':visible').not(tooltip)); //hide all tooltips but the current one - PROBLEM IS HERE?

                $(tooltip).stop(true,true).animate({opacity:'show'},{queue:false, duration:100}); //show tooltip
            },
            function(){
                var tooltip = $(this).find('span.tt');
                $(tooltip).delay(600).fadeOut(300); //hide the tooltip
            }
        );

        $('fieldset a.tooltip span.tt').click(function(){
            hide_tooltip(this); //hide tooltip if it is clicked
        });
        
        function hide_tooltip(selection){
            $(selection).stop(true,true).animate({opacity:'hide'},{queue:false, duration:100});
        }

    });
</script>
</head>
<body>
    <form>
        <fieldset>
            <div class="container">
            
                <div class="question">
                    <label>Label Me This</label>
                    <a href="javascript:void(0);" class="tooltip tooltip_hover"><img src="http://img52.imageshack.us/img52/4199/icontooltip.png" class="pngfix" alt="" /><span class="tt"><span class="p">Title 1</span><span class="p">Blah blah blah yadda yadda yadda blah blah blah so on</span><span class="p">For example, blah blah blah yodel he hi ho</span></span></a>
                </div>
                
                <div class="question">
                    <label>Label Me That</label>
                    <a href="javascript:void(0);" class="tooltip tooltip_hover"><img src="http://img52.imageshack.us/img52/4199/icontooltip.png" class="pngfix" alt="" /><span class="tt"><span class="p">Title 2</span><span class="p">Blah blah blah yadda yadda yadda blah blah blah so on</span><span class="p">For example, blah blah blah yodel he hi ho</span></span></a>
                </div>
                
                <div class="question">
                    <label>Label Me Too</label>
                    <a href="javascript:void(0);" class="tooltip tooltip_hover"><img src="http://img52.imageshack.us/img52/4199/icontooltip.png" class="pngfix" alt="" /><span class="tt"><span class="p">Title Three</span><span class="p">Blah blah blah yadda yadda yadda blah blah blah so on</span><span class="p">For example, blah blah blah yodel he hi ho</span></span></a>
                </div>
                
            </div>
        </fieldset>
    </form>
</body>
</html>

Since that sounded like it was over-complicating things more than necessary, I went back a few steps and ended up removing the additional selector I had for the hover function. Since I added that in before I completely fixed a queuing buildup issue I had earlier, it seems to have done the trick.

    $().ready(function(){
    
        $('fieldset a.tooltip').removeClass('tooltip_hover'); //remove CSS hover fallback
        
        $('fieldset a.tooltip').hover(
            function(){
                var tooltip = $(this).find('span.tt');
                
                hide_tooltip($('fieldset a.tooltip span.tt').not(tooltip).filter(':visible'));//hide all tooltips but the current one

                $(tooltip).stop(true,true).animate({opacity:'show'},{queue:false, duration:100}); //show tooltip
                
            },
            function(){
                var tooltip = $(this).find('span.tt');
                $(tooltip).delay(600).fadeOut(300).css('opacity',''); //hide the tooltip
            }
        );

        $('fieldset a.tooltip span.tt').click(function(){
            hide_tooltip(this); //hide tooltip if it is clicked
        });
        
        function hide_tooltip(selection){
            $(selection).stop(true,true).animate({opacity:'hide'},{queue:false, duration:100});
        }

    });

Instead of storing the reference to the setInterval as a global variable, you can use jQuery’s data method to store the info in relation to the link or the tooltip (whichever is easier to access)

That way, you can have multiple timers set up and can deal with them individually on an as-needed basis

It works…but then another problem came up. Now more than one tooltip can show up at a time and they start overlapping (when moving the mouse from icon to icon). I even lowered the timeout from 500 down to 100, but multiple tooltips can still display at the same time.

    $().ready(function(){
    
        $('fieldset a.tooltip').removeClass('tooltip_hover'); //remove CSS hover fallback
        
        $('fieldset a.tooltip, fieldset a.tooltip span.tt').hover(
            function(){
                var tooltip = $(this).find('span.tt');
                
                var toID = setTimeout(function(){
                    hide_tooltip($('fieldset a.tooltip span.tt').filter(':visible').not(tooltip));
                },100);//hide all tooltips but the current one - PROBLEM IS HERE?
                

                $(tooltip).stop(true,true).animate({opacity:'show'},{queue:false, duration:100}); //show tooltip
                clearTimeout(toID);
                
            },
            function(){
                var tooltip = $(this).find('span.tt');
                $(tooltip).delay(600).fadeOut(300); //hide the tooltip
            }
        );

        $('fieldset a.tooltip span.tt').click(function(){
            hide_tooltip(this); //hide tooltip if it is clicked
        });
        
        function hide_tooltip(selection){
            $(selection).stop(true,true).animate({opacity:'hide'},{queue:false, duration:100});
        }

    });

[edit]: I lowered the timeout to 10, then 1, then 0, but it still operated the same way–multiple tooltips could be present at the same time.

By using clearTimeout

I’m not quite sure what you’re describing. How would I remove the setTimeout?

    $().ready(function(){
    
        $('fieldset a.tooltip').removeClass('tooltip_hover'); //remove CSS hover fallback
        
        $('fieldset a.tooltip, fieldset a.tooltip span.tt').hover(
            function(){
                var tooltip = $(this).find('span.tt');
                
                setTimeout(function(){
                    hide_tooltip($('fieldset a.tooltip span.tt').filter(':visible').not(tooltip));
                },500);//hide all tooltips but the current one - PROBLEM IS HERE?
                

                $(tooltip).stop(true,true).animate({opacity:'show'},{queue:false, duration:100}); //show tooltip
            },
            function(){
                var tooltip = $(this).find('span.tt');
                $(tooltip).delay(600).fadeOut(300); //hide the tooltip
            }
        );

        $('fieldset a.tooltip span.tt').click(function(){
            hide_tooltip(this); //hide tooltip if it is clicked
        });
        
        function hide_tooltip(selection){
            $(selection).stop(true,true).animate({opacity:'hide'},{queue:false, duration:100});
        }

    });

When removing the tooltip, do it after a delay of half a second using setTimeout

That way, when the mouse is moved to a section that results in the same tooltip being shown, you can remove the setTimeout timer that was previously there.