I'm using the HoverIntent plugin to create hover drop-downs in my Bootstrap 3 navigation. However, for smaller sizes I want to still use the native click trigger to activate the dropdowns and not the hover. I'm trying to use enquire.js, which allows me to call a function when the screen size enters a specified width, and another when it leaves that width. This is my code so far:
enquire.register("screen and (min-width: 767px)", {
match : function() {
hoverIntentInit();
},
unmatch : function() {
removeHoverIntent();
}
});
function removeHoverIntent(){
// remove the hoverintent() function <-- this is what I need
}
function hoverIntentInit(){
var config = {
timeout: 900,
over: showMenu,
out: hideMenu};
$('.dropdown').hoverIntent(config);
}
function showMenu(){
// code that shows the dropdown (not important)
}
function hideMenu() {
// code that hides the dropdown (not important)
}
I found a similar question here from few years ago. However, the answers over there either don't work or they they remove both the hoverIntent and the click events from the elements which is not what I want (I want the native Bootstrap click event to remain).
Please help me with this, I've spent more than a day on this and still I can't find a solution.
Thanks!
Related
hey guys i am very new to js and jquery in genenral and i was just going throught the plugin code of a gallery plugin , i can across the function called _loadevents , that had the following content , see below :
this.$navPrev.on('click.gallery', function (event) {
});
this.$navNext.on('click.gallery', function (event) {
});
this.$wrapper.on('webkitTransitionEnd.gallery transitionend.gallery OTransitionEnd.gallery', function (event) {
});
now $navPrev , $navNext , and $wrapper are obviously some HTML element , now my question is about another method i came across in the same plugin , look below :
destroy: function () {
// console.log('inside destroy');
this.$navPrev.off('.gallery');
this.$navNext.off('.gallery');
this.$wrapper.off('.gallery');
}
now i see that if this function is called all the event handlers will be taken off. now , can somebody tell me what is the necessacity of such a function , does it improve a plugins efficiency ? how or when does such a function get used and is it a common practice to write e destroy function for events in plugins ?
Thank you.
Alex-z .
Destroy functions in plugins enable a developer to reset or remove a plugin from an element, restoring the element to before the plugin was initialised. This is useful if, for example, you have a gallery plugin that works and looks fantastic on desktop, but you don't want it on mobile. You can listen to resize event on window and if the window size is smaller than e.g. 710px then destroy the plugin. This will remove all the added events, undo any DOM manipulation, and restore the html elements back to how they were before the plugin was first initialised (turn-wise, if the window size is larger than 710px then initialise the plugin).
They are generally considered good practice.
I am trying to use 2 jQuery navigation scripts on one page (Superfish for desktops and FlexNav for mobile). I am currently using matchMedia along with the polyfill by Paul Irish to respond to CSS3 media query state changes within JavaScript.
The current code is only accomplishing 50% of the overall goal. If you access the web page initially with a window size equal to or greater than 999px wide then you get Superfish and if you initially access the web page with a window size less than 999px then you get FlexNav. The problem occurs when you resize the window above or below 999px as both scripts become active.
// media query event handler
if (matchMedia) {
var mq = window.matchMedia("(min-width: 999px)");
mq.addListener(WidthChange);
WidthChange(mq);
}
// media query change
function WidthChange(mq) {
if (mq.matches) {
$("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
$("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
As much as I would like to get this working with matchMedia, I am open to all suggestions.
Update: Thanks to Stephan's suggestion I now have the following code:
jQuery(document).ready(function () {
// add destroy function for FlexNav
flexNavDestroy = function () {
$('.touch-button').off('touchstart click').remove();
$('.item-with-ul *').off('focus');
}
// media query event handler
if (matchMedia) {
var mq = window.matchMedia("(min-width: 999px)");
mq.addListener(WidthChange);
WidthChange(mq);
}
// media query change
function WidthChange(mq) {
if (mq.matches) {
if (typeof (flexNav) != "undefined") {
flexNavDestroy();
}
superfish = $("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
if (typeof (superfish) != "undefined") {
superfish.superfish('destroy');
}
flexNav = $("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
});
Remaining Issue:
The destroy function for FlexNav is only partially destroying it.
The best way would probably be to destroy the other plugin when you're activating one.
If I look in the source of Superfish there is a destroy function which does this, but flexNav doesn't have such a function. You can create one though:
flexNavDestroy = function(){
$('.touch-button').off('touchstart click').remove();
$(('.item-with-ul *').off('focus');
}
Then you could do this:
function WidthChange(mq) {
if (mq.matches) {
if(typeof(flexNav) != "undefined") {
flexNavDestroy();
}
superfish = $("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
if(typeof(superfish) != "undefined") {
superfish.superfish('destroy');
}
flexNav = $("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
UPDATE
I've looked a little bit more into FlexNav, and there's a few things I missed.
I think the styles are colliding because FlexNav sets a lot of styles by default. We can easily prevent that by using two classes: One for flexnav styling (the default .flexnav) that we can remove to hide all it's styles, and one for binding the javascript function (that will always stay there, or we can't re-attach it).
I generally like to prepend any classes that are meant as JS hooks with js-, so in my example (below) I replaces the .flexnav class on the menu with .js-flexnav. Then to activate flexnav you have to add this line just before you call $('ul.flexnav').flexNav()
$('.js-flexnav').addClass('flexnav');
In the destroy function you will have to remove the class again, which I will show shortly.
In addition, I'm not sure how Superfish does the showing and hiding, but since FlexNav collapses all submenus, it's also safe to say you should re-show them so that Superfish can do it's own thing.
The updated destroy function to reflect this:
function flexNavDestroy(){
$('.touch-button').off('touchstart click').remove();
$('.item-with-ul *').off('focus');
$('.js-flexnav').removeClass('flexnav').find('ul').show(); // removes .flexnav for styling, then shows all children ul's
}
Here's a jsFiddle that shows activating/deactivating flexNav with the new code: http://jsfiddle.net/9HndJ/
Let me know if this does the trick for you!
here is an alternative path :
once page is loaded :
cache the menu in a jquery object, clone it & instantiate both plugin one on each clone
$menucontainer= $("#menu_container");
$memufish = $menucontainer.find(".menu");
$menuflex=$menufish.clone();
$menufish.superfish().detach();
$menuflex.prependTo($menucontainer).flexnav().detach();
(they are loaded anyway so it's no big deal even if most of the time one won't be needed, it will be there & ready just in case - however test if you can instantiate on the clone without appending it to the DOM)
depending on width append / prepend the required one
$menuflex.prependTo($menucontainer);
on change width detach one reattach the other
$menufish.detach();
$menuflex.prependTo($menucontainer);
you could also work your way checking if plugin was instantiated on a width change (in order to not instantiate uselessly onload) but in any way I believe the use of clone() and detach() are very much adapted to solve easily your problem. The destroy way seems to be a hassle, lots of work (for the script as well when some user is raving with window resize) loss of time & a risk of many bugs to me ( expect more and more lag at every destroy re instantiate - with detach() no worries)
cons : will use a bit more memory overhaul
pros :
script will work less & it will be real fast to switch from one to the other
you could make a plugin from this and add other menu plugin to your app very easily without worry about conflict and how to destroy
I am using a BJQS slider on my website.
I am also using fancybox on the same website.
I would like BJQS to pause when the fancybox is open and resume when closed.
Does anyone know how I could create a pause/play toggle button for BJQS?
Thanks
fancybox comes with some callbacks, so you should be able to do something like:
Adopting Lee and Edwards idea about virtual hovering..
$(".fancybox").fancybox({
padding : 0,
openEffect : 'elastic',
closeEffect: 'elastic',
beforeLoad: function(){
$(".banner").trigger("mouseover");
},
afterClose: function(){
$(".banner").trigger("mouseout");
}
});
Without editing the source file to provide either a method to pause the slider, or add in a button you can hide and trigger a click on, the quickest method is to trigger the mouse events that cause the slider to pause.
Looking at the demo, you can see that when you mouseover the slider, the slider stops animating until you move your mouse outside of it. Therefore you can simulate these events.
Assuming your slider div is #slider like the demo on the BJQS site, you would do:
On fancybox open
$('#slider').trigger('mouseover');
On fancybox close
$('#slider').trigger('mouseout');
Go here: http://fancybox.net/api to see how to define open/close callbacks (see near bottom of first table, the "on" options)
I check the plugin but I cant' find any method to pause/play the slider.
I see an option called:
hoverpause : true, // enable/disable pause slides on hover
So we can "hack" in this way using it by triggering the over state on the slider itself:
var stopbjqs = false;
$(function () {
$('#dialog').bjqs({
'showmarkers': false,
'responsive': true,
'automatic': true
});
$("#btn").click(function () {
if (!stopbjqs) {
$("#dialog").trigger("mouseover");
stopbjqs=true;
} else {
$("#dialog").trigger("mouseout");
stopbjqs=false;
}
});
});
But it will be definitely better to have some methods to manipulate the slider.
Demo: http://jsfiddle.net/IrvinDominin/P8UgQ/
Came across this whilst trying add a play/pause button to the plugin. #Irvin Dominin's suggestion relating to hoverpause is good but it will fail as soon as you hover the banners as the mouseover/mouseout is triggered.
I decided to extend the plugin with a new setting and turn off hoverpause.
First add the setting to the defaults object e.g.
// slider default settings
var defaults = {
enableplaypause: false // shows play/pause button
};
Next you'll want to set the click binding to your button, this is done in the init() function e.g.
// run through options and initialise settings
var init = function () {
// configurations only avaliable if more than 1 slide
if (state.slidecount > 1) {
//enable play/pause button using setting we defined earlier
if (settings.enableplaypause) {
conf_enableplaypause();
}
}
};
Now for the conf_enableplaypause(); function which handles the state + button bindings:
var conf_enableplaypause = function () {
$('#btn').click(function () {
if (!state.paused) {
clearInterval(state.interval);
state.paused = true;
$('#btn').text('PAUSED');
} else {
state.interval = setInterval(function () {
go(vars.fwd, false);
}, settings.animspeed);
state.paused = false;
$('#btn').text('PLAYING');
}
});
};
Pretty straightforward and is essentially a copy of what hoverpause does except on a button click along with updating the button text.
Hopefully this helps someone
I'm building Windows 8 app in JavaScript. What I'm trying to do is to slide the html element out of the screen and then change its "display" property to "none":
var panelContainer = $('#panelContainer');
panelContainer.animate({ right: '-400px' }, 200, function () {
panelContainer.hide();
});
But this code doesn't work correctly: it just immediately hides the element without animation.
I've also tried:
var panelContainer = $('#panelContainer');
panelContainer.animate({ right: '-400px' }, 200, function () {
panelContainer.hide(200);
});
and it works, but it's a hack: I don't want to change the opacity when animating and I don't need to have additional timeout for hiding.
I've found that jQuery UI library has extended show and hide methods that do that, but I would like not to reference this library just for one call. I'm aware that there is a WinJS.UI.Flyout that performs similar operation, but it's not applicable in my case. Any ideas how this can be done?
The problem was that jQuery does not put hide animation into its animation queue by default. That's why my initial code was hiding the html element first and then animating it. The solution for that is to call hide with the parameter that explicitly specifies that hide call should be queued:
panelContainer.hide({queue: true});
I'm working on writing a drop-down menu with jQuery and I have a question. My menu is composed of two part span.menu_head (which is in the menu bar) and a ul.menu_body (which contains the drop-down items). I have the following jQuery code:
$("span.menu_head").hover(function(){
$(this).next().slideDown('medium');
}, function(){
});
$("ul.menu_body").hover(function(){
}, function(){
$(this).slideUp('medium');
});
So, when I hover over the span.menu_head, the ul.menu_body slides down and when I leave the ul.menu_body, it slide up. This is working as expected. However, there's one more piece I'm trying to add: When the mouse leaves the span.menu_head, I want the ul.menu_body to slideUp, UNLESS the mouse is over the ul.menu_body. Is there a way in jQuery to determine if the mouse is over a certain element? Or, is there a better way to acheive this effect?
Thanks,
Paul
I found a solution using hoverIntent (http://cherne.net/brian/resources/jquery.hoverIntent.html).
I set a flag when the mouse hovers over the ul.menu_body, so that I can check this flag before closing the menu.
var overBody = false;
function mnuOpen() {
$(this).next().slideDown('medium');
}
function mnuClose() {
if (!overBody) {
$(this).next().slideUp('medium');
}
}
var headConfig = {
over: mnuOpen,
timeout: 250,
out: mnuClose
}
$("span.menu_head").hoverIntent(config);
$("ul.menu_body").hover(function(){
overBody = true;
}, function(){
overBody = false;
$(this).slideUp('medium');
});
This is producing the desired behavior. There's one more thing I need to consider: if the mouse goes from the sub menu back to the header, we don't want to close and re-open, so this might involve setting another flag to detect this behavior.
Paul