2

I want to hide an item from a menu if a user is logged out.

I am currently using the below code that achieves this using two separate menus, but to save duplication, I would like to only have to manage one nav menu.

function my_wp_nav_menu_args( $args = '' ) {

    if ( is_user_logged_in() ) { 
        $args['menu'] = 'logged-in';
    } else { 
        $args['menu'] = 'logged-out';
    }

    return $args;
}
add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );

Is it possible to hide just one item for a logged out user, rather than doing it the way I currently am?

  • what do you want to hide from one menu ? – mmm Jul 31 '16 at 9:33
  • A link which not visible to not registered user. – Iqbal Mahmud Jul 31 '16 at 10:02
  • The "without a plugin" thing is never necessary. Such logic is independent from your design and styling and should survive a redesign of your site. Put it inside a plugin or a mu-plugin. Having hundreds of mini-plugins instead of a big functions.php or an extremely large plugin does not slow down your site. – kaiser Jul 31 '16 at 12:51
-4

Find the class or id of the menu item that you want to hide. suppose the class of that menu is logged-in-menu

Then in header.php file of your theme before closing head tag use the below code

<style>
if(! is_user_logged_in() ) : ?>
    .logged-in-menu{
        display:none;
    }
<?php endif; ?>
</style>
  • This means that you are (a) querying unnecessary data and (b) is non-reversible for child themes or plugins. You might want to at least use a callback attached to wp_head or properly register and enqueue styles. While then it would be a somehow ok solution, it would still be preferable to just conditionally switch those items off before querying the items from the DB. – kaiser Jul 31 '16 at 12:03
4

As @chrisguitarguy already added a more than valid answered while I was writing this answer, here's a simple addition to the other two answers.

The return value of the wp_setup_nav_menu() function has a filter, which has $menu_item as only value provided – exactly before it is returned – and it is of type object and a \stdClass with the following public properties that you can check against:

  • ID: The term_id if the menu item represents a taxonomy term.
  • attr_title: The title attribute of the link element for this menu item.
  • classes: The array of class attribute values for the link element of this menu item.
  • db_id: The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
  • description: The description of this menu item.
  • menu_item_parent: The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise.
  • object: The type of object originally represented, such as "category," "post", or "attachment."
  • object_id: The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
  • post_parent: The DB ID of the original object's parent object, if any (0 otherwise).
  • post_title: A "no title" label if menu item represents a post that lacks a title.
  • target: The target attribute of the link element for this menu item.
  • title: The title of this menu item.
  • type: The family of objects originally represented, such as "post_type" or "taxonomy."
  • type_label: The singular label used to describe this type of menu item.
  • url: The URL to which this menu item points.
  • xfn: The XFN relationship expressed in the link of this menu item.
  • _invalid: Whether the menu item represents an object that no longer exists.

So a simple callback will allow you to use some conditional logic and then maybe exclude an item:

add_filter( 'wp_setup_nav_menu', function( \stdClass $item ) {
    # Check conditionals, and invalidate an item in case
    $item->_invalid = is_user_logged_in() 
        && 'post' === $item->object
        && 'post_type' === $item->type
        # && … whatever you need to check for your invalidation of an item
    ;

    return $item;
} );

The exclusion logic lives inside the _invalid property and is executed by the _is_valid_nav_menu_item( $item ) function that is a callback used when nav menu items are retrieved. It uses it inside a array_filter() to reduce the number of items depending on this flag.


As extension to @MD Sultan Nasir Uddin solution: While a CSS only solution will work, goal should be to not even have the data in this request, in the database query and in the render pipeline. For a complete answer, here still is the how: Example using wp_add_inline_style() to inline the styles and PHP heredoc syntax for readability:

<?php
/** Plugin Name: Hide menu items for logged in users */

# Add class:
add_filter( 'wp_nav_menu_args', function( Array $args ) {
    if ( is_user_logged_in() )
        $args['menu_class'] .= '  logged-in';
    return $args;
} );

# Add inline styles
add_action( 'wp_enqueue_scripts', function() {

    $styles = <<<STYLES
.logged-in .special-item {
    display: none;
}
STYLES;

    wp_add_inline_style( 'custom-style', $styles );
} );

You could probably just use the body classes to find a logged-in or similar class for a more specific target as well – instead of adding an additional class like above.

  • That _invalid thing is nifty. – chrisguitarguy Jul 31 '16 at 13:11
  • @chrisguitarguy It is! And I did not know about it unless you posted the answer and I read through source. Only thing I am unsure now is if it should not just be false when you want to drop something. – kaiser Jul 31 '16 at 14:41
3

Filter wp_nav_menu_objects. It will contain the sorted list of nav menu items to render. Have a look at wp_setup_nav_menu_item for some properties you can use.

Here's a quick (untested) example.

add_filter( 'wp_nav_menu_objects', function( array $items, array $args ) {

    if ( 'someThemeLocation' !== $args->theme_location ) {
        return $items;
    }

    return array_filter( $items, function( $item ) {
        return '/user-specific-thingy' === $item->url 
            && ! is_user_logged_in();
    } );

}, 10, 2 );
2

After that I make it done using css nth child the procedure is

add_action('wp_head','hide_menu');

function hide_menu() { 
    if ( is_user_logged_in() ) {
        //
    } else {
        $output="<style> .menu li:nth-child(3) { display: none; } </style>";
    }
    echo $output;
}

Thanks all of you for your effort :)

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.