WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

The newest version of Bootstrap (v3.0) adds a new List Group component which has the following structure:

<ul class="list-group">
  <li class="list-group-item">Cras justo odio</li>
  <li class="list-group-item">Dapibus ac facilisis in</li>
  <li class="list-group-item">Morbi leo risus</li>
  <li class="list-group-item">Porta ac consectetur ac</li>
  <li class="list-group-item">Vestibulum at eros</li>
</ul>
  1. I would like to be able to add a class to the ul (i.e. <ul class="list-group">)
  2. I would like to style my Category sidebar widget to support this new component, but as you see, this requires classes on each li item.

In reading some similar posts, one option I found is to use jQuery to add the class to each li, but I am concerned about the dreaded FOUC.

Is there some WordPress function that gets me to my goal?

Please advise,

Update:

I was able to add classes to the individual li's by creating a Custom Walker which extends Walker_Category (see code below), but this still does not get me to the ul which also needs a class added (eg <ul class="list-group">).

class Walker_Category_BS extends Walker_Category {
    function start_el( &$output, $category, $depth = 0, $args = array() ) {
        extract($args);

        $cat_name = esc_attr( $category->name );
        $cat_name = apply_filters( 'list_cats', $cat_name, $category );
        $link = '<a href="' . esc_url( get_term_link($category) ) . '" ';
        if ( $use_desc_for_title == 0 || empty($category->description) )
            $link .= 'title="' . esc_attr( sprintf(__( 'View all posts filed under %s' ), $cat_name) ) . '"';
        else
            $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"';
        $link .= '>';
        $link .= $cat_name . '</a>';

        if ( !empty($feed_image) || !empty($feed) ) {
            $link .= ' ';

            if ( empty($feed_image) )
                $link .= '(';

            $link .= '<a href="' . esc_url( get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) ) . '"';

            if ( empty($feed) ) {
                $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';
            } else {
                $title = ' title="' . $feed . '"';
                $alt = ' alt="' . $feed . '"';
                $name = $feed;
                $link .= $title;
            }

            $link .= '>';

            if ( empty($feed_image) )
                $link .= $name;
            else
                $link .= "<img src='$feed_image'$alt$title" . ' />';

            $link .= '</a>';

            if ( empty($feed_image) )
                $link .= ')';
        }

        if ( !empty($show_count) )
            $link .= ' (' . intval($category->count) . ')';

        if ( 'list' == $args['style'] ) {
            $output .= "\t<li";
            $class = 'list-group-item cat-item cat-item-' . $category->term_id;
            if ( !empty($current_category) ) {
                $_current_category = get_term( $current_category, $category->taxonomy );
                if ( $category->term_id == $current_category )
                    $class .=  ' current-cat';
                elseif ( $category->term_id == $_current_category->parent )
                    $class .=  ' current-cat-parent';
            }
            $output .=  ' class="' . $class . '"';
            $output .= ">$link\n";
        } else {
            $output .= "\t$link<br />\n";
        }
    } /* end start_el */

} /* end Walker_Category_BS */

Update 02:

After viewing default-widgets.php in the core, I decided to create a new widget (WP_Widget_Categories_BS, see code below) wherein I basically, copied all the code from the default category widget and simply modified the the UL to add the necessary class.

<?php 

/**
 * Categories widget class
 *
 * @since 2.8.0
 */
class WP_Widget_Categories_BS extends WP_Widget {

    function __construct() {
        $widget_ops = array( 'classname' => 'widget_categories_bs', 'description' => __( "A list or dropdown of categories for Bootstrap 3.0" ) );
        parent::__construct('categories', __('Boostrap Categories'), $widget_ops);
    }

    function widget( $args, $instance ) {
        extract( $args );

        $title = apply_filters('widget_title', empty( $instance['title'] ) ? __( 'Categories' ) : $instance['title'], $instance, $this->id_base);
        $c = ! empty( $instance['count'] ) ? '1' : '0';
        $h = ! empty( $instance['hierarchical'] ) ? '1' : '0';
        $d = ! empty( $instance['dropdown'] ) ? '1' : '0';

        echo $before_widget;
        if ( $title )
            echo $before_title . $title . $after_title;

        $cat_args = array('orderby' => 'name', 'show_count' => $c, 'hierarchical' => $h);
        if ( $d ) {
            $cat_args['show_option_none'] = __('Select Category');
            wp_dropdown_categories(apply_filters('widget_categories_dropdown_args', $cat_args));
?>

<script type='text/javascript'>
/* <![CDATA[ */
    var dropdown = document.getElementById("cat");
    function onCatChange() {
        if ( dropdown.options[dropdown.selectedIndex].value > 0 ) {
            location.href = "<?php echo home_url(); ?>/?cat="+dropdown.options[dropdown.selectedIndex].value;
        }
    }
    dropdown.onchange = onCatChange;
/* ]]> */
</script>

<?php
        } else {
?>
        <ul class="list-group">
<?php
        $cat_args['title_li'] = '';
        wp_list_categories(apply_filters('widget_categories_args', $cat_args));
?>
        </ul>
<?php
        }

        echo $after_widget;
    }

    function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = strip_tags($new_instance['title']);
        $instance['count'] = !empty($new_instance['count']) ? 1 : 0;
        $instance['hierarchical'] = !empty($new_instance['hierarchical']) ? 1 : 0;
        $instance['dropdown'] = !empty($new_instance['dropdown']) ? 1 : 0;

        return $instance;
    }

    function form( $instance ) {
        //Defaults
        $instance = wp_parse_args( (array) $instance, array( 'title' => '') );
        $title = esc_attr( $instance['title'] );
        $count = isset($instance['count']) ? (bool) $instance['count'] :false;
        $hierarchical = isset( $instance['hierarchical'] ) ? (bool) $instance['hierarchical'] : false;
        $dropdown = isset( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false;
?>
        <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e( 'Title:' ); ?></label>
        <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /></p>

        <p><input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('dropdown'); ?>" name="<?php echo $this->get_field_name('dropdown'); ?>"<?php checked( $dropdown ); ?> />
        <label for="<?php echo $this->get_field_id('dropdown'); ?>"><?php _e( 'Display as dropdown' ); ?></label><br />

        <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('count'); ?>" name="<?php echo $this->get_field_name('count'); ?>"<?php checked( $count ); ?> />
        <label for="<?php echo $this->get_field_id('count'); ?>"><?php _e( 'Show post counts' ); ?></label><br />

        <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('hierarchical'); ?>" name="<?php echo $this->get_field_name('hierarchical'); ?>"<?php checked( $hierarchical ); ?> />
        <label for="<?php echo $this->get_field_id('hierarchical'); ?>"><?php _e( 'Show hierarchy' ); ?></label></p>
<?php
    }

} // end WP_Widget_Categories_BS

Combined with a Custom Walker I created (Walker_Category_BS) I now have what I wanted.

Analysis

Is this the best way to do it? Don't know as I have have received zero feedback so far, and this is the first time i've done this (hence the question), but ...it works! I could use a review.

Debug Warning

Concerning my Custom Category Walker Walker_Category_BS, i'm seeing this message

"Strict standards: Declaration of Walker_Category_BS::start_el() should be compatible with Walker::start_el(&$output, $object, $depth = 0, $args = Array, $current_object_id = 0) in C:\wamp\www\mysite\wp-content\themes\mytheme\assets\inc\Walker_Category_BS.php"

It appears to be a warning, of some sort.

share|improve this question
    
Please move your solution to the answer, that is more in line with site's mechanics. – Rarst Sep 12 '13 at 21:23
up vote 4 down vote accepted

Update: 12/19/15 : Here's a plugin on Github that I developed (using the method in answer I provided below) adding support for changing all widget to bootstrap components/styling.


Wordpress Widgets Bootstrapped



Original Answer

I understand not wanting to use javascript, but it seems like overkill in my opinion to completely create a new widget just for the list-group class to be added to the widget <ul> html tag. If you look at what the list-group class actually does you'll notice all it's doing is removing default browser padding and adding some bottom margins.

.list-group {
    padding-left: 0;
    margin-bottom: 0;
}

Most likely you won't even want the bottom margins there because you should be adding the default margins to the .widget or similar sidebar before_widget class in your themes css.

For example: Setting default sidebar widget margins

.sidebar-primary .widget {
    margin-bottom: 20px;
}

So essentially, the only real benefit the class is giving you is removing the default browser padding for lists. This is also something (in my opinion) you should probably be doing in your css since this how bootstrap is handling it anyway.

.sidebar-primary .widget.widget_categories {
    padding-left: 0;
}

As for the the list-group-item classes on the <li> elements we can use the wp_list_categories filter for that. And while we're at it we might as well change the styling for count as well to bootstraps formatting...

function bs_categories_list_group_filter ($variable) {
   $variable = str_replace('<li class="cat-item cat-item-', '<li class="list-group-item cat-item cat-item-', $variable);
   $variable = str_replace('(', '<span class="badge cat-item-count"> ', $variable);
   $variable = str_replace(')', ' </span>', $variable);
   return $variable;
}
add_filter('wp_list_categories','bs_categories_list_group_filter');

If you must have list-group added with php and don't want to use css or javascript, you do have a few other options...

Option 1 - Use output buffering in your theme templates:

ob_start();
dynamic_sidebar( 'registered-sidebar-name' );
$sidebar_output = ob_get_clean();
echo apply_filters( 'primary_sidebar_filter', $sidebar_output );

Then in your function.php you would use the primary_sidebar_filter and use regex to replace the default html for

function bs_add_list_group_to_cats( $sidebar_output ) {

    // Regex goes here...
    // Needs to be a somewhat sophisticated since it's running on the entire sidebar, not just the categories widget. 

    $regex = "";
    $replace_with = "";
    $widget_output = preg_replace( $regex , $replace_with , $widget_output );    

    return $sidebar_output;

}
add_filter( 'primary_sidebar_output', 'bs_add_list_group_to_cats' );

Option 2 - Use output buffering in a plugin / outside of your templates:

This is probably the best way to do this as it gives you a lot more freedom to customize any widgets. This can either be added as a plugin thanks to Philip Newcomer or directly to your functions.php with this code.

Then to use the new callback function for your category widget filtering (to add bootstrap styling) you'd add this to your functions.php:

function wpse_my_widget_output_filter( $widget_output, $widget_type, $widget_id ) {
    if ( 'categories' == $widget_type ) {
        $widget_output = str_replace('<ul>', '<ul class="list-group">', $widget_output);
        $widget_output = str_replace('<li class="cat-item cat-item-', '<li class="list-group-item cat-item cat-item-', $widget_output);
        $widget_output = str_replace('(', '<span class="badge cat-item-count"> ', $widget_output);
        $widget_output = str_replace(')', ' </span>', $widget_output);
    }      
      return $widget_output;
}
add_filter( 'widget_output', 'my_widget_output_filter_footer', 10, 3 ); 
share|improve this answer
1  
Here is an plugin I wrote yesterday including support for all wordpress widgets utilizing Philip's awesome code. And here's a demo of them in action: wordpress bootstrap widgets demo – Bryan Willis Dec 14 '15 at 4:52
    
I really like your solution. Thanks for sharing. – sleeper Dec 16 '15 at 4:08

what about creating a seperate sidebar for this widget and adding class using register_sidebar funciton ?

register_sidebar(array(
    'name' => 'First_sidebar',
    'id' => 'sidebar-1',
    'class'         => 'ul-class-name',
    'before_widget' => '<div class="well">',
    'after_widget' => '</div>',
    'before_title' => '<h4>',
    'after_title' => '</h4>'
));
share|improve this answer
3  
I created a new sidebar and added a class to it. Nothing showed in the HTML. Also, after looking at several tutorials (and several books) not one even mentions the class attribute. The codex states "class - CSS class name to assign to the widget HTML (default: empty)." but then the example provided does not even use that parameter. At this point, I have NO IDEA what this attribute actually does. ...go figure. – sleeper Sep 9 '13 at 7:28
    
It appears that the class parameter only shows on the backend and only on the containing div, not the UL. – sleeper Sep 9 '13 at 9:54

Update: I fixed the debug warning and everything appears to be working. In the absence of any other comments, I will accept my own solution outlined in the original questions updates.

share|improve this answer
    
I have been (and I am still) in this situation too. I d try your solution but it is just ridiculous it has to be so damn complicated... I understand that in principle it is unnecessary because you can use the class of the sidebar to reference the <ul> but it became a nightmare when it comes to bootstrap. – toto_tico Mar 11 '14 at 9:23
    
@sleeper you should post the updated fix for your accepted answer. Also, check out my answer below, I think it will help both of you out if you're still looking for a solution to this. – Bryan Willis Dec 12 '15 at 20:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.