How to untoggle a dropdown by clicking elsewhere in the page - stopPropagation? - javascript

I wonder if anyone can help to finally resolve an issue I brought up on SO a while back.
I am unable to untoggle these dropdown menus by clicking outside of the button, or anywhere else on the page.
Please see this jsFiddle.
I've seen folks using stopPropagaton() but am unsure how to apply it here.
Any ideas how to do this?
My toggling code:
var cur = null;
$(".toggle").click(function(e){
$('#nav ul:visible').hide();
if(cur == null || cur.currentTarget != e.currentTarget)
{
if(cur != null)
{
$(cur.currentTarget)
.children('a:first').children('span').removeClass('fc-state-active');
}
cur = e;
$(cur.currentTarget)
.children('a:first').children('span').addClass('fc-state-active');
$(cur.currentTarget)
.children('ul').show();
}
else
{
$(cur.currentTarget)
.children('a:first').children('span').removeClass('fc-state-active');
cur = null;
}
});

I believe the following should work for you. This utilizes jQuery's focusout() function:
$(".toggle").click(function(){
$('#nav ul:visible').hide();
$('span.fc-state-active').removeClass('fc-state-active');
$(this).children('a:first').children('span').addClass('fc-state-active');
$(this).children('ul').show();
}).focusout(function(){
$('#nav ul:visible').hide();
$('span.fc-state-active').removeClass('fc-state-active');
});
And here's an updated fiddle: jSFiddle
EDIT: The following works in FF & Chrome
New Fiddle: jsFiddle
$(".toggle").click(function(){
$('#nav ul:visible').hide();
$('span.fc-state-active').removeClass('fc-state-active');
$(this).children('a:first').children('span').addClass('fc-state-active');
$(this).children('ul').show();
hide = false;
});
$(document).click(function(){
if(hide){
$('#nav ul:visible').hide();
$('span.fc-state-active').removeClass('fc-state-active');
}
hide = true;
});
Reason: $(document).click() is called after $(".toggle").click()
EDIT 2: The working fiddle can be found here: jSFiddle
var hide;
$(".toggle").click(function(){
var active_span = $(this).children('a:first').children('span');
var active_ul = $(this).children('ul');
$(active_span).toggleClass('fc-state-active');
$("span.fc-state-active").not(active_span).removeClass('fc-state-active');
$(active_ul).toggle();
$("#nav ul:visible").not(active_ul).hide();
hide = false;
});
$(document).click(function(){
if(hide){
$('#nav ul:visible').hide();
$('span.fc-state-active').removeClass('fc-state-active');
}
hide = true;
});

Related

jQuery tabs not working in mobile - custom code

I have just noticed that on mobile and when resized to mobile my tabs are not clicking and changing and wondering if anyone can see why?
I created a fiddle here: https://jsfiddle.net/a6k70p0L/2/
Works fine in desktop view, but when resized the click events does seem to fire and change.
JS:
$(document).ready(function() {
var originalTabs = $('.originalTabs').html();
function clearTabs(){
$('.originalTabs').html(originalTabs);
}
//clearTabs();
//desktopTabs();
function desktopTabs(){
clearTabs();
// cretate tabs for desktop
var headers = $("#tab_description h6");
$('#tab_description h6').each(function(i){
$(this).nextUntil("h6").andSelf().wrapAll('<div class="tab" id="tab-'+i+'"/>');
});
for( var i = 0; i < headers.length; i++ ) {
$('.tabs').append('<li class="">'+headers[i].innerHTML+'</li>');
}
$('ul.tabs').each(function(){
var active, content, links = $(this).find('a');
var listitem = $(this).find('li');
active = listitem.first().addClass('active');
content = $(active.attr('href'));
$('.tab').hide();
$(this).find('a').click(function(e){
$('.tab').hide();
$('ul.tabs li').removeClass('active');
content.hide();
active = $(this);
content = $($(this).attr('href'));
active.parent().addClass('active');
content.show();
return false;
});
});
headers.remove(); // remove headers from description
$('#tab-0').show(); // show the first tab
}
function mobileTabs(){
clearTabs();
//alert("loaded mobile");
var headers = $("#tab_description h6");
$(headers).each(function(i) {
$(this).append('');
//$(this).nextUntil("h6").andSelf().wrapAll('<div class="aTab" id="tab-'+i+'"/>');
});
$('#tab_description h6').first().trigger('click').addClass("active");
$('#tab_description h6').first().nextUntil("h6").show();
}
var tabClick = function() {
//alert("clicked");
var accordionContent = $('#tab_description p, #tab_description ul, #tab_description table, #tab_description iframe, #tab_description div');
$('#tab_description h6').removeClass("active");
if (!$(this).hasClass("active")){
$(this).addClass("active");
}
// Check if current accordion item is open
var isOpen = $(this).next().is(":visible");
// Hide all accordion items
accordionContent.hide();
// Open accordion item if previously closed
if (!isOpen) {
$(this).nextUntil('h6').show();
}
// Disabled to stop on mobile auto scrolling down to product description when clicking through...
$('html, body').animate({
scrollTop: $(this).offset().top - 15
}, 2000);
return false;
}
//bind to resize
$(window).resize( function() {
if(isMobileLandscapeOnly.matches || isTabletLandscapeOnly.matches){
desktopTabs();
$('#tab_description h6').on("click, touchstart", tabClick);
} else if(isMobilePortraitOnly.matches || isTabletPortraitOnly.matches){
mobileTabs();
$('#tab_description h6').on("click, touchstart", tabClick);
} else if(isDesktop) {
desktopTabs();
}
});
//check for the orientation event and bind accordingly
window.addEventListener("orientationchange", function() {
if(isMobileLandscapeOnly.matches || isTabletLandscapeOnly.matches){
desktopTabs();
$('#tab_description h6').on("click, touchstart", tabClick);
} else if(isMobilePortraitOnly.matches || isTabletPortraitOnly.matches){
mobileTabs();
$('#tab_description h6').on("click, touchstart", tabClick);
} else if(isDesktop) {
desktopTabs();
}
}, false);
if(isMobileLandscapeOnly.matches || isTabletLandscapeOnly.matches){
//alert("Mobile / Tablet (Portrait)");
desktopTabs();
$('#tab_description h6').on("click, touchstart", tabClick);
//console.log(originalTabs);
} else if(isMobilePortraitOnly.matches || isTabletPortraitOnly.matches){
//alert("Mobile / Tablet (Portrait)");
mobileTabs();
$('#tab_description h6').on("click, touchstart", tabClick);
} else if(isDesktop) {
//alert("Desktop");
desktopTabs();
}
});
Wrap your $('ul.tabs').each... in function and call the function when you have done all your appending of a/h6 tags in mobile. Or call your appending functions before the each statement.
The each statement fires and applies to everything that is currently available in the DOM, as your appending function hasn't started yet...the each statement doesn't know it's there. Then your appending stuff happens in your mobile functions and the each statement doesn't know it exists(as it is fired before the mobile functions).
Edit - Working Answer
Check the updated fiddle here: https://jsfiddle.net/a6k70p0L/6/
I've mentioned my changes in the comment below.
When you are using append() with an event like onclick() you should use delegate()jQuery function.
Example :
$("body").delegate('.class', 'click' ,function()
{
//do something
});
It is very helpfull. You can read the cause of this usage in other post on Stackoverflow.

jQuery - $(this) on iPad

I ran through the following Javascript to create a click function to toggle an active state and expand an element to show the full text.
$ = jQuery.noConflict();
$( document ).ready( function() {
$(".toggle_container").hide();
var toggle = function(el, direction) {
if (direction === 'open') {
var content = $(el).toggleClass("active").prev();
var curHeight = content.height();
content.css('height', 'auto');
var autoHeight = content.height();
content.height(curHeight).animate({height: autoHeight}, 100).promise().then(setTimeout(function(){content.removeClass("limited")}, 650));
} else {
var content = $(el).toggleClass("active").prev();
content.height(curHeight).animate({height: '70px'}, 100).promise().then(function(){content.addClass("limited")});
}
}
jQuery(".togglefeaBtn").on("click", function(){
if ($.trim($(this).text()) === 'Read MORE') {
$(this).text('Close');
toggle(this, 'open');
} else {
$(this).text('Read MORE');
toggle(this, 'close');
}
return false;
});
});
The issue that I've noticed is that when loaded on an iPad $(this), when in the click function, or $(el), when in the toggle function return a blank value, however, $(this).text('...') works perfectly.
Is there an issue with the way I'm using the click function, or do touch devices require a different set up?
Shouldn't it be toggle($(this), 'close'); not this? The context of this hasn't been defined.
You can also change $(el) to just be el.
var $ = jQuery.noConflict();
$( document ).ready( function() {
$(".toggle_container").hide();
var toggle = function(el, direction) {
if (direction === 'open') {
var content = el.toggleClass("active").prev();
var curHeight = content.height();
content.css('height', 'auto');
var autoHeight = content.height();
content.height(curHeight).animate({height: autoHeight}, 100).promise().then(setTimeout(function(){content.removeClass("limited")}, 650));
} else {
var content = el.toggleClass("active").prev();
content.height(curHeight).animate({height: '70px'}, 100).promise().then(function(){content.addClass("limited")});
}
}
jQuery(".togglefeaBtn").on("click", function(){
if ($.trim($(this).text()) === 'Read MORE') {
$(this).text('Close');
toggle($(this), 'open');
} else {
$(this).text('Read MORE');
toggle($(this), 'close');
}
return false;
});
});
After continuing to wrestle with this issue, I opted to take a much simpler route and scrap the Javascript method to animate the reveal and just use CSS Transform with max-height. The code got a lot smaller, and works nearly everywhere.
$ = jQuery.noConflict();
$( document ).ready( function() {
$(".toggle_container").hide();
jQuery(".togglefeaBtn").on("click", function(){
if ($.trim($(this).text()) === 'Read MORE') {
$(this).text('Close');
$(this).toggleClass("active");
$(this).prev().css('max-height', '500px');
$(this).prev().removeClass("limited");
} else {
$(this).text('Read MORE');
$(this).toggleClass("active");
$(this).prev().css('max-height', '70px');
$(this).prev().addClass("limited");
}
return false;
});
});

Bootstrap collapse - opening multiple accordions at the same time

I've got to a point where my accordions open up at the same time - see http://www.bootply.com/Go4t29rYyF
When you click on "tab1" all the "tab1s" open, when you click on "tab2" all the "tab2s" open - great! But I cant open "tab1s & tab2s" at the same time, it only works when I close one of the tabs first before opening another. The issue is with my js but cant work it out.
$(function () {
var $active = true;
$('.panel-title > a').click(function (e) {
e.preventDefault();
});
$('.number1-collapse').on('click', function () {
if (!$active) {
$active = true;
$('.panel-title > a').attr('data-toggle', 'collapse');
$('.number1').collapse('hide');
} else {
$active = false;
$('.number1').collapse('show');
$('.panel-title > a').attr('data-toggle', '');
}
});
$('.number2-collapse').on('click', function () {
if (!$active) {
$active = true;
$('.panel-title > a').attr('data-toggle', 'collapse');
$('.number2').collapse('hide');
} else {
$active = false;
$('.number2').collapse('show');
$('.panel-title > a').attr('data-toggle', '');
}
});
});
I've tidied up your code and changed to using the toggle method instead of having various flags. The problem is that you are sharing the active flag between them. Here is the improved code and Bootply:
$(function () {
$('.panel-title > a').click(function (e) {
e.preventDefault();
});
$('.number1-collapse').on('click', function () {
$('.number1').collapse('toggle');
});
$('.number2-collapse').on('click', function () {
$('.number2').collapse('toggle');
});
});
You may want to specify which elements you are effecting in your function using the event parameter
Example:
$('.number2-collapse').on('click', function (event) {
var panelTitle = $(event.currentTarget).find('.panel-title > a');
var number = $(event.currentTarget).find('.number2');
if (!$active) {
$active = true;
$(panelTitle).attr('data-toggle', 'collapse');
$(number).collapse('hide');
} else {
$active = false;
$(number).collapse('show');
$(panelTitle).attr('data-toggle', '');
}
});
This is an example. You may need to alter this code for it to work in your situation

Toggle CSS Javascript Function

I'm trying to show this CSS on the first click and remove it on the next but toggleClass doesnt seem to work and neither does my code below. All I can get is the CSS to show and it never gets removed. This is for use on Safari on iOS. Thanks for the help!
var menu_enabled = false;
$('#nav a').click(function () {
if (menu_enabled == true) {
$(this).removeClass('sf-js-enabled');
var menu_enabled = false;
}
else {
var menu_enabled = true;
$(this).addClass('sf-js-enabled');
}
});
You should checkout .toggleClass(), looks like it's exactly what you're wanting.
$('#nav a').click(function () {
$(this).toggleClass('sf-js-enabled');
});
Perhaps using the .className attribute?
var menu_enabled = false;
$('#nav a').click(function () {
if (menu_enabled == true) {
$(this).className = "";
menu_enabled = false;
}else
{
menu_enabled = true;
$(this).className = 'sf-js-enabled';
}
});
I'm not exactly sure what you're trying to accomplish overall, but addClass and removeClass should work just fine. I would add an ID or class to the element that you want to have the click attached to but that's just a personal preference.
$(document).ready(function(){
$('#element').click(function(){
if($('#element').hasClass('sf-js-enabled')){
$('#element').removeClass();
}
else{
$('#element').addClass('sf-is-enabled');
}
});
});

jQuery no Fade on Page Load

I've created your typical jQuery slider (code from here). Everything works just fine as is, except I don't want the fadeIn() to run when the page loads (it just looks weird since the user hasn't clicked anything yet). Any ideas how to fix this? Basically I want to leave it as is, except no fade on page load. Thanks!
// Tab slides
$(function () {
var tabContainers = $('div.slider > div');
$('div.slider ul.slider-nav a').click(function () {
tabContainers.hide().filter(this.hash).fadeIn();
$('div.slider ul.slider-nav a').removeClass('selected');
$(this).addClass('selected');
return false;
}).filter(':first').click();
});
// Tab slides
$(function () {
var tabContainers = $('div.slider > div'),
loaded = false;
$('div.slider ul.slider-nav a').click(function () {
var tab = tabContainers.hide().filter(this.hash);
if (loaded){
tab.fadeIn();
}else{
tab.show();
}
loaded = true;
$('div.slider ul.slider-nav a').removeClass('selected');
$(this).addClass('selected');
return false;
}).filter(':first').click();
});
Explanation: The script is loading a click handler, then (once loaded) calling the click handler it just created. Because of this, it will fade (as the handler is instructing it to do). This can be avoided by added a check (in this cases the loaded variable) that basically lets the first click flow through without any intervention, but makes any future calls apply the fade.
You could set a flag after the first run, which is then checked on successive runs.
// Tab slides
$(function () {
var tabContainers = $('div.slider > div');
var firstRun = true;
$('div.slider ul.slider-nav a').click(function () {
tabContainers.hide().filter(this.hash);
if(!firstRun) { tabContainers.fadeIn(); firstRun = false; }
$('div.slider ul.slider-nav a').removeClass('selected');
$(this).addClass('selected');
return false;
}).filter(':first').click();
});
Well can't you just remove
tabContainers.hide().filter(this.hash).fadeIn();
?

Categories

Resources