Search code examples
javascriptjqueryslidetoggle

How to sync slideToggle() elements so they expand/collapse all together regardless of current state?


I'm trying to show/hide sections using triggers based on classes. So it's easy to throw the trigger class on any element and it will slideToggle the next show/hide element class in the DOM.

The first part works fine. Now I'm adding a trigger to expand/collapse ALL elements. Trouble is, I can't figure out how to sync their states.

For example: I click on 3 of 8 elements so they slide open and become visible, the other 5 are still closed... Now when I click my expand/collapse all trigger, using slideToggle just switches their states so I end up with 3 closed and 5 open.

How do I get their states to sync regardless of what their current state is?

I've been trying to figure out the conditionals to check if the trigger has the opened or close class on it, then toggle the next element, but I've only made a mess so far.

Here's my code:

jQuery( document ).ready( function( $ ) {

    // The element to hide/reveal
    $( '.bodhi-hide-reveal' ).hide();

    // The trigger to hide/reveal
    $( '.bodhi-reveal-trigger' ).click( function( e ) {

        e.preventDefault();

        // Target only the next element to hide/reveal and toggle it
        $( this ).next( '.bodhi-hide-reveal' ).slideToggle();

        // Toggle the trigger class
        $( '.bodhi-reveal-trigger' ).toggleClass( 'opened closed' );

    });

    // Expand/collapse all button
    $( '.expand-collapse-all' ).click( function( e ) {

        e.preventDefault();

        // Find all hide/reveal elements and toggle them all together
        $( 'body' ).find( '.bodhi-hide-reveal' ).slideToggle();

    });

});

Solution

  • Just a class named "opened" is enough to detect if the next div is open or close. So I write an IF/ELSE block to decide whether to slideUp or SlideDown. Also you have to decide what would you do when some of divs are expanded? Do you want to collapse all or expand all? I prefer to collapse all first. So I search to find an opened one (using array length) and if I find it, I collapse all divs, elsewhere I expand all:

    Also there are some inefficient selectors like $( 'body' ).find(). I also replace those selectors with efficient ones:

    jQuery( document ).ready( function( $ ) {
    
    // The element to hide/reveal
    $('.bodhi-hide-reveal').hide();
    $('.bodhi-reveal-trigger').removeClass("opened");
    
    // The trigger to hide/reveal
    $('.bodhi-reveal-trigger').click( function( e ) {
    
        e.preventDefault();
    
        // Target only the next element to hide/reveal and toggle it
        $(this).next('.bodhi-hide-reveal').slideToggle();
    
        // Toggle the trigger class
        $(this).toggleClass('opened');
    
    });
    
    // Expand/collapse all button
    $('.expand-collapse-all').click( function( e ) {
    
        e.preventDefault();
    
        //Check if there is at least one open div:
    
        if ($('.bodhi-reveal-trigger.opened').length){
            $('.bodhi-reveal-trigger').removeClass("opened")
            $('.bodhi-hide-reveal').stop().slideUp();
        }
    
        else {
            $('.bodhi-reveal-trigger').addClass("opened")
            $('.bodhi-hide-reveal').stop().slideDown();        
        }
    
    });
    
    });