EDIT: This software package is the full and undoctored version of what I'm trying to fix here. The problem is in the /data/renderpage.js script. Feel free to examine this before continuing.
https://github.com/Tricorne-Games/HyperBook
I really appreciate all the help guys!
=
I am polishing a jQuery script to do the following in a rigid sequence...
Fade out the text.
Shrink the size of the container div.
Preload the remote HTML ///without showing it yet!///
Open the size of the container div.
Fade in the new remote HTML.
I do not mind if steps 1 and 2, 4 and 5 are combined to be one whole step (fade/resize at the same time). It's when the new HTML is loaded it interrupts the entire animation, even from the beginning.
The idea is that I do not want my remote HTML to show until after the animation renders right. I want the original text to fade out and the container div close up, then, behind the scenes, ready the text of the new HTML, and then have the container div open up and fade the new text in.
It seems when I call the load(url) function, it instantaneously loads the page up, and the animations are still running (like the new HTML ends up fading out, only to fade back in, and not the original text out and then the new one in). Either that, or the whole function is calling each line at the same time, and it's disrupting the page-changing effect I want.
Here's my current script setup...
$(document).ready(function() {
// Start-Up Page Load (Cover, ToC, etc.)
$('#content').load('pages/page1.htm');
// Navigating Pages
$(document).on('click', 'a', function(e) {
e.preventDefault();
var ahref = $(this).attr('href');
$('#content_container').animate({height: 'hide'}, 500);
$('#content').fadeTo('slow', 0.25);
$('#content').load(ahref);
$('#content').css({opacity: 0.0});
$('#content').fadeTo('slow', 1.0);
$('#content_container').animate({height: 'show'}, 500);
return false;
});
});
What is it wrong I'm doing here? I have used the delay() function on every one of those steps and it doesn't solve the problem of holding back the new text.
jQuery objects can provide a promise for their animation queues by calling .promise on the jQuery element.
You can wait on one or more of these to complete using $.when() and then perform other operations.
The following does a fade out and slide up in parallel with the load, then (only when the animations complete), slides it down then fades it in (in sequence):
$(document).on('click', 'a', function (e) {
e.preventDefault();
var ahref = $(this).attr('href')
var $container = $('#content_container');
var $content = $('#content');
// Slide up and fadeout at the same time
$container.animate({
height: 'hide'
}, 500);
$content.fadeOut();
// Load the content while fading out
$('#content').load(ahref, function () {
// Wait for the fade and slide to complete
$.when($container.promise(), $content.promise()).then(function () {
// Slide down and fadein (in sequence)
$container.animate({
height: 'show'
}, 500, function () {
$content.fadeTo('slow', 1.0);
});
});
});
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/pffm1tnb/3/
The only issue with this version is that the load may complete faster than the fadeout/slideup and show the new data too early. In this case you want to not use load, but use get (so you have control over when to insert the new content):
// Load the content while fading out
$.get(ahref, function (data) {
// Wait for the fade and slide to complete
$.when($container.promise(), $content.promise()).then(function () {
$content.html(data);
// Slide down and fadein (in sequence)
$container.animate({
height: 'show'
}, 500, function () {
$content.fadeTo('slow', 1.0);
});
});
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/pffm1tnb/4/
Notes:
return false from a click handler does the same as e.stopPropagation() and e.preventDefault(), so you usually only need one or the other.
I started with the JSFiddle from #Pete as no other sample was handy. Thanks Pete.
Update:
Based on the full code now posted, you are returning full pages (including header and body tags). If you change your code to .load(ahref + " #content" ) it will extract only the part you want. This conflicts with the second (better) example I provided which would need the pages returned to be partial pages (or extract the required part only).
Additional Update:
As $.get also returns a jQuery promise, you can simplify it further to:
$.when($.get(ahref), $container.promise(), $content.promise()).then(function (data) {
$content.html(data);
// Slide down and fadein (in sequence)
$container.animate({
height: 'show'
}, 500, function () {
$content.fadeTo('slow', 1.0);
});
});
The resolve values from each promise passed to $.when are passed to the then in order, so the first parameter passed will be the data from the $.get promise.
JSFiddle: http://jsfiddle.net/TrueBlueAussie/pffm1tnb/11/
The issue is because you're not waiting for the hide animations to finish before loading the content, or waiting for the content to load before starting the show animations. You need to use the callback parameters of the relevant methods. Try this:
$(document).on('click', 'a', function(e) {
e.preventDefault();
var ahref = $(this).attr('href'),
$content = $('#content'),
$contentContainer = $('#content_container');
$contentContainer.animate({ height: 'hide'}, 500);
$content.fadeTo('slow', 0.25, function() {
// animation completed, load content:
$content.load(ahref, function() {
// load completed, show content:
$content.css({ opacity: 0.0 }).fadeTo('slow', 1.0);
$contentContainer.animate({ height: 'show' }, 500);
});
});
});
Note that for the effect to work the most effectively on the UI you would need to perform the load() after the animation which takes the longest to complete has finished.
Instead of using the load() function, you can use the get() function and its callback paramater to save the HTML into a variable before actually putting it into the element with html().
After doing all the animations to fade out and close the old box (and maybe inside an animation-finished callback function) you'll want to use something like the following:
$.get(ahref, function(data) {
// JQuery animation before we want to see the text.
$('#content').html(data); // actually inserts HTML into element.
// JQuery animation to fade the text in.
});
Using a bunch of the code everyone posted here, I rewrote the segment I originally had to follow suit. This is now my working result.
$(document).ready(function() {
// Start-Up Page Load (Cover, ToC, etc.)
$('#content').load('pages/page1.htm');
// Navigating Pages
$(document).on('click', 'a', function(e) {
e.preventDefault();
var ahref = $(this).attr('href');
$('#content').fadeTo('slow', 0.0)
$('#content_container').animate({height: 'hide'}, 500, function(){
$('#content').load(ahref + '#content', function(){
$('#content_container').animate({height: 'show'}, 500, function(){
$('#content').fadeTo('slow', 1.0);
});
});
});
return false;
});
});
You can use deferred or callbacks function
$(document).on('click', 'a', function(e) {
e.preventDefault();
var ahref = $(this).attr('href');
var dfd1 = $.Deferred();
var dfd2 = $.Deferred();
var dfd3 = $.Deferred();
var dfd4 = $.Deferred();
$('#content_container').animate({height: 'hide'}, 500, function(){
dfd1.resolve();
});
dfd1.done(function() {
$('#content').fadeTo('slow', 0.25, function() {
dfd2.resolve();
});
});
dfd2.done(function() {
$('#content').load(ahref, function() {
$('#content').css({opacity: 0.0});
dfd3.resolve();
});
});
dfd3.done(function() {
$('#content').fadeTo('slow', 1.0, function() {
dfd4.resolve();
});
});
dfd4.done(function() {
$('#content_container').animate({height: 'show'}, 500);
});
return false;
});
I have the following simple code:
jQuery( document ).ready(function( $ ) {
$('.menu ul li:has("ul")').hover(function() {
$('>ul', this).stop().slideToggle();
});
});
I'm using the 'no conflict' way of firing jQuery but for some reason my slide toggle is closing on hover rather than opening! What am I doing wrong? I'm sure its simple but I just can't see it.
An example can be seen here http://ng1club.everythingcreative.co.uk
Use mouseenter and mouseleave instead. Sometimes hover without the second function as a hover out, triggers twice.
$('.menu ul li:has("ul")').mouseenter(function() {
$('>ul', this).stop().slideDown();
}).mouseleave(function() {
$('>ul', this).stop().slideUp();
});
Try this according to documentation you can use hover and apply effect on hover and hover out
jQuery('.menu ul li:has("ul")').hover(
function() {
jQuery('>ul', this).stop().slideDown();
}, function() {
jQuery('>ul', this).stop().slideUp();
}
);
.hover()
Sample Fiddle
I have a weird (one more time) issue on my animation.
In a nutshell, I have a picture, when I clicked on it, two div appears, and there is a close button to remove those divs. But when I click on that button, there is only one div who dissapears.
The two new divs have got a debug class and I normally remove it when I clicked on the button
$('#gallery').on('click', 'li', function(e) {
// To display the animations with position
var $this = $(this),
dataItem = $this.data('item');
// Left animation
if ( dataItem == 1 ) {
console.log( $this );
$this
.addClass('active')
.find('.info-texte')
.removeClass('hidden')
.addClass('debug');
// When animation is ended add the second part
$this.find('.debug').on('animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function() {
$this.find('.info-btn')
.removeClass('hidden')
.addClass('debug');
});
}
// Supprime le href event
e.preventDefault();
});
$('.btn-close').on('click', function() {
var $this = $(this);
$this
.parents()
.eq(3)
.removeClass('active')
.find('.info-texte, .info-btn')
.removeClass('debug')
.addClass('hidden');
});
You can see in action right here : http://www.jeremybarbet.com/effect/bug.html
As per my comments, your $this.parents().eq(3) is targeting the wrong element
if you change this to $this.parents('li.active')
both of your divs should dissapear:
http://jsfiddle.net/TDsCT/
EDIT
After closer inspection it is because of your click:
$('#gallery').on('click', 'li'
this is also fired when you click on the button as the button is inside your #gallery li. I have changed your code so you click on the image instead to open and then click on your button to close:
http://jsfiddle.net/TDsCT/5/
It seems the expression $this.parents().eq(3) isn't evaluating to your desired result -- the two divs on the left. Perhaps try a different set of DOM traversal methods? (e.g. $this.parents().eq(2).prev() and $this.parents().eq(2), though this would be sort of a kludge)
I have this code that makes menu items slide down and up. I want to add a timer so there is a delay in the slide down then back up.
$(function () {
$("#menu").find("li").each(function () {
if ($(this).find("ul").length > 0) {
$(this).mouseenter(function () {
$(this).find("ul").stop(true, true).slideDown();
});
$(this).mouseleave(function () {
$(this).find("ul").stop(true, true).slideUp();
});
}
});
});
It appears like you're writing javascript with jQuery
jQuery has a built in .delay function for animation queues.
In your example, delaying the slidedown animation by 300 ms would look like
$(this).find("ul").stop(true, true).delay(300).slideDown();
See jQuery's delay
A smart approach would be to add a hover intent to wait before triggering mouseleave:
jsBin demo
$("#menu").find("li:has(ul)").on('mouseenter mouseleave',function( e ){
var $UL = $(this).find('ul');
if(e.type==='mouseenter'){
clearTimeout( $(this).data('wait') );
$UL.stop(1,1).slideDown();
}else{
$(this).data('wait', setTimeout(function(){
$UL.stop().slideUp();
},180) );
}
});
instead of using if ($(this).find("ul").length > 0) { just use: ("li:has(ul)") to trigger your events only on li elements that has ul as children.
add an event callback e for the mouseenter mouseleave.
if the e event == mouseenter ..... else is mouseleave.
Than clear the data attribute called 'wait' and slideDown the children ul
Now, leaving the original li element to reach the 'distant' ul we have to cross over a white space (demo) that will usually trigger immediately the 'slideUp()', but we set a timeout counter inside that li's data attribute that will wait ~180ms before running.
Reaching the 'distant' ul element - beeing a children of the timeouted 'li' we clear the timeout (step 1) 'retaining' the mouseenter state.
use ~180ms or how much you think is needed to reach with the mouse the 'distant' UL element
Not sure if this is possible but I have a slideshow on my site that when a button is click the relevant slide, slides in.
What I want to do is add a timer so that after 3 seconds the next button is clicked, making my slideshow slide automatically.
$('#button a').click(function(){
var integer = $(this).attr('rel');
$('#myslide .cover').animate({left:-720*(parseInt(integer)-1)}) /*----- Width of div mystuff (here 160) ------ */
$('#button a').each(function(){
$(this).removeClass('active');
if($(this).hasClass('button'+integer)){
$(this).addClass('active')}
});
});
Ive added a Fiddle...
http://jsfiddle.net/5jVtK/
The simplest way to do this is to use setTimeout (happens once after a delay) or setInterval (happens every so often).
setTimeout( function() { $( '#button a' ).trigger( 'click' ) }, 3000 );
setInterval( function() { $( '#button a' ).trigger( 'click' ) }, 3000 );
Once you get this implemented, you may want to think about some other niceties, such as stopping the automatic progression when the user's mouse is over the next button or over the slideshow (since that implies interest in what is currently displayed) and resuming the autoadvance on mouseout.
Next: it sounds like you need to figure out how to dynamically find the correct button to trigger to keep advancing through multiple slides. This is one way to do it:
`
function click() {
// Find the button for the next slide in relationship to the currently active button
var $next = $( '#button' ).find( '.active' ).next( 'a' );
// If there isn't one, go to the beginning
if ( ! $next.length ) {
$next = $( '#button' ).find( 'a' ).first();
}
// Trigger the click
$next.trigger( 'click' );
setTimeout(click, 3000);
}
setTimeout(click, 3000);
Here's a link to a fiddle showing this in action:
http://jsfiddle.net/5jVtK/1/
You can trigger the click event of an element with jQuery by doing
$('#button a').click();
To make this happen at a 3 second interval, use setInterval():
function simulateClick(){
$('#button a').click();
};
setInterval(simulateClick, 3000);
Something like this should work. This way we are running a function at an interval and the click also triggers the same function. The timer never needs to activate a button, just activate the function that the button also activates.
$(document).ready(function() {
var timer = setInterval( slideFunction, 5000);
$('#button a').click(function(){
slideFunction();
});
function slideFunction(){
var integer = $('#button a').attr('rel');
$('#myslide .cover').animate({left:-720*(parseInt(integer)-1)}) /*----- Width of div mystuff (here 160) ------ */
$('#button a').each(function(){
$(this).removeClass('active');
if($(this).hasClass('button'+integer)){
$(this).addClass('active')}
});
}
});