Submenu animation "pops up" - javascript

I have a menu with sub items inside it. In order to make animation effect I want, I need to retrieve sub-menu width,height and height of its first-child. Now my animation works ,but sometimes my sub-menu just "pops up" (it doesn't animate its width ).
Here is The Fiddle of the problem.
http://www.vasinternetposao.com/wordpressdevelopment/wp-content/themes/override/images/submenu_problem.png
I am using this code:
var j = jQuery.noConflict();
j(document).ready(function () {
j('ul.nav').removeClass('nav').addClass('jnav'); //Add jquery Class to our menu
j('ul.jnav li').hover(function () {
if (j(this).children('ul:first').hasClass('jsub-menu')) { //Let's check if "jsub-menu" Class is here
return false; //If it is ("jsub-menu" here) don't SlideDown...
} else { //Else slide down if no class
j(this).find('ul.sub-menu:first').not(':animated').slideDown(500);
}
}, function () {
j(this).find('ul:first').slideUp(500, function () {
j(this).removeClass('jsub-menu').addClass('sub-menu');
j(this).css({
'height': '',
'width': ''
});
});
});
j('ul.jnav ul.sub-menu a').hover(function () {
j(this).addClass('active');
if (j('.active').next('ul.sub-menu').length) { //If submenu exist...
j('.active').next('ul.sub-menu').css({
'visibility': 'hidden',
'opacity': '0',
'display': 'block'
}); //Show it so we can read its:
var get_width = j('.active').next('ul.sub-menu').outerWidth(true); //WIDTH
var get_height_of_first_child = j('.active').next('ul.sub-menu').children('li:first').outerHeight(true); //HEIGHT of its First Child
var get_submenu_height = j('.active').next('ul.sub-menu').outerHeight(true); //HEIGHT of our menu
j('.active').next('ul').removeClass('sub-menu') //Remove class from menu, add another class apply HEIGHT of FIRST CHILD and hide it again...
.addClass('jsub-menu').css({
'visibility': '',
'opacity': '',
'height': get_height_of_first_child + 'px',
'width': '0'
});
j('.active').next('.jsub-menu').animate({
width: get_width
}, 1000, function () { //Animate WIDTH
j('.active').next('.jsub-menu').animate({
height: get_submenu_height
}, 1000); //callback animate HEIGHT
});
} //End if
}, function () {
j('.active').removeClass('active');
});
});
I think that this is happening because my Slide Up/Down animations are conflicting with my animate with/height functions but I am not sure. I have tried to solve it by adding stop(),stop(true,true),stop(true,false) in numerous combinations but failed. I am trying to solve this for days now so you stackers are my only hope. Please help!
Thank you!!

I was finally able to replicate the error.
I wrote this code for you, to replace the code you have for the animation.
var animating = false;
function animate($elm, options, callback) {
if (animating)
return;
animating = true;
$elm.animate(options, 1000, function() {
animating = false;
if (callback != undefined)
callback();
});
}
Call it like this, from inside your hover callback.
animate(j('.active').next('.jsub-menu'),
{
'width': get_width,
'height' : get_submenu_height
});
Basically, it checks if another animation is already running, in which case it doesn't start it. The Flag is set to false when the animation stopped, and let's other animations go on.
You can also pass a callback to do something after the animation is completed, but in your case you don't need it, because you can animate the height and width in the same time.
I tested it for like a minute and it looked pretty smooth.
Here is the updated feedle: http://jsfiddle.net/gabrielcatalin/TNxJ4/1/
P.S. You may also want to use the $ sign instead of 'j' for jQuery wrappers.

Related

Back to top button not triggering properly

I have a back to top button that appears when you scroll a little bit .It's working fine but when scrolling if I get to the footer i want the button to go above the footer.
I used the jquery animate method to change the bottom css rule of the button when I get to the bottom of the page.But that effect doesn't happen instantly on my website because i have more javascript and i think it needs to go through all the code before it runs the effect and It's just not working properly.
Where is the problem ? .Here is what I have done : JSFIDDLE
var offset = 250;
var duration = 500;
$(window).scroll(function () {
if ($(this).scrollTop() > offset) {
$('.back-to-top').fadeIn(duration);
} else {
$('.back-to-top').fadeOut(duration);
}
});
$('.back-to-top').on('click', function () {
event.preventDefault();
$('html,body').animate({ scrollTop: 0 }, duration);
return false;
});
$(window).scroll(function () {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
$('.back-to-top').animate({ 'bottom': '400px' });
} else $('.back-to-top').animate({ 'bottom': '10%' });
});
It seems like adding a class that changes the position of the div, and toggling it when the condition is true solved the problem .

Execute function only if window width is greater than something else don't

function dropdownHover() {
jQuery('ul.nav li.dropdown').hover(function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}, function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
});
}
$(window).on('resize', function(event){
var windowSize = $(window).width();
if(windowSize > 992){
dropdownHover();
}
});
I need this function dropdownHover() to fire only when window is greater than 992px, both on load and on resize, else if window is < 992px, both on load or on resize i dont want to fire this function on hover i want regular bootstrap dropdown on click. I tried to do this with css but i cant add animation on dropdown because its just display: none/block. I also tried to add class on resize to fire this function if element has that class else dont but it doesnt work either.
Edit: Final working version
$('.dropdown').on('mouseenter', function(){
if(!$(this).is('.open') && $(window).data('wide'))
$('.dropdown-menu', this).dropdown('toggle').hide()
.stop(true, true)
.delay(200)
.fadeIn(function(){
this.style.display = '';
}).find('a').on('touchstart click', function (e) {
e.stopPropagation();
});
}).on('mouseleave', function(){
if($(this).is('.open') && $(window).data('wide'))
$('.dropdown-menu', this).dropdown('toggle');
});
$('.dropdown').on('click', function(e){
if( $(window).data('wide')) {
$('.dropdown-menu', this).dropdown('toggle');
} else {
$('.dropdown-menu', this)
.stop(true, true).slideToggle()
.closest('.dropdown').removeClass('open');
}
});
// not entirely necessary. Not sure which is faster: this or just checking the width in all three places.
$(window).on('resize', function(){
$(window).data('wide', $(window).width() > 992);
// reset the open menues
$('.dropdown').removeClass('open');
$('.dropdown-menu').css({
display: '',
left: '',
position: '',
});
// because we are checking the width of the window, this should probably go here although this really should be a media query style
$('.dropdown-menu.pull-center').each(function() {
var menuW = $(this).outerWidth();
if ($(window).width() > 1000) {
$(this).css({
left: - menuW / 2 + 60 + 'px',
position: 'absolute'
});
} else {
$(this).css({
left: '',
position: ''
});
}
});
}).trigger('resize');
Initial Solution
Your question is twofold. First, you need it to not show the menu at smaller sizes. For that, you check on resize what the window width is. The problem is that it only works once. It triggers the event listeners for hover and it doesn't kill those event listeners if the screen is then larger at some point. For that, you can set a flag. There are a lot of ways to do this, but for my answer, I've chosen to use jQuery .data() method.
$(window).on('resize', function(event){
var windowSizeWide = $(window).width() > 600; // reduced for testing purposes
jQuery('ul.nav li.dropdown').data('dropdown-enabled', windowSizeWide);
}).trigger('resize');
Then when we listen for the hover events (which are mouseenter and mouseleave events), we simply return out of the function if the screen is too small.
jQuery('ul.nav li.dropdown').on('mouseenter', function() {
if(!jQuery(this).data('dropdown-enabled')) return;
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}).on('mouseleave', function() {
if(!jQuery(this).data('dropdown-enabled')) return;
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
}).find('.dropdown-menu').hide();
Finally, you also want the event to trigger on load. You can do that by simply adding .trigger('resize') as seen in the first snippet. You can see a functioning demo here: http://jsfiddle.net/jmarikle/xw9Ljshu/
Possible Alternative Solution
Alternatively, you can also use CSS to handle this with media queries. The simplest way to do this is to force display: none on smaller screens. I don't recommend completely hiding the element because it becomes inaccessible at that point, but this is the general idea:
#media(max-width: 600px) {
ul.dropdown-menu {
display:none !important;
}
}
Note that !important is used because jQuery adds inline styles when you fadeIn or fadeOut.
Second demo: http://jsfiddle.net/jmarikle/xw9Ljshu/1
window.screen.availWidth to get the window size. i am yet not tested your code.But i think this will ok.
function dropdownHover() {
jQuery('ul.nav li.dropdown').hover(function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}, function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
});
}
$(document).ready(function(){
$(window).on('resize', function(event){
var windowSize = window.screen.availWidth;
if(windowSize > 992){
dropdownHover();
}
});
})

coming back from a fadeOut in Jquery

In the following code, div's are faded out using the new fadeOut functionality of Jquery. Unfortunately, this changes the style of the tag to "display:none". And I can not figure out how to change it back, so that I can Fade it in.
I do not want to use the opacity style, or fadeTo, because I need the divs to be completely hidden, pulling all other floating divs up with it.
/* Hide interface, fade logo */
$('.toggle-ui').on({
'click': function (e) {
e.preventDefault();
var divToHide = ['#slideshow-nav', '#nav', '#content'];
$.each(divToHide, function(intValue, currentElement) {
// check alpha state and switch
var currVis = $(currentElement).css('visibility');
$(currentElement).css('visibility', currVis == 'visible'
? $(currentElement).fadeOut('slow')
: $(currentElement).fadeIn('slow'));
});
}
});
checking for the style "display" and using that for the conditional works.
As follows:
$('.toggle-ui').on({
'click': function (e) {
e.preventDefault();
var divToHide = ['#slideshow-nav', '#nav', '#content'];
$.each(divToHide, function(intValue, currentElement) {
// check alpha state and switch
var currDis = $(currentElement).css('display');
$(currentElement).css('visibility', currDis == 'none'
? $(currentElement).fadeIn('slow')
: $(currentElement).fadeOut('slow'));
});
}
});

jquery did my first plugin - Issue with fadeIn/fadeOut when it goes too fast

I made my own little tooltip, but there seems to be an issue when I go too fast.
It's a simple tooltip, ie you mouseenter and it shows you a bubble that fadeIn on top with some text, mouseleave the bubble fadeOut.
The issue appears when go very fast mouseenter/mouseleave on several different elements ... first it starts to only fade to like 0.6 and then eventually it doesn't show.
I'm guessing it s something to do with queuing. So I tried stop() and clearqueue() but it still doesn't work.
Here is the plugin:
(function ($) {
$.fn.tooltip = function (options) {
var selector = $(this).selector;
var defaults = {
fadeInSpeed:350,
fadeOutSpeed:200,
delayIn: 350,
delayOut: 300,
popupId:'tooltip_popup',
verticalOffset: 30
};
var settings = $.extend({}, defaults, options);
$(document).delegate(selector, 'mouseenter', function (e) {
var $this = $(this);
var title = $this.attr('title');
$this.attr('title', '');
if (title !== '') {
if ($('#' + settings.popupId).text() === '') {
$('<div />').attr('id', settings.popupId)
.appendTo('body')
.css({
position:'absolute',
backgroudColor: 'black',
zIndex: 5
})
.addClass('tooltip_style')
.hide();
$('<div />').appendTo('#' + settings.popupId).addClass('tooltip_arrow');
$('<span />').appendTo('#' + settings.popupId);
} else {
$('#' + settings.popupId).hide().stop().clearQueue().hide();
}
var ele_x = $this.offset().left;
var ele_y = $this.offset().top;
$('#' + settings.popupId+' span').text(title);
$('#' + settings.popupId)
.css({
top : ele_y - settings.verticalOffset,
left: ele_x
})
.delay(settings.delayIn)
.fadeIn(settings.fadeInSpeed);
}
});
$(document).delegate(selector, 'mouseleave', function (e) {
var $this = $(this);
$('#' + settings.popupId)
.delay(settings.delayOut)
.fadeOut(settings.fadeOutSpeed);
$this.attr('title', $('#' + settings.popupId+' span').text() );
});
return this;
}
})(jQuery);
As you'll probably notice, I think this line is the one I need fixing:
$('#' + settings.popupId).hide().stop().clearQueue().hide();
(it s a bit messy cause I was trying everything...ha).
I don't think I quite understand this queueing thing.
Here is a fiddle: http://jsfiddle.net/denislexic/7YMcu/2/
Thanks for any help.
try using stop(true, true) instead of stop().
From the jQuery docs:
.stop( [clearQueue] [, jumpToEnd] )
clearQueue Boolean indicating whether to remove queued animation as well. Defaults to false.
jumpToEnd Boolean indicating whether to complete the current animation immediately. Defaults to false.
With clearQueue set to true, you can also take out the clearQueue() call.
Explanation
The reason you're having issues is that when you stop an animation, you leave the element in a half-transitioned state, which the next animation considers to be the "normal" state. If you tell a half-visible element to fadeOut, it's going to save the 50% visible state for when it is told to fadeIn later. If you tell stop to jumpToEnd, it will always finish at 0% or 100% visibility.
As for clearQueue, I'm not confident that it needs to be true. jumpToEnd is the important thing here. You can play with the clearQueue value and see what works best in your case.

Implementing Hover Intent

I just finished developing this Wordpress theme:
http://www.minnesdiner.com/
Everything is working well, but I'm not 100% happy with the navigation.
The sliding position indicator works smoothly, but I'd like to integrate the hover intent jQuery plugin to prevent the sliding indicator from sliding when the user unintentionally passes over the nav.
Any ideas as to how I could integrate this plugin? I'm currently firing a separate jQuery function for each nav item and passing coordinates to the sliding indicator based on which item is being hovered upon.
Here's my current jQuery code:
$(document).ready(function() {
var $currentpos = $("#menu-indicator").css("left");
$("#menu-indicator").data('storedpos', $currentpos);
$(".current-menu-item").mouseenter(function () {
$("#menu-indicator").stop().animate({left: $currentpos}, 150);
});
$(".menu-item-26").delay(500).mouseenter(function () {
$("#menu-indicator").stop().animate({left: "52px"}, 150);
});
$(".menu-item-121").mouseenter(function () {
$("#menu-indicator").stop().animate({left: "180px"}, 150);
});
$(".menu-item-29").mouseenter(function () {
$("#menu-indicator").stop().animate({left: "310px"}, 150);
});
$(".menu-item-55").mouseenter(function () {
$("#menu-indicator").stop().animate({left: "440px"}, 150);
});
$(".menu-item-27").mouseenter(function () {
$("#menu-indicator").stop().animate({left: "570px"}, 150);
});
$(".menu-item-164").mouseenter(function () {
$("#menu-indicator").stop().animate({left: "760px"}, 150);
});
$delayamt = 400;
$("#header-row2").click(function () {
$delayamt = 5000;
});
$("#header-row2").mouseleave(function () {
$("#menu-indicator").stop().delay($delayamt).animate({left: $currentpos}, 600);
});
});
As you can see, I need to bind mousover and mouseout to separate elements (list-item and containing div).
Thanks!
If all you want to do is avoid the user triggering the slide by mousing over the nav, I would just setTimeout in your hover function to call your sliding code after a certain amount of time has passed, and clear the timeout on the mouseout event. No extra plugin needed.
For example:
var hover_timer;
$('.menu-item').hover(
function() {
hover_timer = setTimeout(function() {
...
}, 500);
},
function() { clearTimeout(hover_timer); }
);
EDIT: by the by, you should be combining all those hover functions into one. You can do something like:
$('.menu-item-26').data('slider-pos', '52px');
$('.menu-item-121').data('slider-pos', '180px');
...
And then in the code to slide, call it back:
$this = $(this);
$('#menu-indicator').stop().animate({left: $this.data('slider-pos')}, 150);
And that's just a start - you can generalize it even more, I bet.

Categories

Resources