Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MouseOver/MouseOut EventListener inheriting to child nodes?

Edit: Solution

Thanks to Gaby for the help in finding a solution! Didn't quite work exactly how I wanted it to, found a better solution modified from the answers. What I do is only execute the mouseover/mouseout functions when the two elements (target and related target) do not share a parent.

Just modified Gaby's example a bit and have everything working great. As long as your popup is within the same div element as whatever spawns it (even if it's outside the main contents you can append it with overflow visible) and you don't go between non-shared elements on the way over to it, it'll stay alive.

divContents.addEventListener('mouseover', mouseEnter(showPopup, divContents));
divContents.addEventListener('mouseout', mouseEnter(hidePopup, divContents));

Now, the modified mouseEnter...

function mouseEnter(_fn, _parent) {
    return function(_evt) {
        if(!shareParent(_evt.target, _evt.relatedTarget, _parent)) {
            _fn.call(this, _evt);  
        }
    }
};

function shareParent(_object1, _object2, _parent) {
    if (_object1 === _object2) { 
        return true;
    }
    while (_object1 && _object1 !== _parent) {
        _object1 = _object1.parentNode;
    }
    while (_object2 && _object2 !== _parent) {
        _object2 = _object2.parentNode;
    }

    return _object1 === _object2;
}

Solved my problem A-OK, because what I want to fire for mouseover and mouseout events will only happen when the elements don't share a parent - exactly how I intended on displaying the popups anyhow.

Thanks again for the code example from Gaby, though!

NOTE: No error checking for parent validity in shareParent function, haven't checked but it should always return true when it gets to the top of the tree (assuming the _parent isn't actually a parent of either _object1 or _object2). So make sure the parent object you pass to it is valid.

Original Question:

I'm having a problem in JavaScript right now.

I'm trying to create a div Element that pops up dynamically when something has a mouseover. The div is created directly adjacent to the object that spawns it.

divCreator.addEventListener('mouseover', createPopup, true);
divCreator.addEventListener('mouseout', hidePopup, true);

That creates the popup. Now, in the popup, when I create it, I run this before I append it to the document:

divPopup.addEventListener('mouseover', createPopup, true);
divPopup.addEventListener('mouseout', hidePopup, true);

This ensures that if I mouseover the popup, it keeps it alive (because the divCreator mouseout will fire) and when I mouseout of the popup it disappears.

However, with this method, whenever I mouseover a child element it detects a mouseout event from the parent element (divPopup) and closes the div.

Can I make the children 'transparent' to Events, so-to-speak?

like image 724
kwh Avatar asked Dec 06 '11 11:12

kwh


1 Answers

There are two events that handle this case.

The mouseenter W3 DOM3 docs and mouseleave W3 DOM3 docs but they are currently in the DOM3 Events working draft.

They were introduced by Microsoft IE5.5, and Firefox has added support in v10.


A workaround is to manually check and cancel the execution of your handler if the newly moused-over element is a child of your top level one..

code adapted from http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/

divCreator.addEventListener('mouseover', mouseEnter(createPopup), true);
divCreator.addEventListener('mouseout', mouseEnter(hidePopup), true);


function mouseEnter(_fn)
{
   return function(_evt)
   {
      var relTarget = _evt.relatedTarget;
      if (this === relTarget || isAChildOf(this, relTarget))
         { return; }

      _fn.call(this, _evt);
   }
};

function isAChildOf(_parent, _child)
{
   if (_parent === _child) { return false; }
      while (_child && _child !== _parent)
   { _child = _child.parentNode; }

   return _child === _parent;
}

Demo at http://jsfiddle.net/gaby/jMvHv/ (open your console for log messages)

like image 143
Gabriele Petrioli Avatar answered Sep 28 '22 15:09

Gabriele Petrioli