Vertical Sliding Accordion with jQuery

Today we want to share a slick and flexible vertical jQuery accordion with you. The main idea is to expand the accordion slices on click and show some more information. The other slices will become less opaque and squeezed. When navigating to the next slice using one navigation arrows, a new slice will slide in from the top or the bottom. Once a slice is open and we navigate, the subsequent slice will open on slide.

Today we want to share a slick and flexible vertical jQuery accordion with you. The main idea is to expand the accordion slices on click and show some more information. The other slices will become less opaque and squeezed. When navigating to the next slice using one navigation arrows, a new slice will slide in from the top or the bottom. Once a slice is open and we navigate, the subsequent slice will open on slide.

The beautiful portrait photography is by Images by Alan Turkus. Check out his Flickr Photostream. The images are licensed under the Creative Commons Attribution 2.0 Generic (CC BY 2.0) License.

We also use the jQuery Mousewheel Plugin by Brandon Aaron which allows us to navigate through the accordion using the mouse wheel.

The options for this plugin allow to customize the height and width, the number of slices visible, the height of an expanded slice and others. Let’s first see some examples before we explain each option.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

Examples

Take a look at all the examples (you can also navigate from them to all the other demos):

For the HTML structure we have the accordion container, the navigation spans and the wrapper with the slices:

<div id="va-accordion" class="va-container">
	<div class="va-nav">
		<span class="va-nav-prev">Previous</span>
		<span class="va-nav-next">Next</span>
	</div>
	<div class="va-wrapper">
		<div class="va-slice va-slice-1">
			<h3 class="va-title">Marketing</h3>
			<div class="va-content">
				<p>Henry Watson</p>
				<ul>
					<li><a href="#">About</a></li>
					<li><a href="#">Portfolio</a></li>
					<li><a href="#">Contact</a></li>
				</ul>
			</div>
		</div>
		<div class="va-slice va-slice-2">
			...
		</div>
	</div>
</div>

We give each slice a different class to define a background image for them. In the last fullscreen example we have color classes to assign a different background color to each slice.

Let’s take a look at the example with just 2 slices and a slower animation speed:

$('#va-accordion').vaccordion({
	expandedHeight	: 350,
	animSpeed		: 400,
	animOpacity		: 0.7,
	visibleSlices	: 2
});

The following options are available:

// the accordion's width
accordionW		: 1000,
// the accordion's height
accordionH		: 450,
// number of visible slices
visibleSlices	: 3,
// the height of a opened slice
// should not be more than accordionH
expandedHeight	: 350,
// speed when opening / closing a slice
animSpeed		: 250,
// easing when opening / closing a slice
animEasing		: 'jswing',
// opacity value for the collapsed slices
animOpacity		: 0.2,
// time to fade in the slice's content
contentAnimSpeed: 900,
// if this is set to false,
// we collapse any opened slice
// before sliding
savePositions	: true

If you, for example, would like to show a slice fully on expansion, using the whole height of the accordion container, then you set the expandedHeight to the same value like the accordionH (the height of the accordion). In our last example we used the full window width and height and also adjusted some style (see inline style in the example) in order to remove any margins:

$('#va-accordion').vaccordion({
	accordionW		: $(window).width(),
	accordionH		: $(window).height(),
	visibleSlices	: 5,
	expandedHeight	: 450,
	animOpacity		: 0.1,
	contentAnimSpeed: 100
});

We hope you like this accordion and find it useful!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

👾 Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!

Feedback 88

Comments are closed.
  1. @Mike,

    I was able to stop event propagation for links within div.va-slice by adding the following on line 414 of Mary’s code:

    $(‘div.va-slice a’).click(function(event) {
    event.stopPropagation();
    });

  2. how can i change that to disable the clickfunktion in the content area….i wanna click inside my opened content area..but it always closes, by clicking in the content area.
    can somebody help me?

  3. For some reason when I embed it as an XML gadget there are blank slices 2,4,6,8 and slices 1,3,5,7 are organized 1,2,3,4.

    So it appears – 1, blank, 2, blank, 3, blank…

    It appears fine when testing the html document.

    Any solution to this problem?

  4. This is lovely but unfortunately does not work in IE8 and below rendering the plugin mostly useless.

    I doubt this will be fixed any time soon since the issue has already been raised 3 times. Real shame

  5. @Aaron
    Is there any way to have one of the slides in the open state?

    I’m also interested into solving this problem, anyone? I’ve customized example5 for my needs, now the only thing left is the open tab on load. I would really appreciate some help.

    Outstanding work Mary Lou!

  6. So I put a thumbnail image in the first slice that clicks to a larger image using fancy box. The fancy box larger image comes up but at the same time the slider div closes that It was in. I want this to stay open. It does the same thing in the demo when you click on a link instead a slice. Basically I want clicking on the H3 title to be what opens and closes each slice. Is this possible? and how would I do it?
    Please advise,
    thank you,
    Allison

  7. @Allison
    I have also implemented lightbox but my slices stay open when I click on images, but it’s far more simple script then your fancybox.

  8. Nice exemples you’ve worked-out, yet I wonder whether it is possible to have the panels open “onhover” instead of “onclick”.
    Thx in advance for your guidance.

  9. I modified the jquery.vaccordion.js script to confine the navigation of the according to the title, arrows and scrolling of the mouse (i.e. nothing will happen when you click within a slice).

    (function($) {

    // Activate Slice Navigation
    var slicenav = false;

    // cache some values
    var cache = {
    idx_expanded : -1, // the index of the current expanded slice
    sliceH : 0, // the default slice’s height
    current : 0, // controls the current slider position
    totalSlices : 0 // total number of slices
    },
    aux = {
    // triggered when we click a slice. If the slice is expanded,
    // we close it, otherwise we open it..
    selectSlice : function( $el, $slices, $navNext, $navPrev, settings ) {

    return $.Deferred(
    function( dfd ) {

    var expanded = $el.data(‘expanded’),
    pos = $el.data(‘position’),

    itemHeight, othersHeight,

    $others = $slices.not( $el );

    if (slicenav )
    {

    // if it’s opened..
    if( expanded )
    {
    $el.data( ‘expanded’, false );
    cache.idx_expanded = -1;

    // the default values of each slices’s height
    itemHeight = cache.sliceH;
    othersHeight= cache.sliceH;

    // hide the content div
    $el.find(‘.va-content’).hide();

    // control the navigation buttons visibility
    if( aux.canSlideUp( $slices, settings ) )
    $navPrev.fadeIn();
    else
    $navPrev.fadeOut();

    if( aux.canSlideDown( $slices, settings ) )
    $navNext.fadeIn();
    else
    $navNext.fadeOut();
    }
    //if it’s closed..
    else
    {
    $el.data( ‘expanded’, true );
    cache.idx_expanded = $el.index();
    $others.data( ‘expanded’, false );

    //the current slice’s height
    itemHeight = settings.expandedHeight;
    //the height the other slices will have
    othersHeight= Math.ceil( ( settings.accordionH – settings.expandedHeight ) / ( settings.visibleSlices – 1 ) );

    // control the navigation buttons visibility
    if( cache.idx_expanded > 0 )
    $navPrev.fadeIn();
    else
    $navPrev.fadeOut();

    if( cache.idx_expanded < cache.totalSlices – 1 )
    $navNext.fadeIn();
    else
    $navNext.fadeOut();
    }
    slicenav = false;

    // the animation parameters for the clicked slice
    var animParam = {
    height : itemHeight + 'px',
    opacity : 1,
    top : ( pos – 1 ) * othersHeight + 'px'
    };

    // animate the clicked slice and also its title ()
    $el.stop()
    .animate( animParam, settings.animSpeed, settings.animEasing, function()
    {
    if( !expanded )
    $el.find(‘.va-content’).fadeIn( settings.contentAnimSpeed );
    })
    .find(‘.va-title’)
    .stop()
    .animate(
    {
    lineHeight : cache.sliceH + ‘px’
    }, settings.animSpeed, settings.animEasing );

    // animate all the others
    $others.each(function(i){
    var $other = $(this),
    posother= $other.data(‘position’),
    t;

    if( expanded )
    t = ( posother – 1 ) * othersHeight ;
    else {
    if( posother = cache.totalSlices )
    return false;
    else if( dir === -1 && cache.current === 0 )
    return false;

    if( dir === -1 && cache.current === 1 )
    $navPrev.fadeOut();
    else
    $navPrev.fadeIn();

    if( dir === 1 && cache.current + settings.visibleSlices === cache.totalSlices – 1 )
    $navNext.fadeOut();
    else
    $navNext.fadeIn();
    }
    else {
    if( dir === 1 && cache.idx_expanded === cache.totalSlices – 1 )
    return false;
    else if( dir === -1 && cache.idx_expanded === 0 )
    return false;

    if( dir === -1 && cache.idx_expanded === 1 )
    $navPrev.fadeOut();
    else
    $navPrev.fadeIn();

    if( dir === 1 && cache.idx_expanded === cache.totalSlices – 2 )
    $navNext.fadeOut();
    else
    $navNext.fadeIn();
    }

    var $currentSlice = $slices.eq( cache.idx_expanded ),
    $nextSlice,
    t;

    ( dir === 1 ) ? $nextSlice = $currentSlice.next() : $nextSlice = $currentSlice.prev();

    // if we cannot slide up / down, then we just call the selectSlice for the previous / next slice
    if( ( dir === 1 && !aux.canSlideDown( $slices, settings ) ) ||
    ( dir === -1 && !aux.canSlideUp( $slices, settings ) ) ) {
    aux.selectSlice( $nextSlice, $slices, $navNext, $navPrev, settings );
    return false;
    }

    // if we slide down, the top and position of each slice will decrease
    if( dir === 1 ) {
    cache.current++;
    t = ‘-=’ + cache.sliceH;
    pos_increment = -1;
    }
    else {
    cache.current–;
    t = ‘+=’ + cache.sliceH;
    pos_increment = 1;
    }

    $slices.each(function(i) {
    var $slice = $(this),
    pos = $slice.data(‘position’);

    // all closed or savePositions is false
    if( !settings.savePositions || cache.idx_expanded === -1 )
    $slice.stop().animate({top : t}, settings.animSpeed, settings.animEasing);
    else {
    var itemHeight, othersHeight;

    // if the slice is the one we should open..
    if( i === $nextSlice.index() ) {
    $slice.data( ‘expanded’, true );
    cache.idx_expanded = $slice.index();
    itemHeight = settings.expandedHeight;
    othersHeight = ( settings.accordionH – settings.expandedHeight ) / ( settings.visibleSlices – 1 );

    $slice.stop()
    .animate({
    height : itemHeight + ‘px’,
    opacity : 1,
    top : ( dir === 1 ) ? ( pos – 2 ) * othersHeight + ‘px’ : pos * othersHeight + ‘px’
    }, settings.animSpeed, settings.animEasing, function() {
    $slice.find(‘.va-content’).fadeIn( settings.contentAnimSpeed );
    })
    .find(‘.va-title’)
    .stop()
    .animate({
    lineHeight : cache.sliceH + ‘px’
    }, settings.animSpeed, settings.animEasing );
    }
    // if the slice is the one opened, lets close it
    else if( $slice.data(‘expanded’) ){
    // collapse

    $slice.data( ‘expanded’, false );
    othersHeight = ( settings.accordionH – settings.expandedHeight ) / ( settings.visibleSlices – 1 );

    $slice.stop()
    .animate({
    height : othersHeight + ‘px’,
    opacity : settings.animOpacity,
    top : ( dir === 1 ) ? ‘-=’ + othersHeight : ‘+=’ + settings.expandedHeight
    }, settings.animSpeed, settings.animEasing )
    .find(‘.va-title’)
    .stop()
    .animate({
    lineHeight : othersHeight + ‘px’
    }, settings.animSpeed, settings.animEasing )
    .end()
    .find(‘.va-content’)
    .hide();
    }
    // all the others..
    else {
    $slice.data( ‘expanded’, false );
    othersHeight = ( settings.accordionH – settings.expandedHeight ) / ( settings.visibleSlices – 1 );

    $slice.stop()
    .animate({
    top : ( dir === 1 ) ? ‘-=’ + othersHeight : ‘+=’ + othersHeight
    }, settings.animSpeed, settings.animEasing );
    }
    }
    // change the slice’s position
    $slice.data().position += pos_increment;
    });
    },
    canSlideUp : function( $slices, settings ) {
    var $first = $slices.eq( cache.current );

    if( $first.index() !== 0 )
    return true;
    },
    canSlideDown : function( $slices, settings ) {
    var $last = $slices.eq( cache.current + settings.visibleSlices – 1 );

    if( $last.index() !== cache.totalSlices – 1 )
    return true;
    }
    },
    methods = {
    init : function( options ) {

    if( this.length ) {

    var settings = {
    // the accordion’s width
    accordionW : 825,
    // the accordion’s height
    accordionH : 400,
    // number of visible slices
    visibleSlices : 4,
    // the height of a opened slice
    // should not be more than accordionH
    expandedHeight : 300,
    // speed when opening / closing a slice
    animSpeed : 250,
    // easing when opening / closing a slice
    animEasing : ‘jswing’,
    // opacity value for the collapsed slices
    animOpacity : 0.2,
    // time to fade in the slice’s content
    contentAnimSpeed: 900,
    // if this is set to false, then before
    // sliding we collapse any opened slice
    savePositions : true
    };

    return this.each(function() {

    // if options exist, lets merge them with our default settings
    if ( options ) {
    $.extend( settings, options );
    }

    var $el = $(this),
    // the accordion’s slices
    $slices = $el.find(‘div.va-slice’),
    // the navigation buttons
    $navNext = $el.find(‘span.va-nav-next’),
    $navPrev = $el.find(‘span.va-nav-prev’),
    $slicetitle = $el.find(‘h3.va-title’);

    // each slice’s height
    cache.sliceH = Math.ceil( settings.accordionH / settings.visibleSlices );

    // total slices
    cache.totalSlices = $slices.length;

    // control some user config parameters
    if( settings.expandedHeight > settings.accordionH )
    settings.expandedHeight = settings.accordionH;
    else if( settings.expandedHeight <= cache.sliceH )
    settings.expandedHeight = cache.sliceH + 50; // give it a minimum

    // set the accordion's width & height
    $el.css({
    width : settings.accordionW + 'px',
    height : settings.accordionH + 'px'
    });

    // show / hide $navNext
    if( settings.visibleSlices < cache.totalSlices )
    $navNext.show();

    // set the top & height for each slice.
    // also save the position of each one.
    // as we navigate, the first one in the accordion
    // will have position 1 and the last settings.visibleSlices.
    // finally set line-height of the title ()
    $slices.each(function(i){
    var $slice = $(this);
    $slice.css({
    top : i * cache.sliceH + ‘px’,
    height : cache.sliceH + ‘px’
    }).data( ‘position’, (i + 1) );
    })
    .children(‘.va-title’)
    .css( ‘line-height’, cache.sliceH + ‘px’ );

    // click event
    $slicetitle.bind(‘click’, function() {
    slicenav = true;
    });

    // click event
    $slices.bind(‘click.vaccordion’, function(e) {
    // only if we have more than 1 visible slice.
    // otherwise we will just be able to slide.
    if( settings.visibleSlices > 1 ) {
    var $el = $(this);
    aux.selectSlice( $el, $slices, $navNext, $navPrev, settings );
    }
    });

    // navigation events
    $navNext.bind(‘click.vaccordion’, function(e){
    slicenav = true;
    aux.navigate( 1, $slices, $navNext, $navPrev, settings );
    });

    $navPrev.bind(‘click.vaccordion’, function(e){
    slicenav = true;
    aux.navigate( -1, $slices, $navNext, $navPrev, settings );
    });

    // adds events to the mouse
    $el.bind(‘mousewheel.vaccordion’, function(e, delta) {
    slicenav = true;
    if(delta > 0) {
    aux.navigate( -1, $slices, $navNext, $navPrev, settings );
    }
    else {
    aux.navigate( 1, $slices, $navNext, $navPrev, settings );
    }
    return false;
    });

    });
    }
    }
    };

    $.fn.vaccordion = function(method) {
    if ( methods[method] ) {
    return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === ‘object’ || ! method ) {
    return methods.init.apply( this, arguments );
    } else {
    $.error( ‘Method ‘ + method + ‘ does not exist on jQuery.vaccordion’ );
    }
    };

    })(jQuery);

    • Why did you just paste that whole script in here? You could have at least linked to a js file on a server. If anyone copies and pastes this script into their code, they will pick up tons of formatted characters, mostly quotation marks but also minus symbols. Even after fixing those things, I still couldn’t get it to work.

  10. @Erik van Dyck
    In file jquery.vaccordion.js modify line #371 to following:

    $slices.bind(‘mouseenter.vaccordion’, function(e) {

  11. Hi, i really liked this slide, but i need autoplay too, because i’m implementing in my website and him should be more dynamic. So anyone can help me?

  12. I wanna start viewing slider not from thirst slide ((so i wanna hide thirs n slides). How can i do this? Ho can i scroll slider from my script!? I try somthing like
    var a = setInterval(function(){
    $(‘span.va-nav-prev’).click();
    if (++m == elementNumber) clearInterval(a);
    },400);

    But it’s work VERY BAD!
    Help me pleas! I can’t more(

  13. Please help!
    What is the way to disable closing of the slice when i click on an element? I need to put some audio files (with JQuery media plugin). Right now it just closes the slice.

    What is the best method?
    Thanx in advance

  14. To David Wathen!

    Your version of jquery.vaccordion.js doesn’t work when i try to substitute the original. Could you explain in detail what you did to stop closing the slice when clicking on active elements???

  15. Hi!

    First off, this is beautiful. Love all your stuff.

    I’m implementing this script (example 2) on my site and unfortunately it does not work properly in IE8 (all the other examples do work, except that one). It gets stuck after expanding one of the slices and one can’t navigate anymore at all.

    Any ideas how to fix it?

    Thanks a bunch!

  16. Why does it close when you click on the content of accordiion. It is not at all useful with the click on close. Please fix it

  17. Can i have the slices be transparent so that the website’s background may be visible 🙁 HELP NEEDED!

  18. Estimados los felicito, estoy adaptando el acordeon, pero quisiera que al hacer click en el contenido, este no se cierra. Segui el codigo que coloco David Wathen, pero revisando le falta codigo, con lo cual no resulto.

    Ojala me respondan.

    Saludos

    Cristian

  19. really Awsome tutuorial….but I have question man…is there any option to have one slide open by default….can u help me with this…what i mean is I have 4 slides when i enter or reload the page i want to be opened the 2nd slide by default, plz help me

  20. Hi, great tutorial and really nice design… I was wandering if I can use your code to build my website, or is this just for demo purpose? tnx

  21. I am also interested in the option to have a slider open as a default. I require the ‘Welcome’ section of a site to be open as a default initially, with the rest minimised. Is this possible?

  22. Thanks for this accordeon! It works beautifully.
    There is only one thing I cannot manage to do.

    I have all my slices, wrapper and container with a transparent background, so I can to see my background image when the accordeon is closed.
    Then I only the titles of each slice.
    Tha works great.

    I would like to put these titles very close to each other so , when you see the accordeon closed, you see a block of text with the lines superposed on the top and bottom borders. So then I see a block of letters.

    The problem is that when I bring them close, even though ALL the backgrounds are transparent, they do not superspose but cut each other.
    Is there any way I can do this?

    Thanks!

  23. To sat it more clearly……I want my ‘va.title’ classes to overlay on each other when my accordeon is closed and collapsed….
    maybe this is more clear…
    Is this possible?
    Thanks!

  24. Hi,

    Is it possible to add a scrollbar on the right ?
    If it’s possible, is there an easy way to do that ?

    Thanks

  25. For those trying to set it to open a slice on load, you can try

    $(‘.va-slice-1’).trigger(‘click’);

    that will display the contents of va-slice-1

    also, I have mine so that instead of click, mine triggers on mouseenter (similar to hover), just change line 371 or the line that reads in the js file.

    $slices.bind('click.vaccordion', function(e) {

    with

    $slices.bind('mouseenter.vaccordion', function(e) {