post: WP_Dropdown_Categories And Parent Optgroup Elements


WP_Dropdown_Categories And Parent Optgroups

Working With WordPress



PHP Taxonomy WordPress

WP_Dropdown_Categories is a well used core WordPress function used to display or retrieve a dropdown list of categories as formatted HTML.

It is highly customisable, with multiple settings for layout, category & taxonomy types etc. There is however no current method whereby top level parent categories can be set as non-selectable optgroup select options, leaving the child options as selectable.

Here I show how to create a custom Walker Class, and use this as a custom setting to the wp_dropdown_categories function to set top level parent categories as optgroups.

This was something that was needed for a recent project where a couple of post meta data fields for a custom post type coming from an external source via a submitted form were directly input into the database.

The form fields in each case were 2 level multi-option select fields with a strict parent > multi-child structure. One of these was UK countries and their county counterparts. These were held in a hierarchy taxonomy linked to the post type and then retrieved and displayed in the form. The corresponding field in the WP admin post edit screen were displayed in select fields inside custom meta boxes overriding the normal multi-select checkbox layout.

The field was displayed using the generic wp_dropdown_categories() function, which has a number of arguments for manipulating the layout including a ‘hierarchy’ argument for taxonomies which are non-tag structure and allow parent > child relationships.

The only problem is that it displays these in the parent > child layout but both parent and child elements are selectable options. This was not what was required. The only selectable fields needed to be the county options. As there is no current way to set the parent country option as non-selectable – the easiest, and best solution for this being setting is as an optgroup – then a bit of code tinkering was required. The solution turned out to be relatively straightforward.

The codex documentation for the wp_dropdown_categories() function details the available arguments: These though are the most common ones and aren’t exclusive. Another argument is for a custom Walker Class for structuring the option elements. The function uses by default the Walker_CategoryDropdown class, an extension of the parent Walker Class, to structure the select element. By extending this and manipulating the option elements we can do what is needed…

First the extended Walker Class.

 * Parent level optgroup walker extension
class WP_Walker_Optgroup extends Walker_CategoryDropdown {

    var $optgroup = false;

    function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {

        $pad = str_repeat(' ', $depth * 3);
        $cat_name = apply_filters('list_cats', $category->name, $category);
        // set parent optgroup            
        if (0 == $depth) {
            $this->optgroup = true;
            $output .= '<optgroup class="level-$depth" label="' . $cat_name . '" >';           
        } else {
            $this->optgroup = false;
            $output .= '<option class="level-' . $depth. '" value="' . $category->term_id . '"';
            if ( $category->term_id == $args['selected'] ) {
                $output .= ' selected="selected"';
            $output .= '>' . $pad.$cat_name;
            if ( $args['show_count'] ) {
                $output .= '  ('. $category->count .')';
            $output .= "</option>";

    function end_el( &$output, $object, $depth = 0, $args = array() ) {
        if ( 0 == $depth && true == $this->optgroup ) {
            $output .= '</optgroup>';

And the arguments to the function.

$args = array(
    'hierarchical'   => 1, 
    'taxonomy'       => $taxonomy,
    'walker'         => new WP_Walker_Optgroup
wp_dropdown_categories( $args );             

The dropdown now automatically converts the parent top-level elements into optgroups, while leaving the child elements as options.

It would be a useful addition to the wp_dropdown_categories() function to have an inbuilt argument, for example parent_optgroup = true (default: false). Which would set the top-level element – with depth 0 – as an optgroup and leave all other child elements (depth > 0 ) as selectable option elements. Maybe I’ll add it to the WordPress Trac as an enhancement if not already there.

This has been logged as a WordPress enhancement:

comments powered by Disqus