jQuery Submenu delay on mouseOut - javascript

I have been working on adding a delay to a submenu I inherited for a major project. After about 10 hours of trying and failing, I could really use some help. I know that there are literally hundreds of examples out there for adding a delay on a submenu using jQuery or even Javascript, but I have not seen one that works in this modular code structure, and I have searched everywhere for a similar example.
I want to try and do this without a major module re-write, as this is a responsive website and I am unsure if changing the way the module works will have a negative impact on functionality.
Here is the code below, which is just one function in a series of UI functions:
MLS.ui = { ...
tabs: function (element, hover) {
var scope = element,
$contentTabs = $jQ(scope + ' > .tab-content > .tab'),
activeClass = 'active';
$jQ(scope + ' > .tab-menu > .tab').each(function (i, el) {
$jQ(this).add($contentTabs[i]).attr('tab', i + 1);
});
$jQ(scope + ' > .tab-menu > .tab').on((hover ? 'mouseenter' : 'click'), function (e) {
if (!hover) {
e.preventDefault();
}
var tab = $jQ(this).attr('tab');
$jQ(scope + ' > .tab-menu > .tab').add(scope + ' > .tab-content > .tab').removeClass(activeClass);
$jQ(this).add(scope + ' > .tab-content > .tab[tab=' + tab + ']').addClass(activeClass);
});
$jQ(scope + ' > .tab-menu > .tab:first-child').add(scope + ' > .tab-content > .tab:first-child').addClass(activeClass);
},
...}
As far as I can tell, the .on event handler is both adding the class 'active', and removing it from the element no longer hovered over, but I do not understand how that is possible.
A few things I have tried are adding a setTimeout around the entire tabs function, setting up a delay and queue on the .on event handler, and even writing a separate module, none of which had any effect (or the incorrect effect, such as firing the removeClass event even if I was still hovered on the element.)
What am I missing here? I cannot afford to waste any more time on this problem.
EDIT:
Here is an example of something I tried, but it just adds the class to every matching DOM element on hover of one li. How can I just target the single hovered element to have the delay to remove the active class?
tabs: function (element, hover) {
var scope = element,
$contentTabs = $jQ(scope + ' > .tab-content > .tab'),
activeClass = 'active';
$jQ(scope + ' > .tab-menu > .tab').each(function (i, el) {
$jQ(this).add($contentTabs[i]).attr('tab', i + 1);
});
$jQ(scope + ' > .tab-menu > .tab').on((hover ? 'mouseenter' : 'click'), function (e) {
if (!hover) {
e.preventDefault();
}
var tab = $jQ(this).attr('tab');
$jQ(this).add(scope + ' > .tab-content > .tab[tab=' + tab + ']').delay(900).queue(function () {
$jQ(scope + ' > .tab-menu > .tab').addClass(activeClass);
$jQ(this).unqueue();
});
});
$jQ(scope + ' > .tab-menu > .tab').on('mouseleave', function (e) {
if (!hover) {
e.preventDefault();
}
var tab = $jQ(this);
$jQ(scope + ' > .tab-menu > .tab').add(scope + ' > .tab-content > .tab').delay(1000).queue(function () {
$jQ(scope + ' > .tab-menu > .tab').removeClass(activeClass);
$jQ(this).unqueue();
});
});
$jQ(scope + ' > .tab-menu > .tab:first-child').add(scope + ' > .tab-content > .tab:first-child').addClass(activeClass);
},

Related

Conflicting scripts, what's the issue?

I use 2 different scripts on a webpage - one pure JS, the other jQuery.
The JS script is used for toggling the hamburger menu in mobile view. hamb.onclick shows the menu when clicking on hamburger, menuL.onclick hides the menu when clicking on a menu item. The latter refuses to work when the jQuery script is active (it works when the jQuery script is commented out).
<script>
(function () {
function hasClass(elem, className) {
return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
}
function toggleClass(elem, className) {
var newClass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' ';
if (hasClass(elem, className)) {
while (newClass.indexOf(' ' + className + ' ') >= 0) {
newClass = newClass.replace(' ' + className + ' ', ' ');
}
elem.className = newClass.replace(/^\s+|\s+$/g, '');
} else {
elem.className += ' ' + className;
}
}
var hamb = document.querySelector('.hamburger');
var menuL = document.querySelector('.menuList');
hamb.onclick = function () {
toggleClass(this, 'hamburgerOpen');
toggleClass(menuL, 'menuActive');
};
menuL.onclick = function () {
toggleClass(hamb, 'hamburgerOpen');
toggleClass(menuL, 'menuActive');
};
})();
</script>
The jQuery is used for smooth navigation/scrolling to the anchors of the page:
<script>
function scrollNav() {
$('.menu a').click(function(){
//Toggle Class
$(".active").removeClass("active");
$(this).closest('li').addClass("active");
var theClass = $(this).attr("class");
$('.'+theClass).parent('li').addClass('active');
//Animate
$('html, body').stop().animate({
scrollTop: $( $(this).attr('href') ).offset().top - 160
}, 800);
return false;
});
$('.scrollTop a').scrollTop();
}
scrollNav();
</script>
As i understand it, the jQuery script highjacks the onclick event of the JS script, because they work on the same parent element: ".menu a" and "menuList" (which is the "ul" inside ".menu").
What can i do to get both scripts working together? I'm a beginner and my JavaScript skills are still weak.
If the second script hijacks the event, just move the function into the second script and run it there.
It works!

Masonary reload item not working

I am trying to do gallery portfolio with help of masonry and custom click event. I can hide show images with the click event, but when hide show event complete masonry not reset to again.
link
Here is jQuery code:
var pmo = "#p-masonry_outer";
var ppt = "#p-portfolio_typs";
$('' + ppt + ' li span').on('click', function() {
$('' + ppt + ' li span').removeClass('p-active_work');
$(this).addClass('p-active_work');
var WorkRel = $(this).data("cat");
if (WorkRel != "0") {
$("" + pmo + " > div").hide();
$(".p-masonry a").attr('data-lightbox', '');
$("" + pmo + " > div").each(function() {
var WorkAttrStrip = $(this).data('cat');
var WorkAttr = WorkAttrStrip.split(",");
if ($.inArray(WorkRel + "", WorkAttr) !== -1) {
$(this).show;
}
});
var $container = $('.js-masonry');
$container.imagesLoaded(function() {
$container.masonry();
});
} else {
$("" + pmo + " > div").fadeIn("slow");
}
return false;
}

How to show two div when user scroll down using jquery?

Can anyone please tell me how to show two divs when the user scroll down using jquery.I want to push values on the array .Is it possible to add a div on the bottom.As we add when user scroll to top ?
http://jsfiddle.net/Gbd3z/2/
$("#fullContainer").scroll(function () {
// top
if ($(this).scrollTop() === 0 && pages.length) {
console.log("up"+pages);
var stringLoad = "page_" + pages.length;
$("<div id='" + stringLoad + "'>" + pages.pop() + "</div>").prependTo($("#fullContainer"));
$('#fullContainer').children().slice(3).remove()
}
if ($(this).scrollTop() >= $(this)[0].scrollHeight - document.body.offsetHeight) {
console.log("down");
}
});
I'm not entirely sure what you're asking for, but does this do it?
http://jsfiddle.net/luken/R24TH/
var pages = [page_1, page_2, page_3, page_4,page_5];
var lastLoadedPageIndex = -1;
var $container = $('#fullContainer');
addNextPageToContainer();
function addNextPageToContainer () {
lastLoadedPageIndex++;
if (lastLoadedPageIndex < pages.length) {
$( '<div id="page_'
+ lastLoadedPageIndex
+ '" class="page">'
+ pages[lastLoadedPageIndex]
+ '</div>'
).slideDown().appendTo( $container );
}
}
$container.scroll(function(){
if ($container.scrollTop() === 0 && pages.length) {
console.log("up");
} else if ($container.scrollTop() >= $container[0].scrollHeight - document.body.offsetHeight) {
console.log("down");
addNextPageToContainer();
}
});

How can add fade In() effect to each appearance of a div when the mouse click?

How can add fadeIn() effect to each appearance of a div when the mouse click?
So when onmouseclick, yellow part is showing with fadeIn() effect.
Fiddle : http://jsfiddle.net/2ehdW/7/
Script :
window.jQueryclick: function (event) {
if (!event.point.selected) {
$('#testDiv').show();
var chart_data = '<div> Name: ' + event.point.name + ' Share: ' + event.point.y + '</div>';
$('#testDiv').html(chart_data);
} else {
$('#testDiv').hide();
}
}
Add a parameter to .show() method and hide the div before showing it again:
if (!event.point.selected) {
$('#testDiv').hide();
$('#testDiv').show("300"); //or just $('#testDiv').fadeIn();
var chart_data = 'Name: ' + event.point.name + ' Share: ' + event.point.y;
$('#testDiv').html(chart_data);
} else {
$('#testDiv').hide();
}
FIDDLE

Making a button unclickable until jQuery .load() has finished

I'm currently using the following code for my read more buttons:
$(document).ready(function() {
$("a.more-link").attr("href", "");
$("a.more-link").attr("onclick", "return false;");
$('.readmore').each(function(index) {
var $link = $(this);
$(".readmore").prependTo('.' + $link.attr("id") + ' > #bottominfo');
});
$('.readmore').click(function() {
var $link = $(this);
if ( $link.attr("alt") == "read more" ) {
$('.' + $link.attr("id") + ' > #maincontent').load($link.attr("title") + ' #mainarticle', function(index) {
$('.' + $link.attr("id") + ' > #maincontent').hide();
$('.' + $link.attr("id") + ' > #maincontent').slideToggle('slow');
});
$('.' + $link.attr("id") + ' > #maincontent').attr("class", $link.attr("id"));
$link.attr('alt','read less');
} else {
$('#'+ $link.attr("id") + ' > .' + $link.attr("id")).hide();
$link.attr('alt','read more');
}
return false;
});
});
The problem I'm having is that if the user double clicks (or more) on the button it calls the function multiple times.
How do I make the button non-clickable until the .load() have finished?
The easiest would be to add a loading class to the link. I have also just done a quick clean up on your code. I didn't look into how it works but i'm sure if you spend a bit more time you can make it a lot more efficient
$(document).ready(function() {
$("a.more-link").attr("href", "")
.attr("onclick", "return false;");
$('.readmore').each(function(index) {
var $link = $(this);
//wouldn't this call all the elements with "readmore" class????
$(".readmore").prependTo('.' + $link.attr("id") + ' > #bottominfo');
});
$('.readmore').click(function() {
var $link = $(this);
//check if it has already been clicked
if($link.hasClass("loading")) return false;
//add the loading class
$link.addClass("loading");
if ( $link.attr("alt") == "read more" ) {
$('.' + $link.attr("id") + ' > #maincontent').load($link.attr("title") + ' #mainarticle', function(index) {
$('.' + $link.attr("id") + ' > #maincontent').hide()
.slideToggle('slow');
//it's done now and we can remove the loading class so we can click it again
$link.removeClass("loading");
}).attr("class", $link.attr("id"));
$link.attr('alt','read less');
} else {
$('#'+ $link.attr("id") + ' > .' + $link.attr("id")).hide();
$link.attr('alt','read more');
//add it here as well
$link.removeClass("loading");
}
return false;
});
});
Tips: I notice you call the same selectors multiple times. Always check the api doc and see what the methods you are calling do return. Most of them do return the element back, so then you can call the next method without $()
Example: $("div").hide().slideToggle('slow');
Could you not just run the function on .load instead?
$(document).load(function() {
$("a.more-link").attr("href", "");
$("a.more-link").attr("onclick", "return false;");
$('.readmore').each(function(index) {
var $link = $(this);
$(".readmore").prependTo('.' + $link.attr("id") + ' > #bottominfo');
});
$('.readmore').click(function() {
var $link = $(this);
if ( $link.attr("alt") == "read more" ) {
$('.' + $link.attr("id") + ' > #maincontent').load($link.attr("title") + ' #mainarticle', function(index) {
$('.' + $link.attr("id") + ' > #maincontent').hide();
$('.' + $link.attr("id") + ' > #maincontent').slideToggle('slow');
});
$('.' + $link.attr("id") + ' > #maincontent').attr("class", $link.attr("id"));
$link.attr('alt','read less');
} else {
$('#'+ $link.attr("id") + ' > .' + $link.attr("id")).hide();
$link.attr('alt','read more');
}
return false;
});
});

Categories