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>
- I would like to be able to add a class to the
ul
(i.e.<ul class="list-group">
) - 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 .= ">$linkn";
} 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:wampwwwmysitewp-contentthemesmythemeassetsincWalker_Category_BS.php”
It appears to be a warning, of some sort.
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.
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.Most likely you won’t even want the bottom margins there because you should be adding the default margins to the
.widget
or similar sidebarbefore_widget
class in your themes css.For example: Setting default sidebar widget margins
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.
As for the the
list-group-item
classes on the<li>
elements we can use thewp_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…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:
Then in your
function.php
you would use theprimary_sidebar_filter
and use regex to replace the default html forOption 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
:what about creating a seperate sidebar for this widget and adding class using
register_sidebar
funciton ?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.