If you have been working on developing ASP.NET AJAX web controls, there is a good chance that you may have encountered some issues with the onMouseOut event. The issue comes about when you are developing a container-type control (e.g. a Panel or DIV) and you wish to capture the mouse out event on the parent container. A container control contains children, and what ends up happening is when you move your mouse around the control, you’ll find that onMouseOut fires for the parent even though the mouse hasn’t left the container. The problem is due to event bubbling. This probably isn’t anything too new for you if you have done a lot of JavaScript and there are solutions on the Internet that discuss this issue. However, in an ASP.NET AJAX Control, the process is similar to the straight JavaScript equivalent, but there are some changes to be aware of.

The solution involves performing a test on the element that the mouse is now on. The element can be retrieved using the relatedTarget in FireFox or toElement in Internet Explorer. Once we have the element, we will make sure that it isn’t the element itself or a child of the element. Remember, we only want to fire the onMouseOut event when the mouse has fully left the parent. The setup here is that you have an ASP.NET AJAX Control that has a method named _onMouseOut where you have already created the handler and such for it. If you aren’t familiar with this, I would recommend checking out the ASP.NET AJAX Documentation under the ASP.NET AJAX Extensibility section, specifically, a good example is this one, which illustrates using the IScriptControl.

The Solution

_onMouseOut : function(e) {
    /// <summary>
    /// Handler for the Control's mouseout event
    /// </summary>
    /// <param name="e" type="Sys.UI.DomEvent">
    /// Event info
    /// </param>

    // Access the raw event since we need the relatedTarget/toElement
    var ev = e.rawEvent;

    // Access the DomElement; this is the parent we will be testing
    var parent = this.get_element();

    // Access the element the mouse is now over
    // relatedTarget = FireFox; toElement = IE
    var rel = (ev.relatedTarget) ? ev.relatedTarget : ev.toElement;

    // Ensure the mouse isn't still over the control
    // And make sure the mouse isn't over a child of the control
    if (parent != rel && !this._isChild(parent, rel)) {
        // TODO: Handle OnMouseOut
    }
},
// Helper Methods
_isChild : function(parent, child) {
    /// <summary>
    /// Helper method to determine if an element is a child of a parent DomElement
    /// </summary>
    /// <param name="parent" type="Sys.UI.DomElement">
    /// The parent element
    /// </param>
    /// <param name="child" type="Sys.UI.DomElement">
    /// The element to check if it is a child of the parent
    /// </param>

    // Make sure that the child node isn't null
    if (child != null) {

        // While the child still has a parent node
        while(child.parentNode) {
            // Move up the chain of parents
            child = child.parentNode;

            // Test if the test parent element is in the chain
            if(child == parent)
                return true;
        }
    }
    // Element isn't a child of the parent
    return false;
}

I have added inline comments to make the code self-explanatory, I feel it is pretty straightforward to understand. In addition to the _onMouseOut method, you can see I also added a helper method to test if an element is a child of a parent element. You can trim these down into a single function if you’d like. The one thing that you may find confusing is accessing the rawEvent. Note that the event argument passed in an ASP.NET AJAX Control is of the type Sys.UI.DomEvent, not the native DOM event, so hence the rawEvent property, which exposes this. The DomEvent contains this property even though it isn’t listed in the ASP.NET AJAX Documentation as of this writing.

I came up with this technique after doing some research on the Internet and came across this post on the CodeHead forums as well as this one on QuirksMode.org. Using these ideas, I tweaked them to get them to work within the context of an ASP.NET AJAX Control.