Determining causes of Bootstrap events being raised - javascript

I've got a Bootstrap accordion, and a button that expands all of the accordion panels. This works fine.
HTML:
<div class="btn btn-primary btn-sm" id="accordion-expand-all">Expand all</div>
JS:
$("#accordion-expand-all").click(function() {
$(".panel-collapse").each(function() {
if ($(this).hasClass("in") === false) {
$(this).collapse("toggle");
};
});
});
THE PROBLEM:
I'd like to make it so that when a user clicks on a panel's header, if the accordion panel is collapsed, then the panel expands... and the window scrolls down such that the accordion panel's header is positioned at the top of the page.
A naive approach would be to invoke some function like pageScrollToTop(expandedPanel) to the Bootstrap shown.bs.collapse event. But this conflicts with the "Expand All" button, which raises the shown.bs.collapse every time an accordion panel is expanded, and thus scrolling the page all over the place.
I know this is pretty unlikely, but when a shown.bs.collapse event is raised, is there a way of determining if it was raised by a user's click, versus being raised programmatically (as with the "Expand all" button)? If so, my website would know when and when not to call pageScrollToTop().

You could trigger a once only event after someone clicks an unexpanded accordion item. It's not quite what you were after (determining programmatically triggered vs user triggered event), but it should work.
$('a[data-toggle=collapse]').on('click', function(){
if($(this).is('.collapsed')) {
$(this.getAttribute('href')).one('shown.bs.collapse', function(){
$(document.body).stop().animate({scrollTop: $(this).offset().top});
});
}
});
Here's a demo: http://jsfiddle.net/97vju2b7/

As a workaround, I attached a click handler to the accordion panel-headers, and created a "mock" show.bs.collapse event which scrolls the page afterwards (I peeked at the bootstrap.js code base to figure out how to do this).
The buttons just iterate over the accordion panels and invoke $("#foo-panel").collapse("toggle"), which raises the real show.bs.collapse event.

Related

Synchronizing top-nav mouseleave for a tab and its associated submenu

I'm trying to create a top-nav menu as follows:
The idea is that when you click a tab, the tab itself gets highlighted in black and an associated menu shows up beneath it. This works fine.
I also want the menu to disappear and the tab to be unhighlighted if the mouse leaves either the tab or the menu. This is where I'm running into trouble. At the moment, the JQuery I use to handle this is roughly as follows:
$('.item-support a').click(function(e){
// removeClass('curr') from all other tabs
$('.item-support').addClass('curr');
$('#submenu-support').fadeIn('fast');
$('.item-support').mouseleave(function(e) {
$('.item-support').removeClass('curr');
$('#submenu-products').fadeOut('fast');
});
}else{ // Click again
$('.item-support').removeClass('curr');
$('#submenu-support').fadeOut('fast');
}
return false;
});
$('#submenu-products').mouseleave(function(e) {
$('.item-support').removeClass('curr');
$('#submenu-products').fadeOut('fast');
});
// Similar code for the other tabs
The problem is that the mouseleave events for the tab and sub-menu are not synchronized. So, for example, if the mouse leaves the support tab and enters the submenu below it, the submenu vanishes. I've tried many different approaches to get around this and even have one that crudely works using pageX and pageY co-ordinates, but I'm looking for a more elegant solution. How do I get the tab and its associated submenu to work in tandem? Is there a way to bind the two divs together? Or make mouseleave recognize an exception when entering a certain div?
You can check if either element is hovered, and do something like this:
$('.item-support, #submenu-support').mouseleave(function() {
setTimeout(function() {
if (!$('.item-support').is(':hover') && !$('#submenu-support').is(':hover')) {
$('.item-support').removeClass('curr');
$('#submenu-support').hide();
}
}, 50);
});
You also shouldn't bind your mouseleave event in the callback of another event. Currently every time you click item-support, you are binding another mouseleave event to it.

ExtJS react to user clicks on tab in tabpanel

(Yes, I know this is similar to How can I attach an react to user clicking on tabs in Ext.TabPanel, but it is a different question)
I have a tabpanel, the panels of which can be accessed either by clicking on a tab, or by clicking links in other tabs. One tab in the tabpanel has a menu for the user to select the desired subpanel. What I am trying to achieve is that clicking on the tab does not activate it - thus forcing the user to select an option from the menu. However, I still want the panel to activate when a link on another panel is clicked, or when a menu option is selected.
This is how I disable the panel:
'beforeactivate': function (component, eOpts) {
//to prevent loading tab content on tab/menu click
console.debug('tab disabler', arguments);
return false;
}
It works - just works too well, blocking everything. I have not been able to find a way to detect the difference between clicking the tab and, well, doing anything else at all.
Basically you already described the problem: you want to prevent activation of the tab only when the tab header is clicked, not altogether, so the beforeactivate event is not the way to go.
You can access the tab header (which is basically just a button in the tab bar) via the tab property on the panel and prevent the execution of its handler by stopping the propagation of the click event:
panel.tab.on({
'click': function(tab, e) {
e.stopEvent();
}
});
Note: the docs say you can also return false to achieve the same, however that does not seem to work.
This won't affect activation of the panel at all, but the interaction on the tab header.
Check out this fiddle for a working example.

hide an element when click any other place(outside the element) on the page

I am working on a popup menu in my web page. currently, I can successfully display the menu. What I want to do is hiding the menu when I click outside of the menu. I know one way to do this is bind the click event to the document:
$(document).on('click', function(event) {
// here I can hide the menu
});
but I don't want to do that way, because binding an click event to the document looks very ugly and make the code difficult to maintain.
many many thanks.:)
You can wrap your popup menu like this:
<div class="overlay">
<div class="popup">...</div>
<div>
And then
$(".overlay").click(function(){
// hide your popup
})
It would be good to make the overlay position:fixed

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

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