Building a dynamic category tree with get_categories()

As there doesn’t seem to be an adequate and still actively developed WordPress plug-in that allows me to display a collapsible category tree without having to use widgets (I personally dislike widgets), I decided to write one on my own.

I wrote this code:

Read More
<script type="text/javascript">
    function toggleCatDiv(id) {
        if (jQuery("#catTogglerDiv-"+id).is(":visible")) {
            jQuery("#catToggler-"+id).innerHTML="►";
        }
        else {
            jQuery("#catToggler-"+id).innerHTML="▼";
        }

        jQuery("#catTogglerDIV-"+id).slideToggle("normal");
    }
</script>
<?php
$args = array('orderby' => 'name', 'parent' => 0 );
$categories = get_categories( $args );

foreach ( $categories as $category ) {

?>
<span style="cursor:pointer" id="catToggler-<?php echo $category->cat_ID; ?>" onclick="toggleCatDiv('<?php echo $category->cat_ID; ?>')">►</span> <a href="<?php echo get_category_link( $category->term_id ); ?>"><?php echo $category->name;?></a><br />
<div id="catTogglerDIV-<?php echo $category->cat_ID; ?>" style="margin-left:3em;visibility:hidden">
    <?php
    $args = array(
      'child_of'     => $category->cat_ID
    );
    $subcats = get_categories($args);
    foreach ( $subcats as $subcat ) {
        <a href="<?php echo get_category_link( $subcat->term_id ); ?>"><?php echo $subcat->name;?></a>
    }
    ?>
</div>
<?php

}
?>

As you might see, it’s intended to grab the list of “main categories” and display a list of subcategories when clicking the arrow on each of them.

Now clicking the arrow does nothing (not even a JS error), and only 2 of 4 main categories are actually shown. Why?

Related posts

Leave a Reply

1 comment

  1. Problems with your code:

    • You have catTogglerDIV and catTogglerDiv. Give meaningful and consistent names to your functions and variables, it’s very easy to get lost when many of them have similar names.

    • Some categories are not showing up because show_empty is true by default: Function_Reference/get_categories.

    • You are running the sub-categories foreach even if there are no sub-cats.

    • InnerHTML was not working for me, changed for html().

    The widget I like the most is the Text Widget. Why? Because we can put Shortcodes there and execute any kind of output through our functions. Normally, I don’t do Widgets, I do Shortcodes and use this technique.

    The following filter is needed to enable this feature:

    add_filter( 'widget_text', 'do_shortcode', 11 );
    

    And a working example:

    add_shortcode( 'cat_toggle', 'shortcode_so_19260684' );
    
    function shortcode_so_19260684()
    {
        wp_enqueue_script('jquery'); // Load jQuery only when shortcode present.
    
        # Start the output string
        $return = '
        <script type="text/javascript">
            function doToggle( id ) 
            {
                if ( jQuery( "#subCategories-" + id ).is( ":visible" ) )
                    jQuery( "#catToggler-" + id ).html( "‣" );
                else
                    jQuery( "#catToggler-" + id ).html( "▾" );
    
                jQuery( "#subCategories-" + id ).slideToggle( "normal" );
            }
        </script>';
    
        $categories = get_categories( array(
            'orderby'    => 'name', 
            'parent'     => 0, 
            'hide_empty' => false 
        ));
    
        foreach ( $categories as $category ) 
        {
            $subcats = get_categories( array(
                'child_of'   => $category->cat_ID, 
                'hide_empty' => false
            ));
            # Maybe useful
            if( !$subcats )
                $change_the_folding_indicator = 'ø';
    
            $return .= sprintf(
                '<span style="cursor:pointer" id="catToggler-%1$s" onclick="doToggle('%1$s')">‣</span> <a href="%2$s">%3$s</a><br />
                <div id="subCategories-%1$s" style="margin-left:3em;display:none">',
                $category->cat_ID,
                get_category_link( $category->term_id ),
                $category->name
            );
    
            # Add this level only if subcats not empty
            if( $subcats ) 
            {
                foreach ( $subcats as $subcat ) 
                {
                    $return .= sprintf(
                        '<a href="%s">%s</a>',
                        get_category_link( $subcat->term_id ),
                        $subcat->name
                    );
                }
            }       
            $return .= '</div>';
        }
        return $return;
    }