Try Tuts+ Premium, Get Cash Back!
How to Use Radio Buttons With Taxonomies

How to Use Radio Buttons With Taxonomies

Tutorial Details
  • Program: WordPress
  • Version: All
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 minutes

WordPress’ custom taxonomy features are fantastic, allowing you to organize your posts in various taxonomies, with all the hard work done for you. However, it can also be a bit constraining. When editing your posts, your taxonomy terms have their own metabox, and they appear either as a checkbox list (for hierarchal taxonomies) or as a tag cloud (for non-hierarchal taxonomies). Those are your two choices.

This can present a problem when you want to ensure only one term can be selected for each post. Of course, you could hook into the save_post hook and remove any ‘excess’ terms, but this is not particularly user-friendly and certainly doesn’t provide a great user interface. Sometimes, it would just be more aesthetically desirable to present your taxonomies in a different way. This article will show you how to do just that, and all the code we talk about should be added to the functions.php file in your theme. We’ll focus on radio buttons, but you could use any other input method, for example a drop-down menu.


Step 1 Remove the Default Taxonomy Metabox

WordPress automatically produces the taxonomy metabox, so our first job is to remove it so we can produce our own in its place. I shall assume our taxonomy name is ‘mytaxonomy’ (if you wanted to alter WordPress’ tags or categories metabox, you would replace this with ‘category’ or ‘post_tag’).

To remove the metabox we will use remove_meta_box, which should be called from inside a function hooked onto the admin_menu. The remove_meta_box accepts three arguments.

  1. ID: this the id attribute given to the div element containing the metabox. Usually this would be ‘mytaxonomydiv’ for hierarchal taxonomies, or ‘tagsdiv-mytaxonomy’ for non-hierarchal ones.
  2. Post type: the post type the metabox appears for (e.g. ‘post’ or ‘page’ etc). If your metabox appears for several different post types, you will need to call the remove_meta_box function for each one.
  3. Context: Normal, advanced, or side.
add_action( 'admin_menu', 'myprefix_remove_meta_box');
function myprefix_remove_meta_box(){
   remove_meta_box('mytaxonomydiv', 'post', 'normal');
}

Step 2 Add Your Own Metabox

Here we hook onto the aptly named add_meta_boxes hook with a function that will add our metabox. To do that the function will call add_meta_box which takes quite a few arguments, among which are:

  1. ID: Same as the above, give it anything unique.
  2. Title: The title for the metabox.
  3. Callback: The name of the function that will produce our metabox’s innards.
  4. Post type: Same as the above. Again you’ll need to call this function for each post type separately.
  5. Context: Same as the above.
  6. Priority: The priority within the context where the boxes should show.
 
//Add new taxonomy meta box
 add_action( 'add_meta_boxes', 'myprefix_add_meta_box');
 function myprefix_add_meta_box() {
     add_meta_box( 'mytaxonomy_id', 'My Radio Taxonomy','myprefix_mytaxonomy_metabox','post' ,'side','core');
 }

  function myprefix_mytaxonomy_metabox( $post ) {
     echo 'This is my taxonomy metabox';
  }

Together, the above should remove the default metabox and replace it with your own, which currently does nothing but display the message “This is my taxonomy metabox”. The next step is to change the callback function to display what we want.


Step 3 Producing the Radio Buttons

We want our metabox to look and behave as much like the default metaboxes as is possible. Delving into the WordPress core files, you’ll find the place where a metabox’s insides are produced, here. Our custom function below will mimic the core function, but with some changes to how our terms are displayed.

Let’s go through our function a bit at a time. The first bit sets up some of the variables. You only really need to change the $taxonomy variable to match your taxonomy name. Note also the $name variable. We are giving input fields the name tax_input[mytaxonomy]. This the name for the input inside the default metabox. By doing this, WordPress will automatically handle the updating of a post’s taxonomy term.

//Set up the taxonomy object and get terms
$taxonomy = 'mytaxonomy';
$tax = get_taxonomy($taxonomy);//This is the taxonomy object

//The name of the form
$name = 'tax_input[' . $taxonomy . ']';

//Get all the terms for this taxonomy
$terms = get_terms($taxonomy,array('hide_empty' => 0));

We’ll be wanting the ID of the post’s current term (we’re expecting only one).

 
$postterms = get_the_terms( $post->ID,$taxonomy );
$current = ($postterms ? array_pop($postterms) : false);
$current = ($current ? $current->term_id : 0);

If you take a look at WordPress’ category metabox, you’ll notice a tab which will display the ‘most used’ terms. To reproduce that we’ll need the 10 most popular terms. We use the get_terms function again, but this time selecting at most 10 terms and ordered by count (the number of posts that have this taxonomy).

$popular = get_terms( $taxonomy, array( 'orderby' => 'count', 'order' => 'DESC', 'number' => 10, 'hierarchical' => false ) );

Next we want to display the ‘All Categories’ and ‘Most Used’ tabs (it’s best practise to use the taxonomy labels wherever possible). If you don’t want tabs, you can simply remove this bit:

<!-- Display tabs-->
<ul id="<?php echo $taxonomy; ?>-tabs" class="category-tabs">
	<li class="tabs"><a href="#<?php echo $taxonomy; ?>-all" tabindex="3"><?php echo $tax->labels->all_items; ?></a></li>
	<li class="hide-if-no-js"><a href="#<?php echo $taxonomy; ?>-pop" tabindex="3"><?php _e( 'Most Used' ); ?></a></li>
</ul>

Next, we want to set what to display when we are on the ‘all categories’ tab:

<!-- Display taxonomy terms -->
<div id="<?php echo $taxonomy; ?>-all" class="tabs-panel">
	<ul id="<?php echo $taxonomy; ?>checklist" class="list:<?php echo $taxonomy?> categorychecklist form-no-clear">
		<?php   foreach($terms as $term){
			$id = $taxonomy.'-'.$term->term_id;
			echo "<li id='$id'><label class='selectit'>";
			echo "<input type='radio' id='in-$id' name='{$name}'".checked($current,$term->term_id,false)."value='$term->term_id' />$term->name<br />";
			echo "</label></li>";
		}?>
	</ul>
</div>

This is really just displaying a list inside a div element, and each list element is a radio option. Of course you can simply replace this list with a drop-down menu or anything else you like.

Now we do the same for the ‘most used’ tab:

<!-- Display popular taxonomy terms -->
<div id="<?php echo $taxonomy; ?>-pop" class="tabs-panel" style="display: none;">
	<ul id="<?php echo $taxonomy; ?>checklist-pop" class="categorychecklist form-no-clear" >
		<?php   foreach($popular as $term){
			$id = 'popular-'.$taxonomy.'-'.$term->term_id;
			echo "<li id='$id'><label class='selectit'>";
			echo "<input type='radio' id='in-$id'".checked($current,$term->term_id,false)."value='$term->term_id' />$term->name<br />";
			echo "</label></li>";
		}?>
	</ul>
</div>

Step 4 Our Complete Callback Function

Piece it together and our complete function is

//Callback to set up the metabox
function myprefix_mytaxonomy_metabox( $post ) {
    //Get taxonomy and terms
    $taxonomy = 'mytaxonomy';

    //Set up the taxonomy object and get terms
    $tax = get_taxonomy($taxonomy);
    $terms = get_terms($taxonomy,array('hide_empty' => 0));

    //Name of the form
    $name = 'tax_input[' . $taxonomy . ']';

    //Get current and popular terms
    $popular = get_terms( $taxonomy, array( 'orderby' => 'count', 'order' => 'DESC', 'number' => 10, 'hierarchical' => false ) );
    $postterms = get_the_terms( $post->ID,$taxonomy );
    $current = ($postterms ? array_pop($postterms) : false);
    $current = ($current ? $current->term_id : 0);
    ?>

    <div id="taxonomy-<?php echo $taxonomy; ?>" class="categorydiv">

        <!-- Display tabs-->
        <ul id="<?php echo $taxonomy; ?>-tabs" class="category-tabs">
            <li class="tabs"><a href="#<?php echo $taxonomy; ?>-all" tabindex="3"><?php echo $tax->labels->all_items; ?></a></li>
            <li class="hide-if-no-js"><a href="#<?php echo $taxonomy; ?>-pop" tabindex="3"><?php _e( 'Most Used' ); ?></a></li>
        </ul>

        <!-- Display taxonomy terms -->
        <div id="<?php echo $taxonomy; ?>-all" class="tabs-panel">
            <ul id="<?php echo $taxonomy; ?>checklist" class="list:<?php echo $taxonomy?> categorychecklist form-no-clear">
                <?php   foreach($terms as $term){
                    $id = $taxonomy.'-'.$term->term_id;
                    echo "<li id='$id'><label class='selectit'>";
                    echo "<input type='radio' id='in-$id' name='{$name}'".checked($current,$term->term_id,false)."value='$term->term_id' />$term->name<br />";
                   echo "</label></li>";
                }?>
           </ul>
        </div>

        <!-- Display popular taxonomy terms -->
        <div id="<?php echo $taxonomy; ?>-pop" class="tabs-panel" style="display: none;">
            <ul id="<?php echo $taxonomy; ?>checklist-pop" class="categorychecklist form-no-clear" >
                <?php   foreach($popular as $term){
                    $id = 'popular-'.$taxonomy.'-'.$term->term_id;
                    echo "<li id='$id'><label class='selectit'>";
                    echo "<input type='radio' id='in-$id'".checked($current,$term->term_id,false)."value='$term->term_id' />$term->name<br />";
                    echo "</label></li>";
                }?>
           </ul>
       </div>

    </div>
    <?php
}

Step 5 A Little JavaScript…

I was careful in my naming of IDs and radio buttons in the callback function. If you try all the above now, you’ll find that WordPress automatically handles the updating of post terms. Furthermore, WordPress’ javascript automatically handles the tab navigation. There is one slight hiccup. The ‘all categories’ radio buttons are not in sync with the ‘most used’. If you’ve decided to dispense with the ‘most used’ tab then you can ignore this section. Otherwise, we just need to add tiny bit of javascript to fix this issue.

We want to add a bit of javascript to the page, so inside our callback function we’ll use a hook that fires when javascript is added in the admin. That is, the admin_enqueue_scripts hook. Since we add our function onto this hook inside our callback function, it’s only loaded when it’s needed. Just add this line at the top of our callback function above:

add_action('admin_enqueue_scripts','myprefix_radiotax_javascript');

When the javascripts are being loaded in the admin page, this will trigger our function. This function does nothing more than register and enqueue our javascript, which we want to load in the footer:

function myprefix_radiotax_javascript(){
	wp_register_script( 'radiotax', get_template_directory_uri() . '/js/radiotax.js', array('jquery'), null, true ); // We specify true here to tell WordPress this script needs to be loaded in the footer
	wp_enqueue_script( 'radiotax' );
}

Now for the javascript we actually need, create a file in your theme’s js folder. We’ll call it radiotax.js, and here’s the code to put inside:

jQuery(document).ready(function($) {
	var taxonomy = 'mytaxonomy';
	$('#' + taxonomy + 'checklist li :radio, #' + taxonomy + 'checklist-pop :radio').live( 'click', function(){
		var t = $(this), c = t.is(':checked'), id = t.val();
		$('#' + taxonomy + 'checklist li :radio, #' + taxonomy + 'checklist-pop :radio').prop('checked',false);
		$('#in-' + taxonomy + '-' + id + ', #in-popular-' + taxonomy + '-' + id).prop( 'checked', c );
	});
});

So what do these few lines do? Whenever you check a radio button it unchecks all the others (on both tabs) and then checks the radio buttons that correspond to that term.


Conclusion

And with that we are done. WordPress handles all the rest for us. There’s room for improvement though… what about adding new terms? I’ve ommitted that from our metabox, because it’s actually incredibly tricky to do. It would involve a lot more javascript and a also a bit of action on the server side.


Update:

As requested by Roberto, here is a link to the code in full on GitHub. It’s a class implementation of the code used in this tutorial, so to get started you should only need to change the class’ static variables at the top.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Roberto

    Nice tutorial, but I can’t make it work. Would you please publish the whole code?

  • Pingback: How to Use Radio Buttons With Taxonomies | Shadowtek | Hosting and Design Solutions

  • Michael Rüfenacht

    Hmm … wouldnt it be easier just to enqueue a script that unchecks all other checkboxes when selecting one?

    • http://wordpress.org/extend/plugins/profile/stephenh1988 Stephen Harris
      Author

      It’s possible, but I wouldn’t recommend it. Users are used to check-boxes meaning multiple choices and radio buttons meaning only one. To provide a good user interface, you don’t want to be going against that. Also, in the (admittedly unlikely) scenario of JavaScript being turned off, the method in the tutorial would still work, in that only one option would be selectable.

  • Pingback: Tweet-Parade (no.10 Mar 2012) | gonzoblog.nl

  • nigedo

    Excellent resource! Thank you so much for producing this.

    I had just one minor issue with it.

    When I selected a radio button and hit “Update”, WordPress added a new term by handing off the selected term’s ‘term_id’ as the ‘name’ of the new term.

    Clearly WordPress was handling the returned radio button ‘value’ as a term’s name and when it failed to find the received numeric id in the list of term names, it added it as a new one.

    To fix it, I altered this line:

    echo “term_id,false).”value=’$term->term_id’ />$term->name”;

    to read:

    echo “term_id,false).”value=’$term->name’ />$term->name”;

    Nonetheless, thanks for outstanding work. :)

    • http://wordpress.org/extend/plugins/profile/stephenh1988 Stephen Harris
      Author

      Good catch! WordPress handles non-hierarchal taxonomies differently from hierarchal ones. The above tutorial works for hierarchal taxonomies (where the ID is posted), but your alterations are needed for non-hierarchal taxonomies (where, the term name is posted).

      You’ll need to use $term->slug as the value, however, rather than $term->name.

      Thanks for commenting!

      • nigedo

        Thanks I changed that to $term->slug and it works perfectly.

  • http://wurv.com Tad

    Perfect timing, I needed something like this for a project that I’ve been working on. Wouldn’t ya know, this comes through my feed. Cut and paste perfect. -Thanks!

  • http://wpconsult.net Paul

    thanks for posting this Stephen, this is a frequent requirement and it’s a shame it’s not easier to do.

  • Steve

    Hi; thanks for the really useful and timely tutorial. I’m trying to combine this with another WPTuts tutorial to create a custom meta box on a custom post type. I can’t seem to get this to work properly. I only want to use radio buttons to select one value from five, which form a non-hierarchical custom taxonomy.

    This is what I’m using first:
    // Rating
    $ssr_ratings_terms = get_terms(‘ratings’, array(‘hide_empty’ => 0, ‘orderby’ => ‘id’));
    $ssr_ratings_postterms = get_the_terms($post->ID, ‘ratings’);
    $ssr_ratings_current = ($ssr_ratings_postterms ? array_pop($ssr_ratings_postterms) : false);
    $ssr_ratings_current = ($ssr_ratings_current ? $ssr_ratings_current->term_id : 0);

    Then this to generate the radio buttons:
    <li>Rating: <?php echo $ssr_ratings_current . ‘<br />’;
    foreach($ssr_ratings_terms as $ssr_ratings_term) {
    $id = ‘ssr_ratings_’ . $ssr_ratings_term->slug;
    echo ‘<input type=”radio” id=”‘ . $id . ‘” name=”tax_input[ratings]“‘ . checked($ssr_ratings_current, $ssr_ratings_term->slug, false) . ‘ value=’ . $ssr_ratings_term->slug . ‘” />’;
    echo ‘<label for=”‘ . $id . ‘”>’ . $ssr_ratings_term->name . ‘</label> ’;
    }
    ?>
    </li>

    It all seems to work fine in terms of the look and only allowing one button to be selected, but nothing is saved and all the buttons are cleared when I update the post. I have tweaked things here and there as I don’t need all the tabs, etc. but can’t see where I’ve gone wrong.

    I’d be really grateful if anyone could help. Thanks.

  • http://wordpress.org/extend/plugins/profile/stephenh1988 Stephen Harris
    Author

    The problem may be that you are comparing the current saved term’s ID ($ssr_ratings_current) with the term’s slug. For non-hierarchical taxonomies the value should be the slug (as you have it), but the when checking if the current term in the loop is the currently saved term you want to compare ID with ID (or slug with slug).

    I can’t see any other errors, but if you run into difficulty I’d recommended starting with the tutorial as a basis and altering it a bit at a time so you can identify any mistakes as/if they occur.

    • Steve

      Thanks for your reply Stephen. I tried that but still had no joy. I do now have it working though, albeit using a slightly different method.

      Rather than using tax_input[ratings] for the field name, I used a standard ssr_ratings and then added this at the bottom (with all the other form saving stuff, as per another WPTuts tutorial):

      // Rating
      if(isset($_POST['ssr_ratings'])) wp_set_post_terms($post_id, esc_attr($_POST['ssr_ratings']), ‘ratings’, false);

      I could then (by adding a debug bit of text) that the taxonomy was being updated when I saved the post, but this still wasn’t being reflected in the radio buttons. After a bit of poking around, I realised I needed to remove the ,false from the end of the checked() function and all is now good.

      Not quite sure why your complete method didn’t work for me, but it certainly got me well on the way. Thanks again for your work and maybe my experience above can help someone else.

  • Pingback: Update – Radio Buttons With Taxonomies | Stephen Harris

  • ehab

    how to use the radio buttons with taxonomy from the front end?

  • Bartosz

    How to use checkboxes instead of radio? I created an array of current post terms, but can’t get no further.
    Code looks like this:

    $currentterms = array();
    foreach($postterms as $postterm) {
    array_push($currentterms, $postterm->term_id);
    }

    • http://profiles.wordpress.org/stephenh1988/ Stephen Harris

      The default metabox for a hierarchal taxonomy uses checkboxes…?

      • Rafael

        Hello Stephen,

        Congratulations, excellent tutorial. It works perfect with radio buttons but I have the same question asked by Bartosz: “How to use checkboxces instead of radio?”

        I am using your tutorial to build a custom taxonomy metabox that show an image instead the taxonomy original name and the checkboxes aren’t working. When I check multiple boxes, only one returns checked.

        I tried to use the original wordpress file linked in the post but I cant work with the “wp_popular_terms_checklist”

      • http://twitter.com/prionkor Sisir Kanti Adhikar

        @99c40eb6a15d5177d940890784cc797d:disqus I was trying to use this code. But it seems when i am saving with the taxonomy term selected. It creating new taxonomy with the name = selected term id. Not sure why…

  • http://ninenet.org Nate Paul

    This a great tutorial! I am running into an issue though… Radio buttons show up great on the backend but when I can’t select any of them. Clicking but nothing is selected.

    Then I noticed if I remove the JS it allows me to select a taxonomy but then my selection is cleared when I “Update” the post.

  • http://stephenharris.info Stephen Harris
    Author

    Its’ possible that your original taxonomy metabox hasn’t been removed (even if you can’t see it, it might just be hidden). Make sure the ID you are using to remove the metabox is correct. Usually this would be ‘mytaxonomydiv’ for hierarchal taxonomies, or ‘tagsdiv-mytaxonomy’ for non-hierarchal ones. It is the ID of the metabox.

    If it still doesn’t work, try the linked GitHub repro – that works and is easy to set up (only requires a few variables to be set).

  • http://kathyisawesome.com Kathy

    I took Stephen’s class and turned it into a formal WordPress plugin. You can select which taxonomies you’d like to convert to radio buttons from the plugin’s options page without touching any code.

    I wrote a quick post about it here:
    http://www.kathyisawesome.com/441/radio-buttons-for-taxonomies/

    Its on github and should be in the WordPress directory in a few days.

    Thanks Stephen!

    • http://bluemountainswebdesign.com.au Eben

      Thanks Kathy, the plugin work great :)

  • http://www.pixelfans.de Carsten

    Thank you for pointing this out, this saved me some valuable time.

    Do you have any suggestions, how to re-use your code to work also within the quickedit?

  • Pingback: Radio Buttons for Taxonomies

  • Lox_knc

    That is a lot of code where a remove_metabox and add_metabox would have been enough ….

    add_meta_box($tax_name . ‘div’, $label, ‘post_categories_meta_box’, null, ‘side’, ‘core’, array( ‘taxonomy’ => $tax_name ));
    remove_meta_box(‘tagsdiv-’ . $tax_name, null, ‘side’);

    Maybe a little bit of JavaScript or CSS could be needed to hide the parent select box of the add new form.