jQuery tabs not working in mobile - custom code - javascript

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.

Related

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

Sticky scroll with active class on navigation items

I currently have a sticky scroll navigation that changes the active class as it passes each section on the page. It is a little buggy though...
heres my jquery code:
var s = $("#page-nav"),
pos = s.position();
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= pos.top) {
s.addClass("stick");
$('.main').css('margin-top', '60px');
} else {
s.removeClass("stick");
$('.main').removeAttr('style');
}
});
$(window).scroll(function() {
var y = $(this).scrollTop();
$('.linky').each(function (event) {
id = $(this).attr('href');
if (y >= $(id).offset().top) {
$('.linky').not(this).removeClass('active');
$(this).addClass('active');
}
});
});
//page nav
$("#page-nav li a").click(function(e) {
e.preventDefault();
$("#page-nav li a").removeClass('active');
$(this).addClass('active');
goToByScroll($(this).attr("href").replace("#", ""));
});
function goToByScroll(id){
$('html,body').animate({
scrollTop: $("#"+id).offset().top},
'slow');
}
And here is a working example over at codepen
How can I improve my javascript so that is performs a little smoother. As you can see when you click a link it still thinks it is within that section and the active class flickers.
Your problem was that you were setting the active class when you click an link and also when you move. What I did was to remove the class handling from the click event handler and let the scroll handler take care of everything. This way, there is no flicker. Here you have an updated CodePen.
$("#page-nav li a").click(function(e) {
e.preventDefault();
goToByScroll($(this).attr("href").replace("#", ""));
});
If this solution is not good enough for you, tell me and I'll think something more sophisticated.
New working solution here.
Basically, I created a variable that indicates whether you have clicked a link or not. If you have, then the scroll handler won't change CSS classes (avoiding the flicker). Then, in the complete handler of your animate function, I set it to false again (allowing for class changes as you scroll):
var s = $("#page-nav"),
pos = s.position(),
linkClicked = false;
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= pos.top) {
s.addClass("stick");
$('.main').css('margin-top', '60px');
} else {
s.removeClass("stick");
$('.main').removeAttr('style');
}
});
$(window).scroll(function() {
var y = $(this).scrollTop();
$('.linky').each(function (event) {
id = $(this).attr('href');
if (y >= $(id).offset().top) {
if (!linkClicked) {
$('.linky').not(this).removeClass('active');
$(this).addClass('active');
}
}
});
});
//page nav
$("#page-nav li a").click(function(e) {
e.preventDefault();
$("#page-nav li a").removeClass('active');
$(this).addClass('active');
linkClicked = true;
goToByScroll($(this).attr("href").replace("#", ""));
});
function goToByScroll(id){
$('html,body').animate({
scrollTop: $("#"+id).offset().top},
'slow', function() {
linkClicked = false;
});
}

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();
?

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

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;
});

Categories

Resources