Ignoring mouseenter/mouseout events on children of same element in JS - javascript

I am using this menu (http://tympanus.net/codrops/2013/04/17/slide-and-push-menus/) in my website and I modified it slightly. Instead of using the button to show and hide the menu I want to show the menu when the mouse enters a region of the menu and disappears when the mouse leaves the menu. I managed to achieve showing the menu on mouseenter, however, whenever hovering over a child the menu just keeps showing and hiding itself. The JS code can be seen in the snippet below.
showLeft.onmouseover = function() {
classie.toggle( this, 'active' );
classie.toggle( menuLeft, 'cbp-spmenu-open' );
};
I made some research and from what I understood is that this phenomenon is referred to as 'bubbling' and I tried filtering the trigger of the event by the source of the event however I was not successful. Any help would be greatly appreciated.

Yes, this is caused by event bubbling as you identified.
The simplest way to solve your problem is to use jQuery, which specifically handles this case through its mouseenter/mouseleave events.
$(elem).on("mouseenter", function() {
// your code
});

Related

Dropdown plugin closing on scroll bar click

I'm in the process of teaching myself how to write a jQuery plugin. I am using the jquery-hover-dropdown-box as a base example. It's not just copy/paste though, I've made a number of changes trying to get a better understanding of it all. For example I'm not incorporating the hover event, I added a filter, and currently not using any defaults to name a few. Clicking on a div's scroll bar fires the blur event in I.E is the only post I've found with what looks like a good resolution to this and I tried implementing something similar but was unsuccessful.
Complete Example: jsFiddle
Issue:
I click in the input and the dropdown opens but the first time I click on the scroll bar, the dropdown closes. When I open the dropdown a second time and click on the scroll bar, it does not close (as I would expect). From what I can tell, my issue is in the blur on the input. I understand that when I click in the scroll bar, the input has lost focus. I tried to implement something similar to this post on Scrollbars not working on dropdown in IE8 but was unable to get it working.
Steps to Reproduce:
Click in the input to open the dropdown
Click anywhere in the scroll bar and the dropdown closes (should stay open and scroll)
Click in the input a second time and the dropdown opens
Click anywhere in the scroll bar and the dropdown stays open (as it should)
Question:
What am I doing wrong that is causing the dropdown to close only the first time I click on the scroll bar?
What I've Tried:
When I'm appending the ul to the div (currently commented out around line 68 in the jsFiddle), I added the code below. I figured that if I stopped the action from being triggered with a mousedown on the ul it would fix my issue. Although it did fix the issue in Chrome, it persists in IE8.
Update: I changed the code below from $list.mousedown... to $container.mousedown... since $list is the ul and $container is the div that contains it. My thought was that it extend the area. The result was the same though.
...
$container.append($list);
$list.mousedown(function(e) {
e.preventDefault();
});
...
Since this seemed to be close, I tried taking a similar approach in the blur event. The issue explained above happens when I use this code. In Chrome, clicking the scroll bar does not fire the blur event but in IE8, it does. The first time the dropdown is opened and you click in the scroll bar, it logs "hiding". Open the dropdown again and click the scroll bar and it logs "bind mousedown". Click anywhere outside the dropdown and it closes (as it should) and logs "hiding" (as it should). To me it seems backwards, but obviously I'm not understanding it correctly. (The code below is around line 134 in the jsFiddle)
Code edit: Updated with Goran.it suggestion to prevent multiple bindings from happening.
...
// where $dom is the 'div' containing the 'ul'
$dom.unbind('mousedown.auto_dropdown_box_ul')
.bind('mousedown.auto_dropdown_box_ul', function(e) {
console.log('bind mousedown');
e.preventDefault();
});
setTimeout(function() {
console.log('hiding');
$dom.addClass('auto_dropdown_hide').hide();
}, 100);
...
I've also tried removing the blur event. I know this would prevent the dropdown from closing if you tabbed out of the input but figured it was worth a try. In Chrome it works exactly how I expected, clicking outside the input closes the dropdown, clicking the scroll bar does not close it and tabbing out does not close it. In IE8, clicking outside the dropdown does not close it though, nor does it close when you tab out, but clicking in the scroll bar does work. This is the code I added after removing blur (it's not included in the jsFiddle).
// below where the 'blur' event was
$(document).click(function(e) {
if (e.target == dropdownArray[0].input[0] || e.target == dropdownArray[0].dom[0]) {
console.log('matches');
e.preventDefault();
} else {
console.log('does not match');
dropdownArray[0].dom.addClass('auto_dropdown_box_hide').hide();
}
});
Again, this is my first attempt, I'm still learning. I'm sure there are multiple things that I'm probably doing wrong, that I can improve, etc. Before I tackle those, I would just like to understand what I'm doing wrong here and what I need to do to correct it. After reading the plugin concepts, I know there is much for me to learn.
I found few issues on a first look, you should change the :
$dom.bind('mousedown.auto_dropdown_box_ul'
to:
$dom.unbind('mousedown.auto_dropdown_box_ul').bind('mousedown.auto_dropdown_box_ul'
To prevent multiple events binding to the dom node, you can also use .one event handling of jQuery.
In the same event handling you should also put:
console.log('bind mousedown');
e.preventDefault();
return false;
To be sure event is not firing.
Hope this helps (I'm not having IE8 for a long time now)
I believe I finally figured this one out. After multiple tries I thought I'd change up the format to one that seemed, at least to me, a little more straight forward.
Here is the complete jsFiddle
The underlying fix was correctly setting/adjusting which element has focus and when. Since mousedown executes before click, I stuck with that event on the dropdown. In the mousedown event, I set isVisible = true and set focus back on the input (although the latter is not completely necessary). In the blur event, I'm checking isVisible. If it's true, that means that a click happened in the scroll bar so don't close the dropdown. If it's false, close the dropdown. Throughout events, I'm keeping track of isVisible so I know it's state when blur executes. Again, I changed up the format so the two fiddles do look different. I'm sure I could go back and implement something similar to the original fiddle and get it working but I just liked this way more. Here is a snippet of the relevant changes:
{
// some code above
// where $list is the 'ul'
$list.bind('mousedown', methods.onDropdownMousedown);
// where $obj is the 'input'
$obj.bind('blur', methods.doOnBlur);
},
onDropdownMousedown: function(e) {
$input.focus(); // not really needed, just in case
isVisible = true;
},
doOnBlur: function(e) {
if (isVisible) {
$input.focus();
isVisible = false;
} else {
// where $container is the 'div' containing the list
$container.addClass('auto_dropdown_box_hide').hide();
isVisible = false;
}
isVisible = false;
}

jQuery-ui menu submenu not collapsing on mouseout

I'm playing around with the Jquery-ui menu widget and I'm trying to get some functionality that seems like it should be REALLY obvious... but I might be overlooking it, or maybe they did when they wrote it.
I was able to get the menu horizontal easily enough, and make the submenu come down vertically... but, for the life of me, I can't get the submenu to collapse when I mouseout of the menu. It just stays there until I click something else. REALLY annoying. As a designer, it goes against all of my sensibilities and I can't see a reason for it to do just hang there.
Did I break the thing, or do I need to add something special?
As a side note, I've been fiddling with the Jquery-ui js for a couple hours, editing the menu widget. I tinkered around here:
// DEFAULT FUNCTION:
// Clicks outside of a menu collapse any open menus
this._on( this.document, {
click: function( event ) {
if ( !$( event.target ).closest( ".ui-menu" ).length ) {
this.collapseAll( event );
}
// Reset the mouseHandled flag
this.mouseHandled = false;
},
// I ADDED THIS:
mouseout: function( event ) {
this.collapseAll( event );
}
});
It produced the behavior I was going for... sort of...
Now I have spotty mouse enter detection for my menu item with the submenu attached. Not really ok with putting a live site up where the menu only works some of the time. Any ideas? Am I doing this the hard way or the wrong way, or is there something I simply don't know about? I'm not the greatest with jQuery, just started in fact.
I'm using a setup that is basically an augmented version of the example on the jQuery UI demo page of the menu widget, so I don't think it's my html that's causing the problem.
I just had the same problem and find the solution here. I hope it works with you.
Ok, this just took some searching.
I used jplfl's example in the previous answer as a guide and went into the jquery-ui.js file, searching for any functions that had to do with the menu. I found line # 9715 where mouseleave was bound to "collapseAll" instead of calling a function which actually collapsed the menu. I changed it to this:
mouseleave: function( event ){
this.collapseAll( event, true );
},
And now it works.
So, Yeah. jplfl's answer was definitely helpful, but I thought this might be pertinent as it goes into a little more detail.
Aaron:
Just search for 'ui.menu' and scroll down till you see
mouseleave: "collapseAll"
line, for me it is 11507 (jquery-ui-1.10.3)
I'm not sure if this will work:
$(document).ready(function() {
$(“#menu”).menu({
mouseleave: function( event ){ this.collapseAll( event, true ); }
});
But I'm sure there is another option.
You may check this link:

Trigger Javascript click/touch event only if NO other events are simultaneously triggered?

I'm trying to make a navigation menu which is hidden from view but that which appears by touching/clicking on the screen.
The problem I see is that touching/clicking many places on the screen could open the navigation menu while simultaneously triggering an event on whatever button, link, etc. that might have been in the vicinity.
So far, I'm trying to handle this with a :not clause in jQuery. Unfortunately there is something not work with the :not clause as the toggling happens regardless of where you click within the body.
HTML:
<div id="NavigationMenu">i'm the navigation menu</div>
<div class="icon-reorder">toggle</div>
<div id="main_content">i'm the main content
<button type="button">button</button>
</div>​
JS:
$(document.body).on('click', ['body:not(".btn, a, i, button, input, textarea")', '.icon-reorder'], function(){
console.log('clicked');
$('#NavigationMenu').toggle();
$('#main_content').toggle();
});
$('button').on('click', this, function(){
console.log('button clicked');
});
Might someone be able to help with this code? Or is this even the right way to go about solving this problem? It looks a little hack-ish to me.
This navigation menu is the main one for my site so having an annoying UI/UX (nav opens too much/too little) is a deal breaker. I mainly interested in touch compatible code but any and all UI/UX suggestions would be welcome...
Instead of using a :not clause, why not use event delegation (which, I only learned two months ago, is a fancy term for handling the events with a callback, on a parent element)
$(body).on("click", function(event) {
if(event.target.type !== "button" && <whatever other conditions>) {
<toggle menu>
}
});
Here's an updated Fiddle . I'm logging the click event object to the console so you can look at event.target and see if there's anything more suited to your needs to compare to

Controlling dijit.MenuBar with MouseOver events

In Dojo, is it possible to configure dijit.MenuBar so that the menus are triggered by MouseOver and MouseOut events? Actually this behavior is available already, but it is switched on or off by initial or successive mouse click events - so initially, MouseOver would not cause menu popup, but if the user clicks on a menu item, the menubar then becomes responsive to MouseOver events. A successive mouse click would again switch off this behavior.
What I would like to have is menus and sub-menus popping up based on MouseOver events without interference from click events. Please check the examples at http://dojotoolkit.org/reference-guide/dijit/MenuBar.html to see what I mean.
Your question piqued my interest enough to make a working solution.
I checked the dijit._MenuBase source code at dijit/Menu.js and apparently there is a this.isActive flag that is checked before proceeding. So I created a subclass that just sets this flag as true beforehand:
_ActivateOnMouseoverMixin = dojo.declare(null, {
onItemHover: function(item){
if(!this.isActive){
this._markActive();
}
this.inherited(arguments);
}
});
ActiveMenuBar = dojo.declare([dijit.MenuBar, _ActivateOnMouseoverMixin], {});
As a bonus, you can also modify the delay with the popupDelay variable (I changed it to be faster in the example)
I have no idea if there is another, more sane, way to do the same thing.
Here is an example that extends the solution of 'Hugomg' to case of unhovering the menu and the sub-menu:
[enter link description here][1]
[1]: http://jsfiddle.net/vg10c9md/2/

Mouseover events in one object, and mouse out events on an object resulting of the first event

The title is a little bit messy, so let me try to explain in the actual question:
Suppose I have the following HTML setup.
<div id="userMenu">
<div id="userMenu-expanderLink">Mouseover here!</div>
<div id="userMenu-collapserLink">You can close the menu by mouse out.</div>
<div id="userMenu-expandedContent">Extra Content</div>
</div>
Now, userMenu and userMenu-expanderLink are shown by default. userMenu-expandedContent and userMenu-collapserLink are hidden by default.
What I am trying to do in jQuery is to slideDown the userMenu-expandedContentwhen a mouseover event occurs on userMenu-expander. All good there, this is my code:
$("#userMenu-expanderLink").mouseover(function() {
$("#userMenu-expandedContent").stop().slideDown(200);
$("#userMenu-expanderLink").hide();
$("#userMenu-collapserLink").show();
$("#userMenu").addClass("userMenu-expanded");
});
As you can see, I'm also hiding the expanderLink and showing the collapserLink; and also adding a class called userMenu-expanded to #userMenu. Until now, this code has no problems. Everything works well.
But now, I want that when the user has a mouseOut event on #userMenu.userMenu-expanded, effectively moving his mouse out of the #userMenu that is expanded, I want when that happens, the expandedContent is slideUp'd, the expander and collapser links swapped, and the class removed. I know how to do that, but handling the event seems to be a problem.
Putting $("#userMenu.userMenu-expanded")... directly alongside the code I have of course does not work, since a div with such id and such class is only generated if the menu has been expanded, and the div's class is removed once the menu is collapsed. I don't directly use a mouseover/mouseout event on one object because I want the collapsing to be triggered only when the user takes his mouse out of the menu, not the expander link.
So, here's my problem. How can I get such mouse out event? I have tried adding the event handler in the callback of .addClass, but no avail, it would basically permanently close that expanded menu (basically I can't ever expand it again until I reload the page).
How can this be done? I'm not very experienced with jQuery, so a detailed answer would be most appreciated. I'm more interested on how can this be done rather than just accomplishing it, I want to learn ^_^.
Thanks!
I have found a correct way to do this. This is my final implementation.
$(document).ready(function() {
// UserMenu Expander, which is also a form of drop down
$("#userMenu-expander").mouseenter(function() {
//alert("Usermenu expanding…");
$("#userMenu-expandedContent").slideDown(200, function() {
$("#userMenu").addClass("userMenu-expanded");
});
$("#userMenu-expanderLink").hide();
$("#userMenu-collapserLink").show();
});
$("#userMenu.userMenu-expanded").live('mouseleave', function() {
//alert("Usermenu de-expanding…");
$("#userMenu-expandedContent").slideUp(200);
$("#userMenu-expanderLink").show();
$("#userMenu-collapserLink").hide();
$("#userMenu").removeClass("userMenu-expanded");
});
});

Categories