How to add an ajax-ified cart count into a WordPress menu with WooCommerce

Published October 12, 2019
.* :☆゚

If you want to add a cart count anywhere in your theme, WooCommerce makes that pretty easy to do.

But what if you wanted to add a cart count within an actual WordPress menu, so it rendered with the wp_nav_menu() markup like below?

<ul id="account-menu" class="menu">
  <li id="menu-item-2923" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2923"><a href="/my-account/" data-title="Account">Account</a></li>
  <li id="menu-item-2943" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2943"><a href="/cart/" data-title="Cart">Cart <span class="cart-count">(0)</span></a></li>
</ul>

WooCommerce doesn’t yet allow this feature out of the box, but it is possible to achieve this using the hooks available to us, as I’ll detail below.

Benefits of rendering the dynamic cart count within wp_nav_menu()

The benefit of having the cart count within the actual menu rather than outside of it theoretically means that you’ll need to worry less about how the element is positioned across different browser sizes, and therefore use less CSS to ensure the layout is aesthetically pleasing. Furthermore, the markup will be more semantically correct and logical when being read out by screen readers.

Based on past experience, the more elements there are in the header, the more time it takes to position each element, especially on mobile devices.

I like the idea of being able to insert the cart count in the actual nav menu itself, because the end markup is cleaner and I know exactly where the cart link will be in the header when the browser is resized.


The steps to render the dynamic cart count in the menu

1. Register your nav menu in functions.php, then go to the admin and add the WooCommerce cart page to the menu.

I am leaving the label as ‘Cart’ but you can actually rename this to anything you wish (just bear in mind you will need to edit the following code to match this label later on).

2. Paste the following code in your functions file to retrieve the dynamic cart count.

Note: When I am working with WooCommerce, it’s useful to keep WooCommerce functions separate from the WordPress functions. I have a file called woocommerce.php which I then include from functions.php.

<?php
//I namespace all my functions with unicorn_tears - replace this if you wish
if ( ! function_exists( 'unicorn_tears_woocommerce_cart_count' ) ) {
  /**
    * Cart Count
    *
    * Display the number of items in the cart.
    * @return string containing cart count
    */
  function unicorn_tears_woocommerce_cart_count() {
    $item_count_text = sprintf(
      _n( '%d', '%d', WC()->cart->get_cart_contents_count(), 'unicorn-tears' ),
      WC()->cart->get_cart_contents_count()
    );
    return '<span class="cart-count">(' . $item_count_text .')</span>';
  }
}

/**
* Note: _n() is used to conditionally display multiples of items.
* I've left it in my code on the off chance I may use it in the future, but you can replace that line with the following if you wish
*/
_n( '%d item', '%d items', WC()->cart->get_cart_contents_count(), 'unicorn-tears' )

3. Paste this code block in the same file to append the cart count to your menu item and update the variables to match your website.

<?php
if ( ! function_exists( 'unicorn_tears_append_cart_count' ) ) {
  /**
    * Append cart count to the Cart item in the menu.
    *
    * @return formatted menu items with cart count appended
    */
  function unicorn_tears_append_cart_count( $items, $args ) {
    //change the theme_location to your own menu location eg. primary
    if ($args->theme_location == 'account') {
      $split = preg_split('/(?<=>Cart)/',$items);
      $items = $split[0] . unicorn_tears_woocommerce_cart_count() . $split[1];
    }
    return $items;
  }
}
add_filter( 'wp_nav_menu_items', 'unicorn_tears_append_cart_count', 20, 2 );

In my case, I am appending the cart count to the ‘Cart’ item in my menu. If you named your menu item something other than ‘Cart’, you’ll need to update the ‘Cart’ in the regex ie. /(?<=>YOUR_MENU_TITLE_HERE)/.

You will also need to reference the nav menu you are updating by theme_location. In my case, I have a separate menu named 'account'. Yours will likely be different(eg. primary) so update the $args->theme_location == 'account' conditional accordingly.

4. Ajaxify the cart count

Paste the following code in the same file so the cart count updates whenever an item is added to the cart (works well with the ajax add to cart button).

<?php
if ( ! function_exists( 'unicorn_tears_woocommerce_cart_link_fragment' ) ) {
  /**
    * Cart Fragments.
    *
    * Ensure cart contents update when products are added to the cart via AJAX.
    *
    * @param array $fragments Fragments to refresh via AJAX.
    * @return array Fragments to refresh via AJAX.
    */
  function unicorn_tears_woocommerce_cart_link_fragment( $fragments ) {
      ob_start();
      echo unicorn_tears_woocommerce_cart_count();
      $fragments['span.cart-count'] = ob_get_clean();

      return $fragments;
  }
}
add_filter( 'woocommerce_add_to_cart_fragments', 'unicorn_tears_woocommerce_cart_link_fragment' );

With the code above, you should have a menu that now includes the dynamic cart count. It will also update automatically whenever an item is added to your cart via WooCommerce’s ajax add to cart functionality without needing a page refresh.

The site I am currently working on has a working example of this, and I’ll be sure to post a link when it goes live. :)


Extra notes

If you wanted to replace the label altogether, you could modify the regex in unicorn_tears_append_cart_count() so it consumes the word Cart(or whatever label you used) instead.

If you wanted to add an icon as well, you could modify the unicorn_tears_woocomme_cart_count() function to include the relevant markup:

<?php
if ( ! function_exists( 'unicorn_tears_woocommerce_cart_count' ) ) {
  /**
    * Cart Count
    *
    * Display the number of items in the cart.
    *
    */
  function unicorn_tears_woocommerce_cart_count() {
      $item_count_text = sprintf(
        _n( '%d', '%d', WC()->cart->get_cart_contents_count(), 'unicorn-tears' ),
        WC()->cart->get_cart_contents_count()
      );
      return get_icon('cart',20) . '<span class="cart-count">(' . $item_count_text .')</span>';
  }
}

In the code above, I am using a custom function get_icon('cart',20) to retrieve an inline SVG which I can then style accordingly. This is a riff off the twentynineteen theme, and is my preferred method for using SVGs in WordPress.

If you’re looking for a good way to display and manage SVGs in WP, look out for my next blog post soon.