Bootstrap Dropdowns and Popovers call parent element's hide function - javascript

When a Bootstrap Dropdown item is selected or a Popover closed, the popped up content is closed. However, a call is also made to the hide() function of the parent:
<div id="Dropdown" class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown">
Dropdown button
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div>
<button id="Button" type="button" class="btn btn-lg btn-danger" data-toggle="popover" data-placement=bottom title="Popover title" data-content="And here's some amazing content. It's very engaging. Right?">Click to toggle popover</button>
<script>
Button.hide = function() {console.log('hide button called')};
Dropdown.hide = function() {console.log('hide dropdown called')};
</script>
In the above code, messages will appear on the console when the dropdown or popover are dismissed. Looking at the stack trace, the actual call to the hide function is coming from jQuery.
This happens in both Bootstrap 3 and 4.
Is there anyway to prevent this from happening? If not, is there a way to detect if hide() is being called from jQuery?

Don't know how will we stop the event but it's dispatched from Bootstrap. I have used unminified version and below is a snippet from Bootstrap 4 which generates hide event as shown in call stack by chrome. jQuery fires click on window event which Bootstrap listens and afterwards fires hide event for every element which should be hidden.
Dropdown._clearMenus = function _clearMenus(event) {
if (event && event.which === RIGHT_MOUSE_BUTTON_WHICH) {
return;
}
var backdrop = $(Selector.BACKDROP)[0];
if (backdrop) {
backdrop.parentNode.removeChild(backdrop);
}
var toggles = $.makeArray($(Selector.DATA_TOGGLE));
for (var i = 0; i < toggles.length; i++) {
var parent = Dropdown._getParentFromElement(toggles[i]);
var relatedTarget = { relatedTarget: toggles[i] };
if (!$(parent).hasClass(ClassName.OPEN)) {
continue;
}
if (event && event.type === 'click' && /input|textarea/i.test(event.target.tagName) && $.contains(parent, event.target)) {
continue;
}
var hideEvent = $.Event(Event.HIDE, relatedTarget);
$(parent).trigger(hideEvent);
if (hideEvent.isDefaultPrevented()) {
continue;
}
toggles[i].setAttribute('aria-expanded', 'false');
$(parent).removeClass(ClassName.OPEN).trigger($.Event(Event.HIDDEN, relatedTarget));
}
};

The answer is to detect the popup showing in the show.bs.popover event. Only do the hide if no popup was involved. Here's the code for Button in the sample above:
$('#Button').on('show.bs.popover', function () {
Button.popoverShowing = true;
})
Button.hide = function() {
if (Button.popoverShowing) {
Button.popoverShowing = false;
return;
}
Button.style.display = "none";
};

Related

All drop-down menus' text content change at the same time instead of updating separately

I have three drop-down menus on my site that I created via Bootstrap.
Ideally, when a user clicks on a drop-down menu and selects one of the inner menu items, the drop-down menu should update its text content to the text content of the selected menu item.
However, for any drop-down menu, if a user clicks on a menu item, all three drop-down menus' text content update instead of only that individual drop-down menu as seen below
My current approach is that the aria-expanded attribute for each drop-down button changes to true if a drop-down is open on a button. If the aria-expanded value of a specific drop-down menu is true, then that button's text content changes to the text content of the selected menu item.
JS
function addPlanesToDropDowns() {
let dropdowns = document.querySelector('.dropdown-menu');
let dropDownButtonOne = document.querySelector('#dropdownMenuButtonOne');
let dropDownButtonTwo = document.querySelector("#dropdownMenuButtonTwo");
let dropDownButtonThree = document.querySelector("#dropdownMenuButtonThree");
dropDownDisabledCheck();
for (let i = 0; i < state.airplaneData.length; i++) {
let dropDownMenuItem = document.createElement('a');
dropDownMenuItem.classList.add('dropdown-item');
dropDownMenuItem.href = '#';
dropDownMenuItem.textContent = state.airplaneData[i].make + " " + state.airplaneData[i].model;
dropDownMenuItem.addEventListener('click', function (event) {
event.preventDefault();
if (dropDownButtonOne.getAttribute('aria-expanded')) {
dropDownButtonOne.textContent = dropDownMenuItem.textContent;
dropDownButtonOne.setAttribute('aria-expanded', false);
}
if (dropDownButtonTwo.getAttribute('aria-expanded')) {
dropDownButtonTwo.textContent = dropDownMenuItem.textContent;
dropDownButtonTwo.setAttribute('aria-expanded', false);
}
if (dropDownButtonThree.getAttribute('aria-expanded')) {
dropDownButtonThree.textContent = dropDownMenuItem.textContent;
dropDownButtonThree.setAttribute('aria-expanded', false);
}
dropDownDisabledCheck();
});
dropdowns.appendChild(dropDownMenuItem);
}
}
HTML
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButtonOne" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Plane 1</button>
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButtonTwo" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Plane 2</button>
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButtonThree" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Plane 3</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<!-- <a class="dropdown-item" href="#">Planes go here</a> -->
</div>
</div>
Despite this, all three drop-down menus update with the same selected menu item text content even though I am only setting the text content on the button element with the specific ID attribute.
This fragment of code dropDownButtonOne.getAttribute('aria-expanded') returns a string, not a boolean value, so either "true" or "false".
As it is a non-empty string, it always evaluates to TRUE. You need to change your condition.
You could check if(dropDownButtonOne.getAttribute('aria-expanded') === "true") for example.

How to detect the click on a button that is inside of a dropdown with jquery

I am having trouble with a very simple thing. I have two buttons inside a dropdown, and I can't detect when the user click on them. The rest of the buttons works well, less this two.
The buttons are:
<div class="btn-group">
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-toggle="dropdown">Nodes</button>
<ul class="dropdown-menu" id="NodeFilters">
<button type='button' class='btn btn-secondary btn-sm' id='allFilter'>All</button>
<button type='button' class='btn btn-secondary btn-sm' id='cleanFilter'>Clean</button>
</ul>
</div>
My function is:
//This function controls if some button is clicked
function btnController(){
$('.btn').click(function(){
let id_btn = $(this).attr("id");
console.log(id_btn);
if(id_btn == 'allFilter' | id_btn == 'cleanFilter'){
alert('sss')
}
});
}
$(document).ready(function() {
btnController();
} );
When I click the rest of the buttons, the console.log(id_btn); prints the id of the button. But when I click these two buttons inside the dropdown, nothing happens. Why is this happening?
Note: When I click these buttons, the dropdown always gets closed.
Can somebody help me? Thank you very much!
Use event delegation like this:
$(function(){
$(document).on('click', '.btn', function(){
let id_btn = $(this).attr("id");
console.log(id_btn);
if(id_btn === 'allFilter' || id_btn === 'cleanFilter'){
alert('sss')
}
})
})

How to disable Javascript action on links (<a>) on Bootsrap3 non-real-links (<a href="#..."/>)?

I have an <a> tag on my website, a script which shows a loader when you click on a link (any links).
Here is the code :
window.onload = function(){
var loading = document.getElementById("loading");
var elements = document.querySelectorAll("a");
function showLoading(){
loading.style.display = "block";
}
for(var i = 0; i < elements.length; i++){
elements[i].addEventListener("click",showLoading);
}
}
Problem: on non-real links <a href="">, like "tabs" or "dropdown" on Bootstrap3, the load is still running. How can I avoid this ? (using a general .class or like this ?)
What about disabling those anchor tags using this jquery?
$('#anchor_tag').attr("disabled", true);
According to Bootstrap v4 documentation you can now use buttons instead of links, like this:
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu2">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
</div>
</div>
Alternatively, you could specify let elements = document.querySelectorAll("a:not(.class-all-inactive-links-share")).

Turn Inline OnClick to Click Jquery

I have the following inline styles for a dropdown menu list. I do not know how to make it in to 'click' using jQuery.
<button class="btn btn-primary mw" data-toggle="dropdown"><span class="caret">Subject</span></button>
<div class="dropdown-menu">
<a class="dropdown-item Business" onclick="selectBusiness()"> Business</a>
<a class="dropdown-item ICT" onclick="selectICT()"> ICT</a>
<a class="dropdown-item Arts" onclick="selectArts()"> Arts</a>
</div>
More information:
I tried to do this following code for a button and it's working fine. But as I said I do not know how to make it work for a dropdown menu.
var btn = document.querySelector("button.selectAll");
btn.addEventListener("click", selectAll);
....
Your input would be greatly appreciated!
If you want to use jQuery, you can add listener to the classes of each <a> inside your dropdown.
$(".Business").on('click', function() {
//selectBusiness() or your code here
});
$(".Arts").on('click', function() {
//selectarts() or your code here
});
$(".ICT").on('click', function() {
//selectICT() or your code here
});
Another solution would be to add a single listener for all your dropdown-items and then checking the content of the selected one.
$(".dropdown-item").on('click', function() {
switch ($(this).text()) {
case "Business":
selectBusiness();
break;
}
});

Response menu not collapsing and reloads page

At http://www.dentalo.se/contact it is using a responsive menu for mobile devices.
When clicking on the menu button it collapses the menu but the same time reloads the page.
Can anyone tell my why and how I can fix it?
Thanks for the time you are taking to help me.
Edit
HTML
<div class="container">
<div class="navbar-header">
<!-- BEGIN RESPONSIVE MENU TOGGLER -->
<button type="button" class="navbar-toggle btn navbar-btn" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- END RESPONSIVE MENU TOGGLER -->
<!-- BEGIN LOGO (you can use logo image instead of text)-->
<a class="navbar-brand logo-v1" href="/">
<img src="/assets/img/logo_blue.png" id="logoimg" alt="">
</a>
<!-- END LOGO -->
</div>
<!-- BEGIN TOP NAVIGATION MENU -->
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li id="MainMenuHome">Start</li>
<li class="dropdown" id="MainMenuDentalo">
<a class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" data-delay="0" data-close-others="false" href="#">
Dentalo
<i class="icon-angle-down"></i>
</a>
<ul class="dropdown-menu">
<li id="SubMenuAbout">Om Dentalo</li>
<li id="SubMenuJob">Lediga tjänster</li>
<li id="SubMenuConnect">Anslut</li>
</ul>
</li>
<li id="MainMenuLogIn">Logga in</li>
<li id="MainMenuContact">Kontakt</li>
<li class="menu-search">
<span class="sep"></span>
<i class="icon-search search-btn"></i>
<div class="search-box">
<form action="#">
<div class="input-group input-large">
<asp:TextBox runat="server" placeholder="Sök..." CssClass="form-control" ID="textBoxSearch"></asp:TextBox>
<span class="input-group-btn">
<asp:Button runat="server" CssClass="btn theme-btn" ID="ButtonSearch" OnClick="ButtonSearch_Click" Text="Sök" />
</span>
</div>
</form>
</div>
</li>
</ul>
</div>
<!-- BEGIN TOP NAVIGATION MENU -->
</div>
JavaScript
/*
* Project: Twitter Bootstrap Hover Dropdown
* Author: Cameron Spear
* Contributors: Mattia Larentis
*
* Dependencies?: Twitter Bootstrap's Dropdown plugin
*
* A simple plugin to enable twitter bootstrap dropdowns to active on hover and provide a nice user experience.
*
* No license, do what you want. I'd love credit or a shoutout, though.
*
* http://cameronspear.com/blog/twitter-bootstrap-dropdown-on-hover-plugin/
*/
;(function($, window, undefined) {
// outside the scope of the jQuery plugin to
// keep track of all dropdowns
var $allDropdowns = $();
// if instantlyCloseOthers is true, then it will instantly
// shut other nav items when a new one is hovered over
$.fn.dropdownHover = function(options) {
// the element we really care about
// is the dropdown-toggle's parent
$allDropdowns = $allDropdowns.add(this.parent());
return this.each(function() {
var $this = $(this),
$parent = $this.parent(),
defaults = {
delay: 500,
instantlyCloseOthers: true
},
data = {
delay: $(this).data('delay'),
instantlyCloseOthers: $(this).data('close-others')
},
settings = $.extend(true, {}, defaults, options, data),
timeout;
$parent.hover(function(event) {
// so a neighbor can't open the dropdown
if(!$parent.hasClass('open') && !$this.is(event.target)) {
return true;
}
if(shouldHover) {
if(settings.instantlyCloseOthers === true)
$allDropdowns.removeClass('open');
window.clearTimeout(timeout);
$parent.addClass('open');
}
}, function() {
if(shouldHover) {
timeout = window.setTimeout(function() {
$parent.removeClass('open');
}, settings.delay);
}
});
// this helps with button groups!
$this.hover(function() {
if(shouldHover) {
if(settings.instantlyCloseOthers === true)
$allDropdowns.removeClass('open');
window.clearTimeout(timeout);
$parent.addClass('open');
}
});
// handle submenus
$parent.find('.dropdown-submenu').each(function(){
var $this = $(this);
var subTimeout;
$this.hover(function() {
if(shouldHover) {
window.clearTimeout(subTimeout);
$this.children('.dropdown-menu').show();
// always close submenu siblings instantly
$this.siblings().children('.dropdown-menu').hide();
}
}, function() {
var $submenu = $this.children('.dropdown-menu');
if(shouldHover) {
subTimeout = window.setTimeout(function() {
$submenu.hide();
}, settings.delay);
} else {
// emulate Twitter Bootstrap's default behavior
$submenu.hide();
}
});
});
});
};
// helper variables to guess if they are using a mouse
var shouldHover = false,
mouse_info = {
hits: 0,
x: null,
y: null
};
$(document).ready(function() {
// apply dropdownHover to all elements with the data-hover="dropdown" attribute
$('[data-hover="dropdown"]').dropdownHover();
// if the mouse movements are "smooth" or there are more than 20, they probably have a real mouse
$(document).mousemove(function(e){
mouse_info.hits++;
if (mouse_info.hits > 20 || (Math.abs(e.pageX - mouse_info.x) + Math.abs(e.pageY - mouse_info.y)) < 4) {
$(this).unbind(e);
shouldHover = true;
}
else {
mouse_info.x = e.pageX;
mouse_info.y = e.pageY;
}
});
});
// for the submenu to close on delay, we need to override Bootstrap's CSS in this case
var css = '.dropdown-submenu:hover>.dropdown-menu{display:none}';
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
$('head')[0].appendChild(style);
})(jQuery, this);
It appears that your button element (navbar-btn) is submitting your form. Try adding type="button" to the button element.
Also in the click event handler you can also try to add e.preventDefault() to your click event handler.
I cant seem to find the JavaScript from the click event of your button so I can help much more. If this does not work, please edit your question to show the relevant HTML and JavaScript sections.

Categories

Resources