jQuery Combine Functions - javascript

I have two similar functions that I would like to combine so that I can use anywhere throughout the site. It's a simple jquery slideUp / slideDown effect that finds the div with the class 'hidden' and on click, it shows and hides
$('.clicker1').click(function(){
// grab the hidden content
var desc = $(this).parent().find('.hidden');
// remove toggle class and slide up
if ($(this).hasClass('toggled')) {
$(this).removeClass('toggled');
$(desc).slideUp(400, 'linear');
}
// add toggle class, slide down, and move the page up
else {
var loc = this;
$(desc).slideDown(400, 'linear', function () {
$.scrollTo($(loc).offset().top - 60, 400);
});
$(this).addClass('toggled');
$('.clicker1').not(this).removeClass('toggled');
$('.hidden').not(desc).slideUp(400, 'linear');
}
});
$('.clicker2').click(function(){
// grab the hidden content
var desc = $(this).parent().find('.hidden2');
// remove toggle class and slide up
if ($(this).hasClass('toggled')) {
$(this).removeClass('toggled');
$(desc).slideUp(400, 'linear');
}
// add toggle class, slide down, and move the page up
else {
var loc = this;
$(desc).slideDown(400, 'linear', function () {
$.scrollTo($(loc).offset().top - 60, 400);
});
$(this).addClass('toggled');
$('.clicker2').not(this).removeClass('toggled');
$('.hidden').not(desc).slideUp(400, 'linear');
}
});
Can I create one function and put in my own 'clickerX' and 'hiddenX' ?

It looks like the handlers only differ by the class's they use as selectors. The easiest approach is to write a function which generates a click handler based on the class names. Try the following
var makeHandler = function(className, hiddenClassName, ) {
return function() {
// grab the hidden content
var desc = $(this).parent().find(hiddenClassName);
// remove toggle class and slide up
if ($(this).hasClass('toggled')) {
$(this).removeClass('toggled');
$(desc).slideUp(400, 'linear');
}
// add toggle class, slide down, and move the page up
else {
var loc = this;
$(desc).slideDown(400, 'linear', function () {
$.scrollTo($(loc).offset().top - 60, 400);
});
$(this).addClass('toggled');
$(className).not(this).removeClass('toggled');
$(hiddenClassName).not(desc).slideUp(400, 'linear');
};
};
$('.clicker1').click(makeHandler('.clicker1', '.hidden'));
$('.clicker2').click(makeHandler('.clicker2', '.hidden2'));

Absolutely. You want to write a plugin. There's tons of tutorials on making a jQuery plugin, but the official docs give a good start.

Related

Need help getting custom slideshow to autoplay

I've taken over a project that was built by someone else. The site features a custom slideshow on the home page. I've made some changes to the look/feel of the slideshow per client requests, but the last thing it needs is autoplay.
Below is the script for the slideshow. I know about setInterval but I'm not sure where to put it, or if the code needs to be adjusted a bit before dropping that in.
$(document).ready(function() {
// A little script for preloading all of the images
// It"s not necessary, but generally a good idea
$(images).each(function(index, value){
// Ski first image since it is already loaded
if( index != 0 ) {
$("<img/>")[0].src = this;
}
});
// Feature Slider Navagitaion
$('.feature .ei-slider-nav li a').click( function(e) {
e.preventDefault();
var thisLink = $(this);
var navIndex = thisLink.parent('li').index();
thisLink.parents('ul').children('li').removeClass('active');
thisLink.parent('li').addClass('active');
// Is this item already active?
if( !$('.feature .ei-slider-title .ei-slider-title-item:eq('+navIndex+')').hasClass('active')) {
// Fade in/out feature image
$('.feature .ei-slider-large img').animate({opacity: 0}, 500, function() {
// Support for non-opacity browsers
$(this).css('visibility', 'hidden');
// Load new feature image
$(this).attr('src', images[navIndex]);
$(this).attr('alt', imagesAlt[navIndex]);
$(this).css('visibility', 'visible');
$('.feature .ei-slider-large img').animate({opacity: 1}, 500);
});
// Fade in/out feature text
$('.feature .ei-slider-title .ei-slider-title-item.active').fadeOut(500, function() {
$(this).parent().children().removeClass('active');
$('.feature .ei-slider-title .ei-slider-title-item:eq('+navIndex+')').addClass('active').fadeIn();
});
// Fade in/out feature credit
$('.content .ei-slider-credit span.active').fadeOut(500, function() {
$(this).parent().children().removeClass('active');
$('.content .ei-slider-credit span:eq('+navIndex+')').addClass('active').fadeIn();
});
}
});
// Feature Slider Learn More
$('.feature .ei-slider-title-item-learn').click( function(e) {
e.preventDefault();
thisPrev = $(this).prev();
if( thisPrev.css('display') == 'none') {
thisPrev.slideDown();
thisPrev.css('visibility', 'visible');
thisPrev.animate({'opacity': 1}, 500, function() {
});
$(this).html('Hide');
} else {
thisPrev.animate({'opacity': 0}, 500, function() {
thisPrev.slideUp();
thisPrev.css('visibility', 'hidden');
});
$(this).html('Hide');
$(this).html('Learn More');
}
});
});
Thanks!
This would probably be a little bit easier if there were a way to keep track of the current state of the slideshow outside the context of clicking on a slide's navigation link. The first thing I would add, right above $('.feature .ei-slider-nav li a').click( function(e) {... would be:
var eiSlider = {
currentSlideIndex: 0,
nextSlide: null, // we will define this later
autoPlay: null // we will define this later too
};
Then, inside of the function I mentioned above, as the first order of business inside of the check for whether the slide is already active, I'd add this:
// Set the currentSlide index on the global eiSlider tracking object
eiSlider.currentSlide = navIndex;
Next, you'll want to make a function to handle advancing the slideshow automatically:
eiSlider.nextSlide = function() {
var currentSlideIndex = eiSlider.currentSlideIndex,
nextSlideIndex = currentSlideIndex + 1,
totalSlides = $('.ei-slider-large img').length;
// If we are already at the end of the images, loop back to the beginning
if ( nextSlideIndex < totalSlides ) {
nextSlideIndex = 0;
}
// Trigger a click to move forward to the next slide
$('.feature .ei-slider-nav li:eq(' + nextSlideIndex + ') a').trigger('click');
};
I've also moved the work of setting the "active" class on a given slide's nav link to happen inside of the logic around making sure the slide you clicked on wasn't already active, to make sure it doesn't get set incorrectly.
Finally, you can use setInterval (at the bottom of all of the above code) to handle the autoplay portion.
// Auto-advance the slides every 5 seconds. Adjust the value as necessary
eiSlider.autoPlay = window.setInterval(function(){
eiSlider.nextSlide();
}, 5000);
Your final, updated code would look something like this:
$(document).ready(function() {
// A little script for preloading all of the images
// It"s not necessary, but generally a good idea
$(images).each(function(index, value){
// Ski first image since it is already loaded
if( index !== 0 ) {
$("<img/>")[0].src = this;
}
});
// Object for basic state tracking and namespacing of slideshow functions
var eiSlider = {
currentSlideIndex: 0,
nextSlide: null, // we will define this later
autoPlay: null // we will define this later too
};
// Feature Slider Navagitaion
$('.feature .ei-slider-nav li a').click( function(e) {
e.preventDefault();
var thisLink = $(this),
navIndex = thisLink.parent('li').index();
// Is this item already active?
if( !$('.feature .ei-slider-title .ei-slider-title-item:eq('+navIndex+')').hasClass('active')) {
thisLink.closest('li').siblings().removeClass('active');
thisLink.closest('li').addClass('active');
// Set the currentSlide index on the global eiSlider tracking object
eiSlider.currentSlideIndex = navIndex;
// Fade in/out feature image
$('.feature .ei-slider-large img').animate({opacity: 0}, 500, function() {
// Support for non-opacity browsers
$(this).css('visibility', 'hidden');
// Load new feature image
$(this).attr('src', images[navIndex]);
$(this).attr('alt', imagesAlt[navIndex]);
$(this).css('visibility', 'visible');
$('.feature .ei-slider-large img').animate({opacity: 1}, 500);
});
// Fade in/out feature text
$('.feature .ei-slider-title .ei-slider-title-item.active').fadeOut(500, function() {
$(this).parent().children().removeClass('active');
$('.feature .ei-slider-title .ei-slider-title-item:eq('+navIndex+')').addClass('active').fadeIn();
});
// Fade in/out feature credit
$('.content .ei-slider-credit span.active').fadeOut(500, function() {
$(this).parent().children().removeClass('active');
$('.content .ei-slider-credit span:eq('+navIndex+')').addClass('active').fadeIn();
});
}
});
// Feature Slider Learn More
$('.feature .ei-slider-title-item-learn').click( function(e) {
e.preventDefault();
thisPrev = $(this).prev();
if ( thisPrev.css('display') === 'none') {
thisPrev.slideDown();
thisPrev.css('visibility', 'visible');
thisPrev.animate({'opacity': 1}, 500, function() {});
$(this).html('Hide');
} else {
thisPrev.animate({'opacity': 0}, 500, function() {
thisPrev.slideUp();
thisPrev.css('visibility', 'hidden');
});
$(this).html('Hide');
$(this).html('Learn More');
}
});
// Function to handle slide advancement
eiSlider.nextSlide = function() {
var currentSlideIndex = eiSlider.currentSlideIndex,
nextSlideIndex = currentSlideIndex + 1,
totalSlides = $('.ei-slider-large img').length;
// If we are already at the end of the images, loop back to the beginning
if ( currentSlideIndex < (totalSlides - 1) ) {
nextSlideIndex = 0;
}
// Trigger a click to move forward to the next slide
$('.feature .ei-slider-nav li:eq(' + nextSlideIndex + ') a').trigger('click');
};
// Auto-advance the slides every 5 seconds. Adjust the value as necessary
eiSlider.autoPlay = window.setInterval(function(){
eiSlider.nextSlide();
}, 5000);
});
Bear in mind this answer makes a few assumptions, the main one being that the eiSldier namespace is available; if it's not, just use a different namespace than the one I provided, OR add these three new items to the existing namespace so it doesn't get overwritten. The only change in that case would not be defining eiSlider as an object with three properties, but instead defining simply eiSlider.currentSlide = 0, and then proceeding to define the other two functions the way they already are defined later on in the example.
If the eiSlider namespace already exists, it's entirely possible that currentSlide or some equivalent property exists on it already, so you could take advantage of that if it does, rather than making a duplicate (or worse, overriding it in a way that could cause errors in the rest of its functionality).
One thing I should note that the code above doesn't currently do is stop/clear the autoplay out when you manually click on a slide's navigation link. That's a pretty important usability issue that will need to get cleaned up. You can accomplish this by using clearInterval(eiSlider.autoPlay), but to make that really work correctly, you'd need to separate out the code that handles slide advancement from the actual click event.
Check out this slightly modified JS Bin that shows the auto-advance working as intended, plus the changes I mentioned above with clearInterval:
http://jsbin.com/gumaqefere/1/edit?html,js,console,output

skip visible items from animating using JS

I'm using waypoints to have fadeIn animation when the user scrolled down, and it worked. But as you can see, during the onload, there's a fadeIn too for the first few visible items.
How to not to have that? since I binded all .card to way[point.
My js
$(function() {
var waypoints = $('.card').waypoint(function (direction) {
$(this).addClass("animated fadeIn");
}, {
offset: '90%'
});
});
DEMO : http://jsfiddle.net/ghx49d7x/
Not sure if its exactly what you want, but you can add a variable to indicate if the page has loaded or not and only add fadeIn if it has:
$(function () {
var pageLoaded = false;
var waypoints = $('.card').waypoint(function (direction) {
$(this).addClass("animated"
+ (pageLoaded ? " fadeIn" : ""));
}, {
offset: '90%'
});
pageLoaded = true;
});
Updated Fiddle: http://jsfiddle.net/ghx49d7x/3/

ShowHide plugin: Fade in instead of toggle?

I'm using the ShowHide Plugin and I'm trying to get it to fade in instead of toggle/slide into view. Here is what I have:
showHide.js
(function ($) {
$.fn.showHide = function (options) {
//default vars for the plugin
var defaults = {
speed: 1000,
easing: '',
changeText: 0,
showText: 'Show',
hideText: 'Hide'
};
var options = $.extend(defaults, options);
$(this).click(function () {
// optionally add the class .toggleDiv to each div you want to automatically close
$('.toggleDiv').slideUp(options.speed, options.easing);
// this var stores which button you've clicked
var toggleClick = $(this);
// this reads the rel attribute of the button to determine which div id to toggle
var toggleDiv = $(this).attr('rel');
// here we toggle show/hide the correct div at the right speed and using which easing effect
$(toggleDiv).slideToggle(options.speed, options.easing, function() {
// this only fires once the animation is completed
if(options.changeText==1){
$(toggleDiv).is(":visible") ? toggleClick.text(options.hideText) : toggleClick.text(options.showText);
}
});
return false;
});
};
})(jQuery);
$(document).ready(function(){
$('.show_hide').showHide({
speed: 1000, // speed you want the toggle to happen
easing: '', // the animation effect you want. Remove this line if you dont want an effect and if you haven't included jQuery UI
changeText: 1, // if you dont want the button text to change, set this to 0
showText: 'View',// the button text to show when a div is closed
hideText: 'Close' // the button text to show when a div is open
});
});
HTML
<a class="show_hide" href="#" rel="#slidingDiv">View</a></pre>
<div id="slidingDiv" class="toggleDiv" style="display: none;">Fill this space with really interesting content.</div>
<pre>
<a class="show_hide" href="#" rel="#slidingDiv_2">View</a></pre>
<div id="slidingDiv_2" class="toggleDiv" style="display: none;">Fill this space with really interesting content.</div>
<pre>
There is a bug in the line $('.toggleDiv').slideUp(options.speed, options.easing);, here you should not slideup the target div - use $('.toggleDiv').not(toggleDiv).slideUp(options.speed, options.easing);
Try
(function ($) {
$.fn.showHide = function (options) {
//default vars for the plugin
var defaults = {
speed: 1000,
easing: '',
changeText: 0,
showText: 'Show',
hideText: 'Hide'
};
var options = $.extend(defaults, options);
$(this).click(function () {
// this var stores which button you've clicked
var toggleClick = $(this);
// this reads the rel attribute of the button to determine which div id to toggle
var toggleDiv = $(this).attr('rel');
// optionally add the class .toggleDiv to each div you want to automatically close
$('.toggleDiv').not(toggleDiv).slideUp(options.speed, options.easing);
// here we toggle show/hide the correct div at the right speed and using which easing effect
$(toggleDiv).slideToggle(options.speed, options.easing, function () {
// this only fires once the animation is completed
if (options.changeText) {
$(toggleDiv).is(":visible") ? toggleClick.text(options.hideText) : toggleClick.text(options.showText);
}
});
return false;
});
};
})(jQuery);
Demo: Fiddle

Delay animation until other animation is complete (sliding content(

I have this code which animates between divs sliding out. If an item is clicked, it's relevant content slides out. If another item is clicked, the current content slides back in and the new content slides out.
However,
var lastClicked = null;
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).toggle(function() {
if (lastClicked && lastClicked != this) {
// animate it back
$(lastClicked).trigger('click');
}
lastClicked = this;
$('.each-brew-content.'+animCls).show().animate({ left: '0' }, 1000).css('position','inherit');
}, function() {
$('.each-brew-content.'+animCls)
.animate({ left: '-33.3333%' }, 1000, function() { $(this).hide()}) // hide the element in the animation on-complete callback
.css('position','relative');
});
})(animateClasses[i]); // self calling anonymous function
}
However, the content sliding out once the already open content slides back is sliding out too quickly - it needs to wait until the content has fully slided back in before it slides out. Is this possible?
Here's a link to what I'm currently working on to get an idea (http://goo.gl/s8Tl6).
Cheers in advance,
R
Here's my take on it as a drop-in replacement with no markup changes. You want one of three things to happen when a menu item is clicked:
if the clicked item is currently showing, hide it
if something else is showing, hide it, then show the current item's content
if nothing is showing, show the current item's content
var lastClicked = null;
// here lastClicked points to the currently visible content
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).click(function(event){
if(lastClicked && lastClicked == animCls){
// if the lastClicked is `this` then just hide the content
$('.each-brew-content.'+animCls).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
}).css('position','relative');
lastClicked = null;
}else{
if(lastClicked){
// if something else is lastClicked, hide it,
//then trigger a click on the new target
$('.each-brew-content.'+lastClicked).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
$(event.target).trigger('click');
}).css('position','relative');
lastClicked = null;
}else{
// if there is no currently visible div,
// show our content
$('.each-brew-content.'+animCls).show()
.animate({ left: '0' }, 1000)
.css('position','relative');
lastClicked = animCls;
}
}
});
})(animateClasses[i]); // self calling anonymous function
}
Well, I'm pretty sure there are other more easy possibilities and I didn't have much time but here is a working jsfiddle: http://jsfiddle.net/uaNKz/
Basicly you use the callback function to wait until the animation is complete. In this special case it's the complete: function(){...}
$("document").ready(function(){
$('#ale').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
$('#bramling').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
});
I give a toggled class if a div is expanded. Since the animation on your page seems to be pretty much broken I think this would be a better way to do this. But remember: my code isn't really good. Just fast and it can be refactored. It's working tho..
Rather than using toggles, bind an on "click" handler to your ".each-brew" divs. In the handler, first hide content divs and then show the appropriate content when that animation completes. You can do that with either a promise or a callback. Something like this...
$(".each-brew").on("click", function (event) {
$(".each-brew-content").show().animate({ left: "0" }, 1000, function() {
// Get the brew name from the class list.
// This assumes that the brew is the second class in the list, as in your markup.
var brew = event.currentTarget.className.split(/\s+/)[1];
$(".each-brew-content." + brew).animate({ left: "-33.3333%" }, 1000, function() { $(this).hide(); });
});
});
I think an event and observer would do the trick for you.
set up the callback function on completion of your animation to fire an event.
the listener would first listen for any animation event and after that event is triggered listen for the completion event. when the completion event is fired execute the initial animation event.run method (or whatever you would want to call it)
Within the listener
on newanimationeventtriger(new_anim) wait for x seconds (to eliminate infinite loop poss) while if this lastevent triggers done == true{
new_anim.run();
}

Mootools slider adjustments

I am new to mootools. I have joomla site with MooSlider:
var MooSlider = new Class({
initialize: function(options) {
this.options = Object.extend({
container: null,
slides: null,
navs:null,
transition: Fx.Transitions.Sine.easeOut,
effectDuration: 500,
fromTop: 500,
topDist: 100,
slideDelay: 5000
}, options || {});
if(!$(this.options.container)) return;
this.start();
},
start: function(){
this.elements = $(this.options.container).getElements(this.options.slides);
this.navs = $(this.options.container).getElements(this.options.navs);
this.currentElement = 0;
this.elements.each(function(elem, i){
var nav = this.navs[i];
if(i==this.currentElement){
nav.addClass('selected');
}
elem.setStyles({
'position':'absolute',
'top':0,
'left':0,
'opacity':( i==this.currentElement ? 1 : 0 )
});
this.elements[i]['fx'] = new Fx.Styles(elem, {
duration:this.options.effectDuration,
wait:false,
transition:this.options.transition
});
this.elements[i]['nav'] = nav;
elem.addEvents({
'mouseenter': function(){
$clear(this.period);
}.bind(this),
'mouseleave': function(){
if( this.options.slideDelay )
this.period = this.rotate.periodical(this.options.slideDelay, this);
}.bind(this)
});
nav.addEvent('click', function(event){
if(this.currentElement==i) return;
new Event(event).stop();
$clear(this.period);
this.changeSlide(i);
if( this.options.slideDelay )
this.period = this.rotate.periodical(this.options.slideDelay, this);
}.bind(this));
}.bind(this));
if( this.options.slideDelay )
this.period = this.rotate.periodical(this.options.slideDelay, this);
},
rotate: function(){
var i = this.currentElement+1 < this.elements.length ? this.currentElement+1 : 0;
this.changeSlide(i);
},
changeSlide: function(i){
//$(this.options.navigationContainer).addClass('preload');
var cEl = this.currentElement;
this.elements[this.currentElement]['fx'].start({'opacity':0, 'left':1500});
this.elements[i]['fx'].set({'left':0});
this.elements[i]['fx'].start({'opacity':1, 'top':[500,0]}).chain(function(){
//$(this.options.navigationContainer).removeClass('preload');
}.bind(this));
this.elements[this.currentElement]['nav'].removeClass('selected');
this.elements[i]['nav'].addClass('selected');
this.currentElement = i;
}})
This is how it used on page:
<script language="javascript" type="text/javascript">
window.addEvent('domready', function(){
new MooSlider({
container:'topslider',
slides:'.slide',
navs:'.navigator ul li',
effectDuration: 1000,
fromTop:500,
topDist:500,
slideDelay: 3000 });
})
The page url is http://www.miltonwebsitedesign.ca
You can see slider on top of the page. Each slide consists of picture at the left and description at the right.
What I need is to make slides work the same way, but the left side picture must not appear current way, it needs to fade in, when the slide is loaded, not appear, but fade in.
Description text slides and then at the left picture appears.
The structure of each slide is:
<div class='slide'>
<div class="yjsquare">
<div class="yjsquare_in">
<img src='src here' alt=''/><h1>H1 text here</h1><p>description here</p>
</div>
</div>
</div>
Will be happy to hear solution. Thanks.
this class is not well written as in, it does not allow for events like onStart and onComplete to be passed. the logical approach would be to modify the changeSlide method to fire some events:
// add
var el = this.elements[i]['fx'];
this.fireEvent("start", el);
// use el as reference...
el.start({'opacity':1, 'top':[500,0]}).chain(function(){
//$(this.options.navigationContainer).removeClass('preload');
// add
this.fireEvent("complete", el);
}.bind(this));
Make sure that your class also implements Events (which in 1.12 is done like so, if memory serves):
MooSlider.implement(new Events);
you are using a mix of 1.12 and 1.2+ code which is odd. In any case, if you do have 1.2 (joomla usually is not prior to 1.6) then instead add this into the class declaration:
Implements: [Events],
this allows you to add some callback logic upon instigating the class:
new MooSlider({
container:'topslider',
slides:'.slide',
navs:'.navigator ul li',
effectDuration: 1000,
fromTop:500,
topDist:500,
slideDelay: 3000,
onStart: function(el) {
el.getElement("img").setOpacity(0); // hide it during animation
},
onComplete: function(el) {
el.fade(1); // fade it in when done
}
});
you should really implement Options too and use this.setOptions(options) instead of your current $extend.
p.s. the onStart and onComplete code callbacks are examples, you may need to tweak this to suit your html and UI preferences.

Categories