Loading blog entries.. loading

Resolving mouseover/mouseout multi-trigger problem

Friday, December 23, 2011 1:52 AM
Written by Wayne Ye
Font Size: S  M  L 

Background

While I was developing my blog's comments function, I hope reader can write down their Gravatar email address, and my preferable UI was an "hint icon" dispolayed desides the Email textbox, so that while reader mouse overs it an overlay box will show the reason why Email is required, in additional, I want to add a little bit fancier animation:

  • When user's mouse hovers on the icon, show the div box from small to large, once reaches predefined size, fades in the hint sentente.
  • When mouse out the icon, fades out the hint sentence and then hides the div from large to small.

My requirement seems easy, however, I did spend two whole days in making it perfect, so I write down my investigation path here. 

The problems I faced

My first thoughts was definitely simple, subscribe the onmouseover/onmouseout event for the hint icon, and setinterval the size animation, once finished, fades in the texts.

That's fine! But I soon encountered one problem which is easy to trigger: mouse keeps on overing/outing the icon quickly (as long as the interval is shorter than the sizing animation), it will cause the two event handlers (mouseover/mouseout) multi-triggered so that a infinite loop could be easily caused.

The multi-trigger problem I faced is not the one related with DOM event bubbling, which is well described in jQuery mouseover API.

Majority functions on my simple blog didn't use any third party JS library including jQuery, instead, I was trying to be a "rambo", just for improving my coding skills - by implementing various kinds of features and resolving various kinds of problems.

Overcoming this issue cost me one whole day:) I tried strategies listed below:

  1. Put global variables indicating the showing/hiding process - isShowing & isHiding.
  2. Clearing oppositeinterval handle as soon as over/out event was triggered.
  3. Inspired by #2, unsubscribing oppotite event handler as soon as mouseover/mouseout was captured.

#1 does not solve the problem, I tried while multi-triggered, the interval cannot be cleared (not very sure the reason), #2 absolutely not work because it will stop unfinished animation. Finally I resolved the problem using #3, which I think it is the best one. 

Implementation

The HTML

 <div id="hintContainer">
    <div id="hintIcon">
        <img src="../Images/Hint.gif" alt="Email Hint">
    </div>
    <div id="CommentHint">
        <div id="hintText">
            Required (not shown), used only for displaying <strong>Gravatar</strong> and receiving
            future notification when new comment(s) posted on this blog.
        </div>
    </div>
</div>

The CSS

 <style type="text/css">
    #hintContainer
    {
        width: 184px;
        height: 80px;
    }
    #hintIcon
    {
        width: 16px;
        height: 16px;
        float: left;
        cursor: pointer;
    }
    #CommentHint
    {
        position:absolute;
        color: #fff;
        width: 0;
        height: 0;
        background-color: black; /*#adc7ed*/
        border: 1px dotted #666;
        padding: 2px;
        display: none;
    }
    #hintText
    {
        opacity: 0;
        filter: Alpha(opacity=0);
    }
</style>

The JavaScript

Foundation methods in my "rambo" library:

 var $id = function (id) { return document.getElementById(id); }
function bindEvent(domObj, eventName, evtHandler, useCapture) {
    if (domObj.addEventListener) {
        domObj.addEventListener(eventName, evtHandler, useCapture ? useCapture : false);
    }
    else if (domObj.attachEvent) { // IE 8 and below
        evtHandler.wrapper = function () {
            evtHandler.call(domObj)
        };

        domObj.attachEvent("on" + eventName, evtHandler.wrapper);
    }
}
function setSize(ele, width, height) {
    ele.style.width = width + 'px';
    ele.style.height = height + 'px';
}
function setPosition(ele, left, top) {
    ele.style.left = left + 'px';
    ele.style.top = top + 'px';
}

Animation methods:

 var hintIcon = $id('hintIcon'), commentHint = $id('CommentHint'), hintText = $id('hintText');
bindEvent(hintIcon, "mouseover", showHint);

var totalFrames = 20, curFrame = 0;
var sliceWidth = 180 / totalFrames, sliceHeight = 80 / totalFrames;
var showHinthandler = hideHinthandler = undefined;
var offset = 10;

function showHint(evt) {
    if (!evt)
        evt = window.event;

    // Unbind mouseover immediately to prevent multi-trigger
    unbindEvent(this, "mouseover", showHint);

    setPosition(commentHint, evt.clientX + offset, evt.clientY);
           
    showElement(commentHint);
    showElement(hintText);

    showHinthandler = setInterval(function () {
        //console.log("show: " + curFrame);

        if (++curFrame <= totalFrames)
            setSize(commentHint, sliceWidth * curFrame, sliceHeight * curFrame);
        else {
            clearInterval(showHinthandler);
            curFrame = totalFrames;

            // Fade in hint text
            divFadeIn(hintText, 2, 10);

            // Bind mouseout event back to the icon UNTIL finished showing
            bindEvent(hintIcon, "mouseout", hideHint);
        }
    }, 10);
}
function hideHint(evt) {
    if (!evt)
        evt = window.event;

    // Unbind mouseover immediately to prevent multi-trigger
    unbindEvent(this, "mouseout", hideHint);

    divFadeOut(hintText, 2, 10);

    setTimeout(function () {
        hideHinthandler = setInterval(function () {

            hideElement(hintText);
            console.log("hide: " + curFrame);


            if (--curFrame >= 0) {
                setSize(commentHint, sliceWidth * curFrame, sliceHeight * curFrame);
            }
            else {
                clearInterval(hideHinthandler);
                curFrame = 0;

                hideElement(commentHint);

                // Bind mouseover event back to the icon UNTIL finished hiding
                bindEvent(hintIcon, "mouseover", showHint);
            }
        }, 10);
    }, 800);
}

Conclusion

I guess this is a pretty good problem for interview, I am fool so I cannot resolve it in short period, however, I think good fronteers should solve it greatly:)

 

Permalink: http://wayneye.com/Blog/Resolving-mouseover-mouseout-Multi-Trigger-Problem 2998 Views  0 Comments
Tag: Category:Programming»Web Development»JavaScript

 

 


↓Comments↓

No comments so far, be the first one!

Your view point or opinion?
Nickname *
 
Gravatar *
Required (not shown), used only for displaying Gravatar and receiving future notification when new comment(s) posted on this blog.
 
Website/Blog
 
Content *
Current length:     Maximum allowed: charactors