jQuery.trigger => Selector:toggle on Cloned Element = Loop - javascript

On Cloned menu (.original / .cloned ) i have problem getting 1 of the 2 toggle functions to work correctly.
Part #1 Cloned Menu Toggle
$('.mobile-nav-toggle').click(function(e) {
e.preventDefault();
var $this = $('a');
if ($this.hasClass('open')) {
$this.trigger('mobilenav:close', [$this, 'close']);
} else {
$this.trigger('mobilenav:close', [$this, 'open']);
}
// Guess this one get executed twice
$this.trigger('mobilenav:toggle', [$this]);
});
Part #2 Cloned Menu Toggle
$(document).on('mobilenav:toggle', function(e, $obj) {
$('.original>div>nav').toggleClass('open').slideToggle(300);
$('.cloned>div>nav').toggleClass('open').slideToggle(300);
$obj.toggleClass('close');
});
I understand the issue is that am using $('a') as my selector make mobilenav:toggle trigger twice and then it will loop around opening and closing the two menus.
On the other hand the mobile-nav-toggle icon don't loop and syncs correctly, menu drawer is in sync but looping.
If i change
$this = $('a');
// To
$this = $(this);
Everything will work except toggle icon that will not be in sync.
Have given this a couple of hours now and my macro level of Javascript / jQuery is just to weak as probably can be seen on my half ass solution on Part #2.
Watch my masterpiece in action!
Help me do this as a pro !
Edit: Adding rest of code from Sticky / cloning function after request
$('.menu').addClass('original').clone().insertAfter('.menu').addClass('cloned').css('position', 'fixed').css('top', '0').css('margin-top', '0').css('z-index', '100').removeClass('original').hide();
scrollIntervalID = setInterval(stickIt, 10);
function stickMenu() {
var orgElementPos = $('.original').offset();
orgElementTop = orgElementPos.top;
if ($(window).scrollTop() >= (orgElementTop)) {
orgElement = $('.original');
coordsOrgElement = orgElement.offset();
leftOrgElement = coordsOrgElement.left;
widthOrgElement = orgElement.css('width');
$('.cloned').css('left', leftOrgElement + 'px').css('top', 0).css('width', widthOrgElement).show();
$('.original').css('visibility', 'hidden');
$('.original>div>nav').removeAttr("id", "off-nav");
} else {
$('.cloned').hide();
$('.original>div>nav').attr("id", "off-nav");
$('.original').css('visibility', 'visible');
}
}
Edit: Added Markup
<header class="menu" id="main-header-wrap"">
<div class="" id="main-header">
<div class="toptop"></div>
<div class="container">
<div class="row">
<div id="logotype">
</div>
<div id="menu-container" class="clearfix">
<a class="mobile-nav-toggle lines-button x skiplink">
<span class="lines"></span>
</a>
<nav>
</nav>
</div>
</div>
</div>
<nav id="off-nav">
<ul><li class=""></li></ul>
</nav>
</div>
</header>

Related

Jquery Menu icon changes only works after reload

I'm trying to implement a 'slide toggle menu' and I would like that the menu icon changes to 'close' every time the menu icon is clicked to open, and goes back to normal every time the menu icon is clicked to close. But it works only when I reload the page. After that, the icon is always the same: normal (not X).
Can you help me to find the problem?
/* Mobile navigation */
$(function() {
if ($(window).width() < 980) {
var nav = $('.js--mobile-nav');
nav.hide();
}
});
$('.js--mobile-nav-icon').click(function() {
var nav = $('.js--mobile-nav');
var icon = $('.js--mobile-nav-icon i');
nav.slideToggle(200);
if (icon.hasClass('icon ion-md-rainy')) {
icon.addClass('icon ion-md-restaurant');
icon.removeClass('icon ion-md-rainy');
} else {
icon.addClass('icon ion-md-rainy');
icon.removeClass('icon ion-md-restaurant');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="sec1" id="sec1">
<div class="header">
<div class="logo-img"><img src="img/logo_small.png" alt="Logo Thais Fachini" class="logo-img"></div>
<!-- Sticky Navigation -->
<div class="logo-img-sticky">
<img src="img/logo_small.png" alt="Logo Thais Fachini" class="logo-img-sticky">
</div>
<div class="logo-name">
<h1>THAÍS FACHINI</h1>
<p>Nutrition for Life</p>
</div>
<!-- Sticky Navigation -->
<div class="logo-name-sticky">
<h1>THAÍS FACHINI</h1>
<p>Nutrition for Life</p>
</div>
<div class="links">
<!-- Mobile Navigation --><a class="mobile-nav-icon js--mobile-nav-icon"><i class="icon ion-ios-menu"></i></a>
<nav class="js--mobile-nav">
Sobre
Como funciona
Assinar
Cliente
</nav>
</div>
</div>
I end up changing the code like this (I thought it was because of hasClass wasn't recognized). The code works.
$(function() {
var menu = false;
if ($(window).width() < 980){
var nav = $('.js--mobile-nav');
nav.hide();
}
$('.js--mobile-nav-icon').click(function() {
var nav = $('.js--mobile-nav');
var icon = $('.js--mobile-nav-icon i');
nav.slideToggle(200);
if (menu) {
icon.removeClass('icon ion-ios-close');
icon.addClass('icon ion-ios-menu');
} else {
icon.removeClass('icon ion-ios-menu');
icon.addClass('icon ion-ios-close');
}
menu = !menu;
});
});

Using .width() returns undefined

I'm currently building a script to create a slider and stumbled upon an error I can't seem to solve. Basically I trying to get the width of a container and multiply it base on the number of slides inside.
Here is a snippet a of the code I'm working on. Whenever I try to use .width in order to fetch the width of a container it returns undefined on the console. I tried looking back and forth on my code but I can't seem to pinpoint the issue.
_setSliderWidth() {
this.sliderBanner = this.$el.find('.slider-banner');
this.sliderBannerWidth = this.sliderBanner.width();
console.log(this.sliderBannerWidth);
this.slides.width(this.sliderBannerWidth);
this.slidesContainer.width(this.sliderBanner.width() * this.slideCount);
}
-- -- -- -- --
'use strict';
(function($) {
/**
* Base Image Slider class
*/
class ImageSlider {
constructor(el) {
this.$el = $(el);
this._dom();
this.slideCount = this.slides.length;
this.currentSlide = 0;
this.arrows = {
prev: this.$el.find('.arrow.-prev'),
next: this.$el.find('.arrow.-next')
};
// image formatting and detection
this.$el.find('img').each(function(e, el) {
let $img = $(el);
if ($img.height() > $img.width())
$img.addClass('-portrait');
});
this._setSliderWidth();
}
_dom() {
this.slides = this.$el.find('.slides');
this.slidesContainer = this.$el.find('.slider-items');
}
init() {
this._bind();
this._evaluatePosition();
}
_bind() {
this.arrows.next.on('click', this._nextSlide.bind(this));
this.arrows.prev.on('click', this._prevSlide.bind(this));
}
_nextSlide() {
this.currentSlide++;
this._moveSlide();
}
_prevSlide() {
this.currentSlide--;
this._moveSlide();
}
_setSliderWidth() {
this.sliderBanner = this.$el.find('.slider-banner');
this.sliderBannerWidth = this.sliderBanner.width();
console.log(this.sliderBannerWidth);
this.slides.width(this.sliderBannerWidth);
this.slidesContainer.width(this.sliderBanner.width() * this.slideCount);
}
_moveSlide() {
// set the min and max range
if (this.currentSlide < 0) this.currentSlide = 0;
if (this.currentSlide > this.slideCount - 1) this.currentSlide = this.slideCount - 1;
this._evaluatePosition();
this._move();
}
_move() {
let position = this.currentSlide * -100;
this.slidesContainer.css({
transform: 'translate(' + position + '%, 0)'
});
}
_evaluatePosition() {
this.arrows.prev.toggleClass('-hide', (this.currentSlide === 0));
this.arrows.next.toggleClass('-hide', (this.currentSlide === this.slideCount - 1));
}
}
$(document).ready(function() {
//--------------------------------------------------
// Image Slider
let $imageSliders = $('.image-slider');
$imageSliders.each(function(e, el) {
let imageSlider = new ImageSlider(el);
imageSlider.init();
});
//--------------------------------------------------
// Slider Banner
let $bannerSliders = $('.slider-banner');
$bannerSliders.each(function(e, el) {
let bannerSlider = new ImageSlider(el);
bannerSlider.init();
});
});
})(jQuery);
HTML
<div class="slider-banner -alternate">
<span href="#" class="arrow -prev -hide"></span>
<span href="#" class="arrow -next"></span>
<div class="slider-items">
<div class="slides">
<div class="image" style="background-image:url(/iom/sites/default/files/2018-07/partnerships-2_0.jpg)">
<div class="banner-detail">
<div class="article-detail">
<div class="timestamp">
page
</div>
<h2 class="title">
Migrant Integration
</h2>
<div class="mini-caption">
IOM supports policies and strategies that promote the social, economic and cultural inclusion of migrants within existing legal frameworks in countries of destination.
</div>
More Details
</div>
</div>
</div>
</div>
<div class="slides">
<div class="image" style="background-image:url(/iom/sites/default/files/2018-07/definitional-issues_1.jpg)">
<div class="banner-detail">
<div class="article-detail">
<div class="timestamp">
page
</div>
<h2 class="title">
Forum on Migration, Trade and the Global Economy
</h2>
<div class="mini-caption">
IOM, together with partners ICTSD and Fundanción Foro del Sur will host the Forum on Migration, Trade & the Global Economy in Buenos Aires on 14 December.
</div>
More Details
</div>
</div>
</div>
</div>
<div class="slides">
<div class="image" style="background-image:url(/iom/sites/default/files/2018-07/identity-management_0.jpg)">
<div class="banner-detail">
<div class="article-detail">
<div class="timestamp">
page
</div>
<h2 class="title">
Comparative Research on the Assisted Voluntary Return and Reintegration of Migrants
</h2>
<div class="mini-caption">
Assisted Voluntary Return and Reintegration (AVRR) is an indispensable part of a comprehensive approach to migration management aiming at orderly and humane return and reintegration of migrants.
</div>
More Details
</div>
</div>
</div>
</div>
</div>
</div>
It seems from your screenshots and code that the this. sliderBanner object does NOT return a DOM object and thus the .width() would be undefined.
To resolve you can:
1) Retrieve the DOM object through a weird method of this.sliderBanner.prevObject. More information in this thread: What is prevObject and why is my selector returning that?
The main problem is that the .find from the $el object can't doesn't have the slider banner object within it's DOM, so...
2) Try using this.sliderBanner = $(".slider banner") to select the banner from the document object instead

Add Class to Following Element

I had a look out on the interwebs for a jQuery image gallery and couldn't find one that suited what I wanted to do. So I, ended up creating one myself and am trying to figure out how to get the prev and next buttons to work.
<div class="gallery portrait">
<nav>
<div class="close"></div>
<div class="prev"></div>
<div class="next"></div>
</nav>
<div class="cover">
<img src="image.jpg">
</div>
<ul class="thumbs">
<li class="thumb">
<img src="image.jpg">
</li>
...
</ul>
</div>
I'm also using a bit of jQuery to add a class of .full to the .thumb a element, which makes the thumbnails go fullscreen.
$( ".thumb a" ).click(function() {
$( this ).toggleClass( "full" );
$( "nav" ).addClass( "show" );
});
Now I can't work out this next bit, I need a way when the .prev or .next buttons are clicked for it to remove the class of .full from the current element and add it to the next or previous .thumb a element, depending on which was clicked.
I've got a demo setup here: http://codepen.io/realph/pen/hjvBG
Any help is appreciated, thanks in advance!
P.S. If this turns out well, I plan on releasing it for free. I guess you can't have too many jQuery image galleries, eh?
You can use $.next() and $.prev():
$(".prev").click(function () {
var current = $('.full');
current.prev('.thumb').addClass('full');
current.removeClass('full');
return false; // stop propagation; prevents image click event
});
$(".next").click(function () {
var current = $('.full');
current.next('.thumb').addClass('full');
current.removeClass('full');
return false; // stop propagation; prevents image click event
});
I suggest the following additions to your code to handle wrapping around with your next and previous links:
$(".next").click(function (event) {
navigate("next");
return false;
});
$(".prev").click(function (event) {
navigate("prev");
return false;
});
function navigate(operation) {
var $thumbs = $(".thumb"),
$full = $thumbs.find("a.full").closest(".thumb"),
$next;
$thumbs.find('a').removeClass('full');
if (operation == 'prev' && $full.is($thumbs.first()))
$next = $thumbs.last();
else if (operation == 'next' && $full.is($thumbs.last()))
$next = $thumbs.first();
else
$next = $full[operation]();
$next.find('a').click();
}
Here is a forked CodePen.
Something like this will get you started, but what you're wanting to do takes a little time to get just right.
<script type="text/javascript">
var imgSrcs = ['/imgs/this.jpg', '/imgs/will.jpg', '/imgs/work.jpg', '/imgs/just.jpg', '/imgs/fine.jpg'];//img url loaded into an array
var btnPrev = document.getElementById('prev'),
btnNext = document.getElementById('next'),
cover = document.getElementById('cover'),
thumb = document.getElementById('thumb'),
currImgIx = 0;
btnPrev.onclick = function () {
if (currImgIx === 0) { return; };
currImgIx--;
cover.src = imgSrcs[currImg];
thumb.src = imgSrcs[currImgIx];
};
btnNext.onclick = function () {
if (currImgIx === imgSrcs.length - 1) { return; };
currImgIx++;
cover.src = imgSrcs[currImgIx];
thumb.src = imgSrcs[currImgIx];
};
</script>
<div class="gallery portrait">
<nav>
<div class="close">X</div>
<div id="prev" class="prev">Prev</div>
<div id="next" class="next">Next</div>
</nav>
<div class="cover">
<img id="cover" src="image.jpg">
</div>
<ul class="thumbs">
<li class="thumb">
<img id="thumb" src="image.jpg">
</li>
...
</ul>
</div>

If class also is class then.. move active class on click

(New at jQuery)
Trying to write a function that moves an active class along as an arrow is clicked..
html
<div id="steps">
<a href="#1" data-ref="dynamic-tabs slider-id">
<div class="step step-1 active">
<div class="step-img">
<img width="175" height="120" src="http://site.com/img.png">
</div>
<div class="step-title">Schedule Online or Wave Down a Biker</div>
<div class="step-over"></div>
</div>
</a>
<a href="#2" data-ref="dynamic-tabs slider-id">
<div class="step step-2">
<div class="step-img">
<img width="175" height="120" src="http://site.com/img.png">
</div>
<div class="step-title">We Bike to & Clean Your Car</div>
<div class="step-over"></div>
</div>
</a>
<a href="#3" data-ref="dynamic-tabs slider-id">
<div class="step step-3">
<div class="step-img">
<img width="175" height="120" src="http://site.com/img.png">
</div>
<div class="step-title">Come Back to a Clean Car</div>
<div class="step-over"></div>
</div>
</a>
</div>
<div class="nav-right" data-dir="next" title="Slide right">
right »
</div>
jQuery I'm trying
$(".nav-right a").click(function() {
if( $(".step-1").is('.active') ) {
$(".step-1").removeClass("active");
$(".step-2").addClass("active");
}
});
$(".nav-right a").click(function() {
if( $(".step-2").is(".active") ) {
$(".step-2").removeClass("active");
$(".step-3").addClass("active");
}
});
$(".nav-right a").click(function() {
if( $(".step-3").is(".active") ) {
$(".step-3").removeClass("active");
$(".step-1").addClass("active");
}
});
The problem seems to be that you create 3 (sic!) event handlers which fire sequentially when you click the link.
The first handler, as step 1 is active, sets step 2 to active.
The second handler, as step 2 is now active, sets step 3 to active
The third handler, as step 3 is now active, sets step 1 to active again.
In the end, nothing has happened.
Solution: Use only one event handler, and if-else-statements instead of only if-statements:
$(".nav-right a").click(function() {
if( $(".step-1").is('.active') ) {
$(".step-1").removeClass("active");
$(".step-2").addClass("active");
} else if( $(".step-2").is(".active") ) {
$(".step-2").removeClass("active");
$(".step-3").addClass("active");
} else if( $(".step-3").is(".active") ) {
$(".step-3").removeClass("active");
$(".step-1").addClass("active");
}
});
Of course, we can do much better and write a generic function, which also does not need the step-N classes:
var rotatedElements = $(".step");
$(".nav-right a").click(function() {
var cur = rotatedElements.filter(".active");
cur.removeClass("active");
var index = rotatedElements.index(cur) + 1;
if (index >= rotatedElements.length)
index = 0;
rotatedElements.eq(index).addClass("active");
});
As suggested in comment by theScientist you should use only one event handler.
$(".nav-right a").click(function() {
if( $(".step-1").is('.active') ) {alert('1');
$(".step-1").removeClass("active");
$(".step-2").addClass("active");
return;
}
if( $(".step-2").is(".active") ) {alert('2');
$(".step-2").removeClass("active");
$(".step-3").addClass("active"); return;
}
if( $(".step-3").is(".active") ) {alert('3');
$(".step-3").removeClass("active");
$(".step-1").addClass("active"); return;
}
});
Here it is in a single handler and about as concise as I can make it :
$(".nav-right a").click(function() {
var $steps = $(".step"),
index = $steps.filter('.active').removeClass("active").index();
$steps.eq((index + 1) % $steps.length).addClass('active');
});
There's a couple of problems here, but to address the question you could simplify your code to just this and remove the dependency on .step1, .step2, etc in case you decide to add more steps.
$('.nav-right').find('a').on('click', function(e) {
e.preventDefault();
var $activeStep = $('.step.active'),
$expectedNextStep = $activeStep.closest('a').next().find('.step'),
$nextActiveStep = $expectedNextStep.length ? $expectedNextStep : $('.step').first();
$activeStep.removeClass('active');
$nextActiveStep.addClass('active');
});
​
However; I would highly recommend putting this in an ordered list, and not wrapping block elements in an anchor. The page will work as you have it but it's just poor semantics.

In Javascript, is it possible to give tab an active style when it only unhides a div, instead of loading a new page?

I have Javascript tabs which show/hide divs instead of loading new pages. The tabs have a style which gives a hover effect. I now want to add an active style to match up with the curently visible div.
THE JAVASCRIPT, which does not work as it is from a version which loads pages:
function setActive() {
aObj = document.getElementById('nav').getElementsByTagName('a');
for(i=0;i < aObj.length;i++) {
if(document.location.href.indexOf(aObj[i].href) >= 0) {
aObj[i].className='active';
}
}
}
function showdiv(id){
document.getElementById(id).style.display = "block";
}
function hidediv(id){
document.getElementById(id).style.display = "none";
}
THE STYLE:
#pageAdmin { display:block; }
#userAdmin { display:none; }
THE HTML:
<ul id="nav">
<li><a onclick="showdiv('pageAdmin'); hidediv('userAdmin')"
href="#">Page Admin</a></li>
<li><a onclick="showdiv('userAdmin'); hidediv('pageAdmin')"
href="#">User Admin</a></li>
</ul>
<div id="pageAdmin">
<h1>Page admin</h1>
</div>
<div id="userAdmin">
<h1>User admin</h1>
</div>
This is my first question on SO, so I hope it is appropriate - please accept my apologies in advance if it is not!
Your setActive function isn't very helpful. And using location, href and hash it will be hard doing what you want.
You may change your script to
function showdiv(id){
var el = document.getElementById(id);
el.style.display = "block";
el.className = "active";
}
function hidediv(id){
var el = document.getElementById(id);
el.style.display = "none";
el.className = "";
}
Now it should do what you want.
However, you should take a look on jQuery.
Using jQuery you do eliminate the use of getElementById and you can much simpler attach an eventhandler for onclick's, as by using vanilla js. It is considered as bad practice to setup handlers in html attributes.
jQuery also handles you the splitting and joining the space separated className string, if you want to use multiple styles.
Using jQuery it looks like:
$("a", "#nav").click(function () {
var $navA = $(this);
var tabName = $navA.attr("data-tabName");
$(".tab").each(function () {
var $tab = $(this);
if ($tab.attr("id") === tabName) {
$tab.css({ display: 'block' }); // you may move this into active style
$tab.addClass("active");
$tab.removeClass("inactive");
}
else {
$tab.css({ display: 'none' }); // you may move this into inactive style
$tab.removeClass("active");
$tab.addClass("inactive");
}
});
});
The HTML for the jQuery example
<ul id="nav">
<li><a data-tabName="pageAdmin" href="#">Page Admin</a></li>
<li><a data-tabName="userAdmin" href="#">User Admin</a></li>
</ul>
<div class="tab" id="pageAdmin">
<h1>Page admin</h1>
</div>
<div class="tab" id="userAdmin">
<h1>User admin</h1>
</div>

Categories

Resources