I want to make a categories list of this site: https://premium.wpmudev.org/blog/

So I make this code:

<select class="select">
    $args = array(
        'orderby' => 'name',
        'order'   => 'ASC'
    $categories = get_categories($args);
    foreach ($categories as $cat) {
         echo '<option value="'.esc_url( get_category_link( $cat->term_id ) ).'" '.selected( esc_url( get_category_link( $cat->term_id ) ) ).'>'.$cat->name.'</option>';

The code is working and redirects me to the category link that I selected but the problem that after I redirect to the category the category option inside the categories select list is not changed to the current category.

The problem is here: selected( esc_url( get_category_link( $cat->term_id ) )

but i can't solve it.

  • selected compares two values, and outputs the selected attribute if they match. If you omit the 2nd argument, it defaults to true, so you are currently checking if the category URL is equal to true. – Milo Mar 7 '18 at 4:51
  • I try this but it's not working can you add an answer for this to understand more? – John Mar 7 '18 at 4:59
  • john add in if condtion in to select like get current id and category id are same echo select if( $cat->term_id==get_cat_ID()){ echo "selected"; } else { echo "" } – Dharmishtha Patel Mar 7 '18 at 5:41
  • get_cat_ID() doesn't do what you think it does. – Jacob Peattie Mar 7 '18 at 6:53

The problem is how you've used selected(). That function outputs selected="selected" if the first argument matches the second argument.

So what you're doing is marking the option as selected if:

(string) 'https://premium.wpmudev.org/blog/category/news-community/' === (string) true

Which - because the values are cast as strings and compared with === - will never be true, even if the URL is the same as the category you're viewing. See the function that's used under the hood for this.

So what you need to do is pass values to this function that can check whether the current option is the same as the currently viewed category. You don't actually need to use the actual value attribute for the option to do the comparison. Since each option is representing a category, you can just compare the ID for that option and the ID of the current category being viewed.

We can therefore use $cat->term_id for the ID of the current option, and get_queried_object_id() to get the ID of the category that's currently being viewed. However one important caveat is that get_queried_object_id() will also return the post ID if you're viewing a post, and since posts and terms are stored separately, you could get a match even if you're not viewing a category. So you should check that you're even viewing a category before using get_queried_object_id().

Apply all this and your code should look something like:

$categories = get_categories( array(
    'orderby' => 'name',
    'order'   => 'ASC'
) );

$current_category = is_category() ? get_queried_object_id() : false;

foreach ( $categories as $category ) {   
        '<option value="%s" %s>%s</option>',
        esc_url( get_category_link( $category ) ),
        selected( $category->term_id, $current_category, false ),
        esc_html( $category->name )

This line is getting the ID of the current category, as long as we are even viewing a category:

$current_category = is_category() ? get_queried_object_id() : false;

Then this line is how the selected() function is working:

selected( $category->term_id, $current_category, false ),

Note that I'm comparing the ID of the category in the foreach loop to the category we're viewing, as defined above. Also note that the last argument is false. We only want to return the text of the attribute because we're already echoing.

There's also a few stylistic changes I made that you don't need to use but I feel I should explain:

  • I used $category instead of $cat. I don't like abbreviations in code. You can do what you want.
  • I used printf() instead of concatenating a string together. Concatenating an <option> tag with all these values can result in a really long line of code. Using printf() keeps it more compact and readable (in my opinion).
  • This one you should do: I escaped the name of the category with esc_html(). This is so that the name of the category can't break the HTML of the option tag we're creating.
  • 1
    Jacob, really you are amazing I learn from you many things thank you very much :) – John Mar 7 '18 at 12:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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