jQuery callback functions - javascript

I have a function - vertToggle() - that toggles an element to slide up and down off the screen. I want to create a chain of events that slides the element off screen, changes the content within it, then slides it back up. However they need to be fired one after the other, not at the same time so they need to be setup as callbacks to one another.
The current, non-working, setup is:
vertToggle( '-' );
$(".content").hide()
$("#"+load).show();
vertToggle( '+' );
However I don't have my head around the necessary callback function syntax to make these trigger correctly, ie perform vertToggle(), when complete do the hide/show inbetween, then when these are done perform the second vertToggle().
Thanks for any pointers.
EDIT: as requested this is the vertToggle function, not sure if it's relevant.. $overlay is the element that is being animated:
function vertToggle( offset ) {
var height = $overlay.outerHeight();
var props = {};
var distance = ( offset == '-' ) ? height : 0 ;
props["bottom"] = offset+distance+'px';
if( !Modernizr.csstransitions ) {
$overlay.animate(props, 750);
$overlay.toggleClass("open");
}
else {
$overlay.css(props);
$overlay.toggleClass("open");
}
}

If you use .slideToggle(), you can add a function as a callback.
$("something").click(function(){
$("somethingelse").slideToggle(function(){
CallbackFunction()
})
});
Does that help?

Related

getBoundingClientRect in each: undefined is not a function

So I am trying to call some functions when fullscreen sections are in the viewport. Let's say I have 7 sections, then I want something to happen when a certain section is inside the viewport (I have a function that snaps the sections into the viewport so there can never be multiple sections in the viewport, but I am trying to find out which section is visible in the viewport).
Here is a fiddle: http://jsfiddle.net/h7Hb7/2/
function isInViewport() {
$("section").each(function () {
var $this = $(this),
wHeight = $(window).height(),
rect = $this.getBoundingClientRect(); // Error in console
// Borrowed from http://stackoverflow.com/a/7557433/5628
if (rect.top >= 0 && rect.bottom <= wHeight) {
console.log($this.attr("id") + "in viewport");
}
});
}
$(window).scroll(function () {
// Other functions are called inside the setTimeout function, can't remove
clearTimeout($.data(this, "scrollTimer"));
$.data(this, "scrollTimer", setTimeout(function () {
isInViewport();
}, 1200));
});
I don't know where to start looking but I am guessing it's to do with the each function. Is it the each function that poses a problem? It can't be a CSS issue, because the problem occurs on scroll when the CSS has already loaded.
You could stick with jQuery and use the array [] notation ie:
var myClient = $(currentGrid)[0].getBoundingClientRect();
alert(myClient.top)
jQuery object doesn't have getBoundingClientRect method, you should get the HTMLElement object and then call the method or:
this.getBoundingClientRect();
As a suggestion, if using a plugin is an option, you can consider using the jquery.inview plugin.
You can pass event through function and use
e.target.getBoundingClientRect() function. It will Work

Sliding Carousel - Pagination not directing to correct location

My carousel has been built so that it slides to the next frame automatically every 5 seconds, for which I have written:
var carousel = $('.carousel ul:first');
var pagination = $('.carousel ul:last');
function slide01() {
carousel.delay(5000).animate({'margin-left':'-100%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(2)').addClass('on');
});
slide02();
};
function slide02() {
carousel.delay(5000).animate({'margin-left':'-200%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(3)').addClass('on');
});
slide03();
};
function slide03() {
carousel.delay(5000).animate({'margin-left':'-300%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(4)').addClass('on');
});
slide04();
};
function slide04() {
carousel.delay(5000).animate({'margin-left':'0%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(1)').addClass('on');
});
slide01();
};
slide01();
While this is working, I'm having difficulties with the pagination buttons. They respond but instead of going to its appropriate page, it's going to the next page, which you can view in my FIDDLE.
This is the code I have written for each button:
pagination.find('li:nth-child(1)').click(function(){
carousel.stop().animate({'margin-left':'0%'});
});
pagination.find('li:nth-child(2)').click(function(){
carousel.stop().animate({'margin-left':'100%'});
});
pagination.find('li:nth-child(3)').click(function(){
carousel.stop().animate({'margin-left':'200%'});
});
pagination.find('li:nth-child(4)').click(function(){
carousel.stop().animate({'margin-left':'300%'});
});
What can I do to fix this?
I have updated your Javascript code to provide your desired functionality and made it more extendable. Please let me know if you have any questions.
var carousel = $('.carousel ul:first');
var pagination = $('.carousel ul:last');
var slideWidth = 200;
var autoSlideTiming = 5000;
var timeout = null;
// Buttons
$.each(pagination.children(), function(i){
$(this).click(function(){
pagination.find('li.on').removeClass('on');
//if you wanted to find the width dynamically
//var slideWidth = $(this).parent().parent().find('ul:eq(0) li:eq('+i+')').width();
carousel.stop().animate({'margin-left': -(i * slideWidth)});
$(this).addClass('on');
});
});
// Slider
function advanceSlide(){
var currIndex = $(".pagination li.on").index();
var nextIndex = ((pagination.children().length - 1) == currIndex) ? 0 : (currIndex + 1);
carousel.animate({'margin-left': -(nextIndex * slideWidth)}, function() {
pagination.find('li.on').removeClass('on');
pagination.find(':nth-child('+(nextIndex + 1)+')').addClass('on');
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
});
}
$('.carousel').hover(function(){
clearTimeout(timeout);
}, function(){
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
});
//init auto slide
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
Also here is an updated Fiddle
Explanation
The $.each is a jQuery utility function that works like a normal for loop (I would actually recommend a vanilla JavaScript for loop if you know how). This $.each accepts 2 arguments: a collection (pagination.children() in our case, which is an array of the li's), and a callback function. This callback function passes in i which is our zero-based index of the collection. So, it's not 1, 2, 3, 4 but 0, 1, 2, 3. In this loop this is each li, which it is attaching a click event that handles removing and applying your on class and also the animation. The -(i * slideWidth) is taking the current zero-based index and multiplying it times the slideWidth and then getting the negative value of that result. So, the first slide -(0 * 200) would animate the margin-left to -0 or 0, and the second slide -(1 * 200) would animate the margin-left to -200, which pulls it 200 pixels in the left direction making it slide. This will allow you to had new li's to your pagination and this code will still work!
The trick to making the advanceSlide() function dynamic, is finding what the index of the next slide is. To find out where we need to go we first must find where we are. That is what currIndex is for, which we find with the jQuery index() function, which returns the zero-based index of the li with the on class. Now the magic. The nextIndex variable is being set using a ternary operator to find the value. A ternary is just programmer shorthand for a basic if then else statement. So if the ternary in this code was written out the normal way it would look like this:
var nextIndex;
if( (pagination.children().length - 1) == currIndex ){
nextIndex = 0;
}else{
nextIndex = currIndex + 1;
}
Remeber our currIndex is zero-based, but our collection (pagination.children()) length (number of items in the total collection) is not zero-based so we must minus 1 from it. Then we are checking if that value is equal to our currIndex because if this is the last item in our collection we need to set our nextIndex back to 0 so the slideshow loops back around. If it's not the last index we just add 1 to advance to the next slide. After we find that nextIndex we do our animation and apply our class.
I know this is a little long winded, but it's important that you fully understand the code you implement! Please let me know if you have nay further questions!
You have a "Maximum call stack size exceeded." error. Yours slide0X function calls another slide0Y function and so on. You should invoke slide0Y only once the slide0X animation ended (inside the complete callback).
Moreover in your click callbacks, margin-left should be negative since you want to slide to the left.
BTW, I would implement a function slideTo(slideId) so that it will work for n slides, instead of writing a function for each slide.

Animating long sequences in jQuery

I have to make a long animation with jQuery, full of fadeOuts,fadeIns,slideIns,...
The problem I am having is that my code looks ugly and it is full of callback. Also, if I want to stop animation for some time like: slideOut->wait 5 seconds->slideIn I have to use delay and I am not sure if that is the best practice.
Example:
/* Slides */
var slide1 = $('div#slide1'),
slide2 = $('div#slide2'),
slide3 = $('div#slide3');
$(document).ready(function(){
slide1.fadeIn(function(){
slide2.fadeIn(function(){
slide3.fadeIn().delay(3000).fadeOut(function(){
slide2.fadeOut(function(){
slide1.fadeOut();
});
});
});
});
});
JSFIddle: http://jsfiddle.net/ZPvrD/6/
Question: Is there any other way of building animations in jQuery, possibly even some great plugin to help me solve this problem?
Thanks!
Here's the plugin you were looking for :) Does the exact same thing, but is much more flexible than your existing code http://jsfiddle.net/ZPvrD/11/
(function($){
$.fn.fadeInOut = function(middleDelay) {
middleDelay = middleDelay || 0;
var index = 0,
direction = 1, // 1: fading in; -1: fading out
me = this,
size = me.size();
function nextAnimation() {
// Before the first element, we're done
if (index === -1 ) { return; }
var currentEl = $(me.get(index)),
goingForward = direction === 1,
isLastElement = index === (size - 1);
// Change direction for the next animation, don't update index
// since next frame will fade the same element out
if (isLastElement && goingForward) {
direction = -1;
} else {
index += direction;
}
// At the last element, before starting to fade out, add a delay
if ( isLastElement && !goingForward) {
currentEl.delay(middleDelay);
}
if (goingForward) {
currentEl.fadeIn(nextAnimation);
} else {
currentEl.fadeOut(nextAnimation);
}
}
nextAnimation();
return this;
}
})(jQuery);
And you call it like
$('div.slideWrapper>div.slide').fadeInOut(3000);
This process of traversing up and down a list of jQuery elements waiting for each animation to finish could be abstracted so that it could be used for other things besides fadeIn and fadeOut. I'll leave that for you to try out if you feel adventurous.
Try this:
/* Slides */
var slide = $('div[id*="slide"]');
$( function(){
slide.each( function( k ){
$( this ).delay( 500 * k ).fadeIn();
});
});
JQuery animations take two parameters (maximum), duration and complete, duration is the time in milliseconds for how long you want your animation to complete, or you can use "slow" or "fast", and the second params complete which is the callback function.
If don't want to use delay, you may make the previous animation slow.
e.g.
slide1.fadeIn(5000, function(){
slide2.fadeIn();
};

how to make a <div> appear in slow motion

I want the javascript code to show a div in slow motion.
function showDiv(divID)
{
if(document.getElementById(divID).style.display=='none')
{
document.getElementById(divID).style.display='block';
}
}
Here div appears, but not in slow motion. Can anyone help ??
Thanks in advance
Dev..
There is no need of jQuery in this atall , its just a basic I am using your function to explain how thats done.
function showDiv(divID)
{
if(document.getElementById(divID).style.display=='none')
{
document.getElementById(divID).style.display='block';
}
}
What your function is doing is basically removing the whole Element from BOX Model ( the toggle of block and none removes the element totally from the BOX Model so it doesnt occupies any space or anything , this but may / may not cause some layout issues );
Now to animate it in slow motion you need a timing function.
a timing function is a simple mathematical function which gives the value of the property ( opacity in your case ) for a given time or depending on other parameters .
Other then that you also need to use properties like opacity in order to fade it (Opacity is a CSS property that defines the transparency of an element and its childrens )
So let us begin with a very basic show / hide using setTimeout Function in JS.
function getValue(t,dir){
if( dir > 0){
return 0.5*t; /* Y = mx + c */
}else{
return 1-(0.5*t);
}
/*
Here the slope of line m = 0.5.
t is the time interval.
*/
}
function animator(divID){
if(!(this instanceof animator)) return new animator(divID); /* Ignore this */
var Node = document.getElementById(divID),
start = new Date.getTime(), // The initiation.
now = 0,
dir = 1,
visible = true;
function step( ){
now = new Date.getTime();
var val = getValue( now - start,dir)
Node.style.opacity = val;
if( dir > 0 && val > 1 || dir < 0 && val < 0 ){
visible = !(visible*1);
// Optionally here u can call the block & none
if( dir < 0 ) { /* Hiding and hidden*/
Node.style.display = 'none'; // So if were repositioning using position:relative; it will support after hide
}
/* Our animation is finished lets end the continous calls */
return;
}
setTimeout(step,100); // Each step is executated in 100seconds
}
this.animate = function(){
Node.style.display = 'block';
dir *= -1;
start = new Date.getTime();
setTimeout(step,100);
}
}
now you can simply call the function
var magician = new animator('divName');
then toggle its animation by
magician.animate();
Now playing with the timing function you can create whatever possibilities you want as in
return t^2 / ( 2 *3.23423 );
or even higher polynomial equations like
return t^3+6t^2-38t+12;
As you can see our function is very very basic but it explains the point of how to make animations using pure js . you can later on use CSS3 module for animation and trigger those classes with javascript :-)
Or perhaps write a cross browser polyfill using CSS3 where available ( it is faster ) , and JS if not :-) hope that helps
Crossbrowser solution (without jQuery) :
HTML :
<div id="toChange" ></div>
CSS :
#toChange
{
background-color:red;
width:200px;
height:200px;
opacity:0;//IE9, Firefox, Chrome, Opera, and Safari
filter:alpha(opacity=0);//IE8 and earlier
}
Javascript :
var elem=document.getElementById("toChange");
var x=0;
function moreVisible()
{
if(x==1)clearInterval(t);
x+=0.05;
elem.style.opacity=x;
elem.style.filter="alpha(opacity="+(x*100)+")";
}
var t=setInterval(moreVisible,25);
Fiddle demonstration : http://jsfiddle.net/JgxW6/1/
So you have a few jQuery answers but I wouldn't recommend jQuery if fading the div is all you want.
Certainly jQuery makes things easier but it is a lot of overhead for a single simple functionality.
Here is someone that did it with pure JS:
Fade in and fade out in pure javascript
And a CSS3 example:
How to trigger CSS3 fade-in effect using Javascript?
You can use jquery $.show('slow') for the same, if you want to do the same without using jquery then you might be required to code something to show the effect yourself, you may have a look at source of jquery's show function http://james.padolsey.com/jquery/#v=1.6.2&fn=show . alternatively , you can also use fadein() for fade in effect in jquery
Yes you can do it using Jquery. Here is my sample example
$('#divID').click(function() {
$('#book').show('slow', function() {
// Animation complete.
});
});
For details clik here
Thanks.

JQuery If condition (specific for a content slider)

I am new at JQuery and I have a specific question about the IF-THEN-ELSE fork.
The big problem for me is the syntax of this (I suck at Javascript). It would help me if anyone can "translate" the pseudo code into a JQuery (or Javascript) valide code.
The pseudo code:
IF "#Contentshowroom" css "left" is NOT > 1960px
THEN
On Click "#Forwardbutton" DO
animate "#Contentshowroom" css "left" =+980px
ELSE You can not click on the "#Forwardbutton"
Place the if() statement in the click handler for #Forwardbutton to test the left position of #Contentshowroom.
If you're using jQuery:
$('#Forwardbutton').click(function() {
var $Content = $('#Contentshowroom');
if( $Content.offset().left <= 1960 ) {
$Content.animate({ left: '+= 980' });
}
});
So now when you click the Forwardbutton, it will check the left .offset() position of the Contentshowroom to see if it is less than or equal to 1960px. And if so, it will animate the left position an additional 980px.
jQuery's .offset() method gives you the top/left positions relative to the body. If you want it relative to its parent container, then use jQuery's .position() method.
click doc
animate doc
offset doc
$("#Forwardbutton").click( function( e ){
// lookup is safe, no noticable performance cost.
// though a reference makes it more losely coupled.
// I'll leave it at your discretion.
var target = $("#Contentshowroom")
// NOTE: the offset parent should have position relative or absolute.
, leftPos = target.offset().left;
if ( leftPos < 1960 ) {
target.animate({
left : leftPos + 980
}); // see docs to tweak animation
} // else do nothing.
} );
Could also use e.preventDefault(); , but don't if it's not needed, it will safe you headaches if you add more listeners to your buttons and find out they're not working.
// first store contentShowroom and it's left property to save getting > 1
var contentShowroom = $('#Contentshowroom');
var showroomLeft = contentShowroom.css('left');
var forwardButton = $('#Forwardbutton');
if (showroomLeft <= 1960){
forwardButton.click(function(){
contentShowroom.animate({left: showroomLeft + 980);
}
}
else {
forwardButton.unbind('click');
}
if this is to be run once at the beginning then
if ( $('#Contentshowroom').offset().left > 1960 )
{
$('#Forwardbutton').click( function(){
$('#Contentshowroom').animate({left:'+=980'});
} );
}
else
{
// if the #Contentshowroom is a link then
$('#Contentshowroom').removeAttr('href');
// if the #Contentshowroom is a button then
// $('#Contentshowroom').attr('disabled',true);
}

Categories