Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Move WooCommerce subcategory list items into their own <ul> separate from the product <ul>.
<?php
/**
* Move WooCommerce subcategory list items into
* their own <ul> separate from the product <ul>.
*/
add_action( 'init', 'move_subcat_lis' );
function move_subcat_lis() {
// Remove the subcat <li>s from the old location.
remove_filter( 'woocommerce_product_loop_start', 'woocommerce_maybe_show_product_subcategories' );
add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_start', 40 );
add_action( 'woocommerce_before_shop_loop', 'msc_maybe_show_product_subcategories', 50 );
add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_end', 60 );
}
/**
* Conditonally start the product loop with a <ul> contaner if subcats exist.
*/
function msc_product_loop_start() {
$subcategories = woocommerce_maybe_show_product_subcategories();
if ( $subcategories ) {
woocommerce_product_loop_start();
}
}
/**
* Print the subcat <li>s in our new location.
*/
function msc_maybe_show_product_subcategories() {
echo woocommerce_maybe_show_product_subcategories();
}
/**
* Conditonally end the product loop with a </ul> if subcats exist.
*/
function msc_product_loop_end() {
$subcategories = woocommerce_maybe_show_product_subcategories();
if ( $subcategories ) {
woocommerce_product_loop_end();
}
}
@eggdesign
Copy link

eggdesign commented May 24, 2019

I also want to find a way of changing the class name on the categories list. The above code is a step in the right direction but I can't believe this is still so difficult to do in Woocommerce after all these years,

I found a workaround for it. Just copy archive-product.php to your child theme folder then add a wrapper div around the products section. This will allow you to style the products separate from the categories. You may need to add some opening & closing php tags too to add the div. Screenshot here: https://ibb.co/GTvgkTx

@fkoomek
Copy link

fkoomek commented Jun 13, 2019

My solutions:

  1. You can just delete woocommerce_product_loop_start(); and woocommerce_product_loop_end(); from the code and echo your products wrapper.
<?php
add_action( 'init', 'move_subcat_lis' );
function move_subcat_lis() {
	// Remove the subcat <li>s from the old location.
	remove_filter( 'woocommerce_product_loop_start', 'woocommerce_maybe_show_product_subcategories' );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_start', 40 );
	add_action( 'woocommerce_before_shop_loop', 'msc_maybe_show_product_subcategories', 50 );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_end', 60 );
}

/**
 * Conditonally start the product loop with a <ul> contaner if subcats exist.
 */
function msc_product_loop_start() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {
		echo '<ul class="product-cats">';  //products wrapper
	}
}
/**
 * Print the subcat <li>s in our new location.
 */
function msc_maybe_show_product_subcategories() {
	echo woocommerce_maybe_show_product_subcategories();
}
/**
 * Conditonally end the product loop with a </ul> if subcats exist.
 */
function msc_product_loop_end() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {
		echo '</ul>';  //products wrapper ends
	}
}

You will need some CSS to style your subcategories then. For example:

ul.product-cats li {
    list-style: none;
    margin-bottom: 4.236em;
	font-size:1.1em;
	float: left;	
	width: 48%;
}

ul.product-cats li:nth-child(2n+2){
float:right;	
}

ul.product-cats li img {
    margin: 0 0 1em; 
}

ul.product-cats{
margin:0;	
}
 
@media screen and (min-width:769px) {
    ul.product-cats li {
        width: 22.05%;
        float: left;
        margin: 0 3.8% 2em 0;
    }
		ul.product-cats li:nth-child(2n+2){
float:left;	
}
    ul.product-cats li:nth-of-type(4n+4) {
        margin-right: 0;
    }
}

the CSS code is made to fit my theme so you will have to tweak it.
I was working with the CSS provided at https://code.tutsplus.com/tutorials/display-woocommerce-categories-subcategories-and-products-in-separate-lists--cms-25479 so maybe it will better option to start with.

You can wrap the whole ul element of subcategories with your own element like:

<?php
add_action( 'init', 'move_subcat_lis' );
function move_subcat_lis() {
	// Remove the subcat <li>s from the old location.
	remove_filter( 'woocommerce_product_loop_start', 'woocommerce_maybe_show_product_subcategories' );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_start', 40 );
	add_action( 'woocommerce_before_shop_loop', 'msc_maybe_show_product_subcategories', 50 );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_end', 60 );
}
/**
 * Conditonally start the product loop with a <ul> contaner if subcats exist.
 */
function msc_product_loop_start() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {
		echo '<div class="subcategories">'; //custom wrapper 
		woocommerce_product_loop_start();
	}
}
/**
 * Print the subcat <li>s in our new location.
 */
function msc_maybe_show_product_subcategories() {
	echo woocommerce_maybe_show_product_subcategories();
}
/**
 * Conditonally end the product loop with a </ul> if subcats exist.
 */
function msc_product_loop_end() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {
		woocommerce_product_loop_end();
		echo '</div>'; //custom wrapper ends
	}
}

@Timoti
Copy link

Timoti commented Oct 3, 2019

Super stoked to find this @twoelevenjay - thanks for sharing and supporting

@twoelevenjay
Copy link
Author

twoelevenjay commented Oct 3, 2019

@rubinablack ypu would add this code to your functions.php file or include the file by writing require_once 'PATH_TO_FILE'; in the functions.php file. Sorry this reply is so late. I don't get notifications for comments unless I am @ mentioned.

@twoelevenjay
Copy link
Author

twoelevenjay commented Oct 3, 2019

@daw8706, @eggdesign , @1stwebdesigns sorry I never so these comments before. I am happy that @fkoomek gave a good answer on how to change the value of the class property of the

    tag.

@twoelevenjay
Copy link
Author

twoelevenjay commented Oct 3, 2019

@StefanTrailovic, I am sorry I don't have Flatsome. I am guessing that maybe infinite scroll evokes the template files directly and doesn't use the actions that I have hooked here. Or maybe infinite scroll has its own template files. You would have to look through the code for template files named something like loop-start-end.php, or look for functions like add_action(). Sorry for the delayed response.

@newcomer9087
Copy link

newcomer9087 commented Nov 13, 2019

@twoelevenjay I am having the same problem as @StefanTrailovic different theme, infinite loader load twice the products as I start scrolling, I don't know much coding any specific fix?
searched for the loop.php file but I think there is none, looking for the functions seems impossible to me.
Any help will be much appreciated.

@twoelevenjay
Copy link
Author

twoelevenjay commented Nov 13, 2019

@newcomer9087, by any chance, is there a publicly accessible URL that I can see this issue in action? What theme are you using, and is the infinite scroll a theme option or from a plugin?

@newcomer9087
Copy link

newcomer9087 commented Nov 14, 2019

@twoelevenjay Theme is inktheme, fresh veggies layout, but I don't think it has to do with the theme, is more about the plugin how it acts with wocommerce with your code added into functions.php.

The plugin is WooCommerce Load More Products - BeRocket.

I have emailed you the site let me know if you did not receive any mail.

Thanks

@johnsimeon
Copy link

johnsimeon commented Jul 20, 2021

Hello guys, the snippet works perfect! But i want to try some customizations also.
How to change the number of product categories per row?
I'm using Woocommerce settings to show categories thumbnail on the initial shop page and then products and their thumbnails within them.
I want to have that initial category page to display 3 thumbnails per row and the products page to show 5 categories per row.
Is is possible?

@fkoomek
Copy link

fkoomek commented Jul 21, 2021

Hi.
Try this snippet. It will set 3 products per row on the main shop page and 5 products per row on other pages. I believe it should also affect the number of categories per row. However, if you want to manipulate just the number of categories, you would have to use for example this shortcode [product_categories] and modify more the code provided above.
https://docs.woocommerce.com/document/woocommerce-shortcodes/

add_filter ('loop_shop_columns', 'loop_columns', 999); // change number of products per row
if (!function_exists('loop_columns')) {
	
	function loop_columns() {
		if (is_shop()){	// if is main shop page
		return 3; // 3 products per row
	}
	else{
		return 5; // 5 products per row
	}
}
}

@johnsimeon
Copy link

johnsimeon commented Jul 21, 2021

Hi thanks for your help, unfortunatelly i don't want this.
I want the category page to display 3 subcategories and below the row of the products to show 5 products per row!
Thanks again!

@fkoomek
Copy link

fkoomek commented Jul 21, 2021

Do you have a link to your page? I think it could be done with pure CSS, it would be probably the easiest solution.

@fkoomek
Copy link

fkoomek commented Jul 21, 2021

Otherwise, this should work. Replace existing code with this. You will probably also need some CSS. If you want to change for example ordering of the categories check out this page https://docs.woocommerce.com/document/woocommerce-shortcodes/#section-14

/*Separate product categories from products and display 3 categories per row*/

add_action( 'init', 'subcat_custom_number' );
function subcat_custom_number() {
	// Remove the subcat <li>s from the old location.
	remove_filter( 'woocommerce_product_loop_start', 'woocommerce_maybe_show_product_subcategories' );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_category_custom', 10 );
}

function msc_product_loop_category_custom() {
	
	    $term_id  = get_queried_object_id();
        $taxonomy = 'product_cat';

        // Get subcategories of the current category
        $terms    = get_terms([
            'taxonomy'    => $taxonomy,
            'hide_empty'  => true,
            'parent'      => $term_id
        ]);


        // Loop through product subcategories WP_Term Objects
        foreach ( $terms as $term ) {
            $term_link = get_term_link( $term, $taxonomy );

            $output .= $term->term_id . ',';
        }
       $output2 = substr_replace($output, "", -1); //output ids of subcategories
	
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {

		echo do_shortcode('[product_categories ids="'. $output2 . '" columns="3"]'); //show subcategories
	}
}

@johnsimeon
Copy link

johnsimeon commented Jul 21, 2021

I will test it!
Thanks a lot!!!

@fkoomek
Copy link

fkoomek commented Jul 21, 2021

I updated the code. Now it works automatically, so you don't have to specify anything. Still, you'll probably need some CSS for a little bit of styling. Let me know if it works.

@anddilolz
Copy link

anddilolz commented Feb 4, 2022

Is it possible to add a divider or a shortcode between these two

    elements?

@fkoomek
Copy link

fkoomek commented Feb 4, 2022

function msc_product_loop_end() { $subcategories = woocommerce_maybe_show_product_subcategories(); if ( $subcategories ) { woocommerce_product_loop_end(); echo '<hr id="sub_divider">'; } }

You can try to add the


element to the last function. As I added the id attribute to the element, you can then style it to your needs.

@anddilolz
Copy link

anddilolz commented Feb 5, 2022

Absolutely wonderful, thanks a lot mate! A quick Google search also taught me I can use shortcodes with do_shortcode. I also managed to show a title before the subcategories as well by echoing it after the "woocommerce_product_loop_start".

@fkoomek
Copy link

fkoomek commented Feb 5, 2022

No problem. I just noticed missing quotation mark in my code (in id). Sorry, I fixed it 🙂

@anddilolz
Copy link

anddilolz commented Feb 8, 2022

Thanks a bunch! I ended up using do_shortcode so I didn't even realize. Now I have nice titles for both loops! But when there are no subcategories, the product loop doesn't have a title. Can I somehow add this in functions.php so I don't have to manually add it in each category?

EDIT: the following works, but it also adds a second title when there are subcategories.. I don't know how to modify it to my needs. :p

add_action( 'woocommerce_before_shop_loop', 'amc_add_content_before_loop' );
function amc_add_content_before_loop() {

	if ( is_product_category() ) {
		echo do_shortcode('[title text="PRODUCTS" tag_name="h2"]');
	}

}

@fkoomek
Copy link

fkoomek commented Feb 10, 2022

Hi. Do you have a link to your site please?

@anddilolz
Copy link

anddilolz commented Feb 10, 2022

Example with a category that has subcategories: [redacted]
No subcategories, no title :( : [redacted]

@fkoomek
Copy link

fkoomek commented Feb 10, 2022

And what is the full code you are currently using?

@anddilolz
Copy link

anddilolz commented Feb 10, 2022

Here's the current code:

/**
* Move WooCommerce subcategory list items into
* their own <ul> separate from the product <ul>.
*/

add_action( 'init', 'move_subcat_lis' );

function move_subcat_lis() {
	// Remove the subcat <li>s from the old location.
	remove_filter( 'woocommerce_product_loop_start', 'woocommerce_maybe_show_product_subcategories' );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_start', 40 );
	add_action( 'woocommerce_before_shop_loop', 'msc_maybe_show_product_subcategories', 50 );
	add_action( 'woocommerce_before_shop_loop', 'msc_product_loop_end', 60 );
}

/**
 * Conditonally start the product loop with a <ul> contaner if subcats exist.
 */
function msc_product_loop_start() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {
		woocommerce_product_loop_start(); echo do_shortcode('[title text="ALAKATEGORIAT" tag_name="h2"]');
	}
}

/**
 * Print the subcat <li>s in our new location.
 */
function msc_maybe_show_product_subcategories() {
	echo woocommerce_maybe_show_product_subcategories();
}

/**
 * Conditonally end the product loop with a </ul> if subcats exist.
 */
function msc_product_loop_end() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	if ( $subcategories ) {
		woocommerce_product_loop_end(); echo do_shortcode('[title text="TUOTTEET" tag_name="h2"]');
	} 
}

@fkoomek
Copy link

fkoomek commented Feb 10, 2022

Try to replace the end of the code with this:

/**
 * Conditonally end the product loop with a </ul> if subcats exist.
 */
function msc_product_loop_end() {
	$subcategories = woocommerce_maybe_show_product_subcategories();
	
	if ( $subcategories ) {
		woocommerce_product_loop_end();
		}
		
//if category has products, show title PRODUCTS title		
if (have_posts()){	
echo do_shortcode('[title text="TUOTTEET" tag_name="h2"]');
}
}

Also, I added a condition that the word Products will show only when the category has some products. If you want to delete this condition, put echo do_shortcode('[title text="TUOTTEET" tag_name="h2"]'); outside have_posts condition...

Is it what you were trying to achieve?

@anddilolz
Copy link

anddilolz commented Feb 10, 2022

Well I'll be damned, that sure did the trick! This was exactly what I was trying to achieve, thank you so much! :)

@fkoomek
Copy link

fkoomek commented Feb 10, 2022

Glad to help sir :)

@twoelevenjay
Copy link
Author

twoelevenjay commented Feb 11, 2022

@anddilolz and @fkoomek, you two are awesome!

@anddilolz
Copy link

anddilolz commented Feb 11, 2022

@twoelevenjay @fkoomek mad props to you both!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment