jQuery animate element and hide - javascript

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});

Related

Destroying InfiniteScroll on AJAX page change

We have a website that is running jQuery Infinite Scroll Plugin. The plugin is no longer maintained but it is the only one that really serves our purpose properly. However, the problem I have is that our site is ajax based. On page change the pg-changed trigger is fired against the window, which allows us to check if the Infinite Scroll container is there and enable Infinite Scroll. If the Infinite Scroll container isn't there but $.infscr exists, we will attempt to destroy the previous instance.
The problem I am having is that when changing to another page, it doesn't seem to be getting destroyed properly and sometimes AJAX calls will be made, along with the infscr loading bar displaying. Here is the code I am using to instantiate and destroy the plugin:
$(window).on('pg-changed', function () {
// delete our infinite scroll
if(typeof $.infscr !== 'undefined') {
$('.snap-inner, .infscr').data('infinitescroll', null);
$('.snap-inner, .infscr').infinitescroll('unbind');
$('.snap-inner, .infscr').infinitescroll('destroy');
$('#infscr-loading').remove();
$.infscr.data('infinitescroll', null);
$.infscr.infinitescroll('unbind');
$.infscr.infinitescroll('destroy');
delete $.infscr;
}
// setup our infinite scroll
if($('.infscr').length) {
$.infscr = $('.infscr').infinitescroll({
// define our navigation selectors
navSelector : 'div.infscr-navigation',
nextSelector : 'div.infscr-navigation A:first',
itemSelector : '.infscr-item',
// allow scrolling an overflowed element
behavior : 'local',
bufferPx : 120,
binder : $('.snap-inner'),
dataType : 'html',
loading : {
msg : null,
selector : '.snap-content',
img : 'data:image/gif;base64,TRIMMED',
msgText : '<span class="infscr-loading">Loading...</span>',
}
}, function (arrayOfNewElems) {
// render background images on our new elements
$(this).renderBgImages();
});
}
});
I really hope you can help with this as it has become quite a problem now, firing on scroll, making AJAX calls and displaying the loading bar.
I guess the problem might be that the plugin binds events on elements external to itself. It's hard to say without further debugging (is the plugin deleted or does it fail to do so?). If it does, why would it fail to unbind the events? etc.
When you say, 'on another page', I suppose you are not loading a whole new page (which would 'reset' the javascript.
Though, depending on wether this could work for you, you might want to try unbinding ALL events on the document, and starting fresh.
$(document).add('*').off();
should do so. If it does, you could try to pinpoint what elements/events you need to unbind manually. Let me know if this works or not.

Visual feedback for value update

I have contenteditable td elements in a table. I'm using bootstrap and table-hover (so the colour changes when you hover over a row. On blur of the td I do an ajax request that updates the value on the server and then I get a response that indicates success or the error. On success I want to indicate that the value has updated successfully. Right now I'm doing this:
var trueColor;
trueColor = $(element).css('backgroundColor');
$(element).animate({
backgroundColor: '#cce2ff'
}, {
duration: 100,
complete: function() {
$(element).delay(10).animate({
backgroundColor: trueColor
}, {
duration: 900
});
}
});
So I'm using the complete callback of the first animate to put the colour back to what I want it to be. The code works but the problem is obviously if you're hovering over the element when it sets trueColor. I've thought of using css animations but the main thing was that I didn't know how to get a "flash" kind of effect (I think maybe keyframes would help but I don't know how to use them let alone how browser compatible they are).
So the question is basically how do I achieve the effect? I don't mind whether it's css or javascript and I welcome superior suggestions if you think there's a better way to give this sort of visual feedback to a user.
Update
Thanks to #chiliNUT I am removing the class to get the colour but the problem was then that jquery's animate had styled the element and so hovering was broken on modified cells. So now I also removeAttr("style") to get rid of that once we're done
var trueColor;
$(el).closest('table').removeClass('table-hover');
trueColor = $(el).css('backgroundColor');
$(el).closest('table').addClass('table-hover');
$(el).animate({
backgroundColor: '#cce2ff'
}, {
duration: 100,
complete: function() {
$(el).delay(10).animate({
backgroundColor: trueColor
}, {
duration: 900,
complete: function() {
return $(el).removeAttr("style");
}
});
}
});
You can remove the hover effect while its doing the display update, then restore it once the display update finishes. You can get the table element containing the td with $(element).closest('table') and then remove the hover effect with removeClass('table-hover') and then put it back with addClass('table-hover'), so
var trueColor;
//remove hover
$(element).closest('table').removeClass('table-hover');
//get original color
trueColor = $(element).css('backgroundColor');
//restore hover
$(element).closest('table').addClass('table-hover');
//..rest of your original code

How to effectively unbind() hoverintent() from an element

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!

Switch between Superfish and FlexNav depending on window width

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

jQuery - bxSlider plugin reloadSlider issues

I'm using jQuery with the bxSlider plugin, here is the link to it just incase: http://bxslider.com/
I'm trying to reload the slider and my custom pager after I've removed certain slides from it.
Here is what I have tried:
$(function() {
var slider = $('#slider').bxSlider({
pagerCustom: '#bx-pager'
});
$('.list').on('click', '.delete', function() {
image = $(this).closest('li').find('[type="hidden"]');
// image.attr('id') contains a string: image-0, image-1, image-2, etc.
$('#slider, #bx-pager').find('.' + image.attr('id')).remove();
slider.reloadSlider({
pagerCustom: '#bx-pager'
}); // I have also tried: slider.reloadSlider();
});
});
It works partially. What happens is the slider gets reloaded just fine but it removes the pager completely when it runs the reload.
Thank you very much for any help.
As long as I see, this is a bug in bxSlider, in fact, when you call the reloadSlider method, internally are called the methods destroySlider and init.
In the destroySlider method the pagerEl element is destroyed, this is right if you are not using a custom one, because it is recreated programmatically in the init method, but if you are using a custom one it can't be recreated programmatically.
I ended up modifying the destroySlider method to check if a custom pager is used, in this case it must not be deleted.
Here is the before (line 1294):
if(slider.pagerEl) slider.pagerEl.remove();
And after:
if (slider.settings.pagerCustom === '') {
if(slider.pagerEl) slider.pagerEl.remove();
}
I'll post the bug on GitHub as soon as I have the time.

Categories

Resources