Problems with the animation queue deforming my divs - javascript

So, I have an animated drop down menu that slides down when your mouse is over it, then fades away when it isn't. Simple enough. But if the use moves the mouse before the animation is complete moving down, then it will either jump to the end of the queue exposing the entire menu and then fading, or it deforms the menu by making it shorter depending on what parameters I pass to stop().
How do I fix this?
EDIT::
Here's pictures of what I'm talking about:
Before:
http://img192.imageshack.us/i/beforetc.jpg/
After:
http://img682.imageshack.us/i/afterr.jpg/
$('.menu')
.mouseover( function() {
var subMenu = [];
$(this).next().children().each( function() {
subMenu.push( $(this) );
});
slideMenu(subMenu);
});
$('.menuItem').parent()
.mouseleave( function() {
$(this).find('.menuItem').children().stop(true,false).fadeOut(200);
});
function slideMenu(menu) {
var subMenu = $.makeArray(menu);
if ( subMenu.length == 0 )
return true;
else {
var menuItem = subMenu.shift();
$(menuItem).slideDown(50, function() { slideMenu(subMenu); });
}
}
<div class='box'>
<div>
<div class='menu'>Resources</div>
<div class='menuItem'>
<div>Library</div>
<div>Internet</div>
<div>Your mom</div>
</div>
</div>
</div>

Well I made another recursive method that checks if the element is animated, and then stops and hides it. The key here is separating each child and then using the stop()'s second parameter which jumps to the end of the animation queue. This will render the child fully visible and slid down so that is not truncated. Before when I was using the [...].children().each( function() {} ), it would jump the end of the entire animation queue effecting all children. Maybe this'll help someone else in my predicament.
$('.menuItem').parent()
.mouseleave( function() {
var subMenu = [];
$(this).find('.menuItem').children().each( function() {
subMenu.push( $(this) );
});
hideMenu(subMenu);
});
function hideMenu(menu) {
var subMenu = $.makeArray(menu);
if ( subMenu.length == 0 )
return false;
else {
var menuItem = subMenu.shift();
$(menuItem).is(':animated') ? $(menuItem).stop(false,true).hide() :
$(menuItem).fadeOut(300);
hideMenu(subMenu);
}
}

Related

Is it possible to completely ignore children when using events in javascript?

I've been working on task organization website at work and i've hit a roadblock. I'm trying to create a tasklist which holds cards (similar to how trello works). I've managed to get the cards to correctly drag & drop, and it works just as it should. However, my issue comes with an indicator that shows up when a card is hovered over by the dragged card. I've designed the indicator to activate when the drop target is entered. This works, until a child element within the drop target, is entered. Once that happens, the indicator freaks out, and only gets fixed when you exit the card, and re-enter(assuming you don't also enter a child element).
So, what I need is a way to ignore children and only see the parent element. I tried various methods with stopping both capturing and bubbling before they reach the children, but this didn't help. The children still forced the held card to believe it exited the drop target.
Since my code is pretty long, i'll just include a few lines from the javascript. Keep in mind, I'm semi new to web development, so my javascript may not follow the best practices. Originally, the code was better maintained and prettier to look at, but since i've been trying so many methods of ignoring children, it has got a bit hectic.
Anyway, any pointers would be awesome. Thanks in advance!
function addListeners(card)
{
//takes the interior card and adds listeners.
card.draggable = true;
card.style.cursor = 'pointer';
$(card).on('click', card, function() { editCard(card) });
card.addEventListener('dragstart', dragStart, false);
card.addEventListener('dragend', dragEnd, false);
card.addEventListener('dragenter', dragEnter, false);
card.addEventListener('dragover', dragOver, false);
card.addEventListener('dragleave', dragLeave, false);
card.addEventListener('drop', dragDrop, false);
//event.currentTarget !== event.target
return card;
}
function dragStart()
{
originalClassname = this.className;
this.className += ' holding';
this.id = 'dragged-card';
previousStyle = this.parentNode.style.display;
setTimeout(() => (this.parentNode.style.display = 'none'), 10);
}
function dragEnd()
{
this.className = originalClassname;
this.parentNode.style.display = previousStyle;
}
function dragEnter(e)
{
console.log("enter");
var enteredElement = e.currentTarget.parentNode;
var dropIndicator = enteredElement.querySelector("#drop-indicator");
dropIndicator.className += ' indicator-active';
}
function dragOver(e)
{
e.preventDefault();
}
function dragLeave(e)
{
console.log(e.currentTarget.parentNode);
if (e.currentTarget.parentNode === enteredElement)
{
var dropIndicator = e.currentTarget.parentNode.querySelector("#drop-indicator");
dropIndicator.className = 'drop-indicator';
}
else
{
console.log("Still in element.");
}
}
function dragDrop(e)
{
e.preventDefault();
var dropIndicator = e.currentTarget.parentNode.querySelector("#drop-indicator");
var draggedCard = document.getElementById('dragged-card');
tasklist.insertBefore(draggedCard.parentNode, this.parentNode);
dropIndicator.className = 'drop-indicator';
draggedCard.id = '';
}
A few notes. The cards are created dynamically and get parented to the tasklist. That card ends up in this form:
<div id="card" style="">
<div class="drop-indicator" id="drop-indicator"></div>
<div class="card card-sizer" draggable="true" style="cursor: pointer;" id="">
<span class="card-label" id="card-label" style="background: rgb(250, 86, 86);"></span>
<info class="card-info">
<h3 id="card-title">Stuff</h3>
<p id="card-description">Thaangs</p>
</info>
</div>
</div>
The interior card, with class card is what gets passed to the event listener function.

Using A Conditional Statement For A Menu

I have a Menu for a website that is broken into two parts.
A left hand panel and a right hand panel.
When clicking one of the two menu buttons the panel slides in from the side and covers the browser window.
At the moment I have got the function working.
Where I am getting stuck is creating the logic for the function using conditionals that say:
'If the left panel is 'active' / 'visible' and the right panel button is clicked, slide the left panel out of view while sliding in the right panel.'
and Vise Versa.
Here is the js-fiddle and below is the code:
NOTE: I've tried to fire an alert when the class name .panel--oneBtn.is-active yet nothing is happening. Thanks
HTML
<div class="wrapper">
<div class="nav_header">
<div class='nav_header_link_wrap abso-left panel--oneBtn'>
<span class="nav_header_link abso-left open">Eat.Drink.Sleep</span>
<span class="nav_header_link abso-left close">Close</span>
</div>
<div class='nav_header_link_wrap abso-right panel--twoBtn'>
<span class="nav_header_link abso-right open">Bookings/Contact</span>
<span class="nav_header_link abso-right close">Close</span>
</div>
</div>
<div class="menuPanel panel--one"></div>
<div class="menuPanel panel--two"></div>
</div>
JS
function navSlider(){
var $buttonOne = $('.panel--oneBtn'),
$buttonTwo = $('.panel--twoBtn');
$buttonOne.on('click', function() {
$('.panel--one').toggleClass('is-visible');
$buttonOne.find('.open').toggleClass('is-gone');
$buttonOne.find('.close').toggleClass('is-visible');
$(this).toggleClass('is-active');
});
$buttonTwo.on('click', function() {
$('.panel--two').toggleClass('is-visible');
$buttonTwo.find('.open').toggleClass('is-gone');
$buttonTwo.find('.close').toggleClass('is-visible');
$(this).toggleClass('is-active');
});
if ( $buttonOne.hasClass('is-active') ){
alert('Left is active');
} else if ($buttonTwo.hasClass('is-active') ){
alert('Right is active');
}
}
navSlider();
Is this effect what you're looking for?
https://jsfiddle.net/snookieordie/oez0488h/41/
$buttonOne.on('click', function() {
//added this code
if ( $buttonTwo.hasClass('is-active') ) {
$('.panel--two').toggleClass('is-visible');
$buttonTwo.find('.open').toggleClass('is-gone');
$buttonTwo.find('.close').toggleClass('is-visible');
$buttonTwo.toggleClass('is-active');
}
//
$('.panel--one').toggleClass('is-visible');
$buttonOne.find('.open').toggleClass('is-gone');
$buttonOne.find('.close').toggleClass('is-visible');
$(this).toggleClass('is-active');
});
$buttonTwo.on('click', function() {
//added this code
if ( $buttonOne.hasClass('is-active') ) {
$('.panel--one').toggleClass('is-visible');
$buttonOne.find('.open').toggleClass('is-gone');
$buttonOne.find('.close').toggleClass('is-visible');
$buttonOne.toggleClass('is-active');
}
//
$('.panel--two').toggleClass('is-visible');
$buttonTwo.find('.open').toggleClass('is-gone');
$buttonTwo.find('.close').toggleClass('is-visible');
$(this).toggleClass('is-active');
});

Loop through element and change based on link attribute

Trying to figure this out. I am inexperienced at jQuery, and not understanding how to loop through elements and match one.
I have 3 divs:
<div class="first-image">
<img src="images/first.png">
</div>
<div class="second-image">
<img src="images/second.png">
</div>
<div class="third-image">
<img src="images/third.png">
</div>
And off to the side, links in a div named 'copy' with rel = first-image, etc.:
...
Clicking the link will fade up the image in the associated div (using GSAP TweenMax)
Here is the function I've been working on to do this... but I am not fully understanding how to loop through all the "rel" elements, and make a match to the one that has been clicked on.
<script>
//pause slideshow on members to ledger when text is clicked, and show associated image
$(function() {
$('.copy').on('click','a',function(e) {
e.preventDefault();
var slideName = $(this).attr('rel');
$("rel").each(function(i){
if (this.rel == slideName) {
console.log(this);
}
});
//var change_screens_tween = TweenMax.to('.'+slideName+'', 1, {
//autoAlpha:1
//});
});
});
</script>
What am I doing wrong? I don't even get an error in my browser. :-(
Thanks to the answer below, I got farther. Here is my revised code.
$('[rel]').each(function(k, v){
if (v == slideName) {
var change_screens_tween = TweenMax.to('.'+slideName+'', 1, {
autoAlpha:1
});
} else {
var change_screens_tween = TweenMax.to('.'+slideName+'', 1, {
autoAlpha:0
});
}
});
});
This is starting to work, but makes the screenshots appear and then instantly fade out. And it only works once. Any thoughts?
Add brackets around rel, like so:
$('[rel]').each(function() {
if ($(this).attr('rel') == slideName) {
console.log(this);
}
});

jQuery Accordion | Open first element on pageload & active state confusion

I am using the Javascript below to animate an accordion (it's a slightly modified variant of the one explained here: http://tympanus.net/codrops/2010/04/26/elegant-accordion-with-jquery-and-css3/.
Now I wanted to have the first element to be open on pageload, so I figured I just give it some sort of extra-class via Javascript (and define that .active state via CSS) to have it open up.
This worked, however if I hover over any but the first-element with said .active class, the first element keeps its state, and stays open until I hover over it at least once.
So, what I want is: the first element of my accordion is open and collapses if the user hovers over any of the elements that are not the first. I think I need to add a line in the hover function to either take the class away of the first element or to give the new element the active state, but I don't know how to do it and keep breaking the thing.
<script type="text/javascript">
jQuery(function() {
activeItem = jQuery("#accordion li:first");
jQuery(activeItem).addClass('active');
jQuery('#accordion > li, #accordion > li.heading').hover(
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
},
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
}
);
});
</script>
Looks like this is happening because each accordion item has its own hover event that takes care of its own animation. You can refactor the code slightly to make this easier to understand and reuse:
var activeItem = jQuery("#accordion li:first");
jQuery('#accordion > li, #accordion > li.heading').hover(
function () { hoverMe(jQuery(this)); },
function () { unhoverMe(jQuery(this)); }
);
//This gets called when cursor hovers over any accordion item
var hoverMe = function(jQuerythis) {
//If the first item is still active
if (activeItem) {
contract(activeItem); //...Shrink it!
activeItem = false;
}
//Expand the accordion item
expand(jQuerythis);
};
//This gets called when cursor moves out of accordion item
var unhoverMe = function(jQuerythis) {
contract(jQuerythis);
};
//I have moved the hover animation out into a separate function, so we can call it on page load
var expand = function(jQuerythis) {
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
};
//I have moved the unhover animation out into a separate function, so we can contract the first active item from hoverMe()
var contract = function() {
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
};
//Now expand the first item
expand(activeItem);
I have put together a simplified version demonstrating the logic. Please let me know how you get on.

Close accordion alike div using jQuery?

I have this simple code which shows 3 items
When I press the header ($(".fileHeader")) , it should open then next element which is the next element (hidden div) ($(".LST_Documents"))
sketch :
JSBIN : it does work.
Most important :
When I press on a $(".fileHeader")- i need to close all other $(".LST_Documents") and then ( that why i used promise) open the relevant $(".LST_Documents").
The problem is (look at the pic) if i press again on the first $(".fileHeader").
what is happening is that it closing and then re opening. and I want it to stay CLOSED.
P.S.
I could solve it with class ( .rowOpen or something like that) but I want to do it via JS/JQ only.
How can I enhance my code to work as expected ?
Just hold the header's content visibility state before sliding it up. And slide down the content only when it was not visible.
Here is the fiddle.
$(".fileHeader").on('click', function () {
var content$ = $(this).next(),
isContentVisible = content$.is(':visible');
$(".LST_Documents:visible").slideUp().promise().done(function () {
if ( ! isContentVisible ) {
content$.slideDown();
}
});
});
How 'bout a simple condition:
$(".fileheader").on('click', function() {
var next = $(this).next();
if(next.is(':visible'))
{
next.slideUp();
}
else
{
$(".LST_Documents:visible").slideUp().promise().done(function() {
next.slideDown();
});
}
});

Categories

Resources