1

Code 1 - uses wp_list_categories to list product categories in columns of x categories up to a total of y, with a link to view all categories if there are more than y.

Code 2 - Later realising i needed thumbnails aswell, I tried using get_terms to retrieve the list and thumbnails. However it is just 1 long list and I am having a lot of trouble trying to apply the principles from the Code 1 to Code 2 to make it work.

This is Code 2:

<?php           
                $args = array(
                'taxonomy'   => 'product_cat',
                'title_li'   => '',
                'hide_empty' => 0,
                'echo'       => 0,
                'show_count' => 0,
                'style'      => '',
                'parent'    => 0
                );

$get_cats = get_terms( $args );
        ?>
<div class="design">
   <div class="menu">
       <ul class="hoverimage">
<?php
    foreach ( $get_cats as $cat ) {
    $thumbnail_id = get_term_meta( $cat->term_id, 'thumbnail_id', true ); 
    $image = wp_get_attachment_url( $thumbnail_id ); 

   if($image == "") {
   $image = get_bloginfo('url') . '/wp-content/uploads/woocommerce-placeholder-100x100.png';
    }
?>
    <li class="menu-item" data-image="<?php echo $image; ?>"><a href="<?php echo get_term_link( $cat ); ?>"><?php echo $cat->name; ?></a></li>
    <?php
      }
    ?>
       </ul>
   </div>
    <div class="zoom-preview">
        <div class='img_container'>
        </div>
    </div>
   <div class="clear"></div>
</div>

<script>
$(function(){
    var imageContainer = '.img_container',
        imageList      = '.hoverimage',
        maxWidth       = 'parent', // parent or specific css width/  
        defImage       = '<?php bloginfo('url'); ?>/wp-content/uploads/woocommerce-placeholder-100x100.png';

    var $imageContainer = $(imageContainer).eq(0);
    var $imageList      = $(imageList).eq(0);
    if (maxWidth === 'parent') { maxWidth = $imageContainer.width() + 'px'; }
    //Load images and set hover::
    $imageList.find('li').each(function(index){
        if (typeof $(this).data('image') === 'string') {
            $imageContainer.append(
                "<img id='imageToggle"+index+
                "' src='"+$(this).data('image')+
                "' style='max-width: "+maxWidth+"; display:none;' />"
            );
            $(this).data("image","imageToggle"+index);
            $(this).hover(
                function(){ loadImage($(this).data('image')); },
                function(){ loadImage('imageToggleDef'); }
            );  
        }
    });
    //Load default:
    $imageContainer.append(
                "<img id='imageToggleDef"+
                "' src='"+defImage+
                "' style='max-width: "+maxWidth+"' />"
    );
    //Toggle images:
    function loadImage(imageId) {
        $imageContainer.stop(true).fadeOut('fast', function(){
            $(this).find('img').hide();
            $(this).find('img#'+imageId).show();
            $(this).fadeIn('fast');
        });
    }

});
</script>


This is Code 1. I am trying to combine this with Code 2 while still using get_terms instead of wp_list_categories. Ive noticed that you cant explode the results from get_terms, but having trouble where $i seems to be a string and not a number....Its over my head and Im getting lots of errors.

// First, define these.
$max_cat_count = 32; // this is 'x'
$qty_per_column = 8; // this is 'y'

// Then the $args array.
$args = array(
    'taxonomy'   => 'product_cat',
    'number'     => $max_cat_count + 1, // keep the + 1
    'title_li'   => '',
    'hide_empty' => 0,
    'echo'       => 0,
    'style'      => '',
    // ... your other args here ...
);

// Get the categories list.
$get_cats = wp_list_categories( $args );

// Split the list into array items.
$cat_array = explode( "<br />", $get_cats );

$total = count( $cat_array );
$list_number = 1;
$_new_col = false;

foreach ( $cat_array as $i => $category ) {
    if ( $i >= $max_cat_count ) {
        break;
    }

    if ( $i % $qty_per_column === 0 ) {
        // Close previous column, if any.
        if ( $_new_col ) {
            echo '</div><!-- .cat_columns -->' . "\n";
        }

        // Open new column.
        $id = 'cat-col-' . $list_number;
        echo '<div class="cat_columns" id="' . $id . '">' . "\n";

        $_new_col = true;
        $list_number++; // increment the columns count
    }

    if ( $total > $max_cat_count && $i === $max_cat_count - 1 ) {
        // Make sure to change the # to the proper URL.
        echo "\t" . '<div><a href="#">View All</a></div>' . "\n";
    } else {
        echo "\t" . '<div>' . trim( $category ) . '</div>' . "\n";
    }
}
// Close last column, if any.
if ( $_new_col ) {
    echo '</div><!-- .cat_columns -->' . "\n";
}

This is what the output looks like: enter image description here

6
  • So referring to the "Code 1", where should the thumbnail appear? After the div with the category link/name?
    – Sally CJ
    Nov 12, 2019 at 11:46
  • before the very first column of categories is listed, to the left.
    – aye cee
    Nov 12, 2019 at 12:58
  • You mean, a list of thumbnails to the left of the first .cat_columns like this?.. If yes, you can just use CSS?
    – Sally CJ
    Nov 12, 2019 at 14:29
  • There is actually some more to the code which uses the data tag to display the relevant thumbnail on hover of the category. I left it out because I thought it might confuse things, but have now added it above to Code 2.
    – aye cee
    Nov 12, 2019 at 15:30
  • 1
    Have just updated above to show an image of the output.
    – aye cee
    Nov 13, 2019 at 2:58

1 Answer 1

1

Modified PHP code

<?php
// First, define these.
$max_cat_count = 32; // this is 'x'
$qty_per_column = 8; // this is 'y'

// Then the $args array.
$args = array(
    'taxonomy'   => 'product_cat',
    'number'     => $max_cat_count + 1, // keep the + 1
    'hide_empty' => 0,
    // ... your other args here ...
);

// Get the categories list.
$get_cats = get_terms( $args );
$get_cats = ( ! is_wp_error( $get_cats ) ) ? $get_cats : [];

// Default thumbnail URL.
$default_thumb = content_url( '/uploads/woocommerce-placeholder-100x100.png' );

$total = count( $get_cats );
$list_number = 1;
$_new_col = false;

$columns = '';
foreach ( $get_cats as $i => $cat ) {
    if ( $i >= $max_cat_count ) {
        break;
    }

    if ( $i % $qty_per_column === 0 ) {
        // Close previous column, if any.
        if ( $_new_col ) {
            $columns .= '</ul></div><!-- .cat_columns -->';
        }

        // Open new column.
        $id = 'cat-col-' . $list_number;
        $columns .= '<div class="menu cat_columns" id="' . $id . '">';
        $columns .= '<ul class="hoverimage">';

        $_new_col = true;
        $list_number++; // increment the columns count
    }

    if ( $total > $max_cat_count && $i === $max_cat_count - 1 ) {
        // Make sure to change the # to the proper URL.
        $columns .= '<li class="menu-item"><a href="#">View All</a></li>';
    } else {
        $thumbnail_id = get_term_meta( $cat->term_id, 'thumbnail_id', true );
        $image = $thumbnail_id ? wp_get_attachment_url( $thumbnail_id ) : '';
        // If $image is empty, just let it be. JS will set it to $default_thumb.
        // That way, the page source/HTML will be less.

        $link = '<a href="' . esc_url( get_term_link( $cat ) ) . '">' . esc_html( $cat->name ) . '</a>';
        $columns .= '<li class="menu-item" data-image="' . esc_url( $image ) . '">' . $link . '</li>';
    }
}
// Close last column, if any.
if ( $_new_col ) {
    $columns .= '</ul></div><!-- .cat_columns -->';
}
?>
<div class="design">
    <div class="zoom-preview">
        <div class="img_container" data-default_thumb="<?php echo esc_url( $default_thumb ); ?>">
        </div>
    </div>
    <?php echo $columns; ?>
    <div class="clear"></div>
</div>

Modified jQuery code

jQuery(function( $ ){
    var imageContainer = '.img_container',
        imageList      = '.hoverimage',
        maxWidth       = '100%'; // parent or specific css width;

    var $imageContainer = $(imageContainer).eq(0);
    var $imageList      = $(imageList);

    /*
     * I moved this var to here, and use data-default_thumb to specify the
     * default thumbnail URL. That way, you can - and should - avoid using
     * PHP in JS. So your opening DIV tag would look like:
     * <div class="img_container" data-default_thumb="http://example.com/image.png">
     */
    var defImage        = $imageContainer.data( 'default_thumb' );

    if (maxWidth === 'parent') {
        maxWidth = $imageContainer.width() + 'px';
    }

    // Load images and set hover::
    $imageList.find('li[data-image]').each(function(index){
        if (typeof $(this).data('image') === 'string') {
            var thumb_url = $( this ).data( 'image' ) || defImage;
            $imageContainer.append(
                "<img id='imageToggle"+index+
                "' src='"+ thumb_url +
                "' style='max-width: "+maxWidth+"; max-height:100%; display:none;' />"
            );
            $(this).data("image","imageToggle"+index);
            $(this).hover(
                function(){ loadImage($(this).data('image')); },
                function(){ loadImage('imageToggleDef'); }
            );
        }
    });

    // Load default:
    $imageContainer.append(
        "<img id='imageToggleDef"+
        "' src='"+defImage+
        "' style='max-width: "+maxWidth+"; max-height:100%' />"
    );

    // Toggle images:
    function loadImage(imageId) {
        $imageContainer.stop(true).fadeOut('fast', function(){
            $(this).find('img').hide();
            $(this).find('img#'+imageId).show();
            $(this).fadeIn('fast');
        });
    }
});

Sample CSS code

.cat_columns {
    float: left;
    width: 20%;
}

.zoom-preview {
    float: left;
    width: 20%;
    text-align: center;
}

.clear {
    clear: both;
}

So basically, just "play" with the CSS to get the proper layout — e.g. without the thumbnail preview, the .cat_columns would have a width of 25% (to have a total of 4 columns in a row).

And after you implemented all the code, the generated markup/HTML would look something like so:

<div class="design">
  <div class="zoom-preview">
    <div class="img_container" data-default_thumb="{Default thumbnail URL}">
      {Thumbnails added via JS}
    </div>
  </div>
  <div class="menu cat_columns">
    <ul class="hoverimage">
      <li class="menu-item" data-image="{Thumbnail URL}"><a href="{Category URL}">{Category Name}</a></li>
      <li class="menu-item" data-image="{Thumbnail URL}"><a href="{Category URL}">{Category Name}</a></li>
      ...
    </ul>
  </div>
  <div class="menu cat_columns">
    <ul class="hoverimage">
      <li class="menu-item" data-image="{Thumbnail URL}"><a href="{Category URL}">{Category Name}</a></li>
      <li class="menu-item" data-image="{Thumbnail URL}"><a href="{Category URL}">{Category Name}</a></li>
      ...
    </ul>
  </div>
  ...
  <div class="clear"></div>
</div>

Try a demo on JS Fiddle

Additional Notes

  1. The arguments title_li, echo, style and show_count are to be used with wp_list_categories(), and have no effects in get_terms().

  2. I used content_url() to retrieve the WordPress "content" URL which looks like http://example.com/wp-content.

  3. After you've tested everything, you should put the jQuery script in an external script and use the wp_enqueue_scripts hook and wp_enqueue_script() to load the script. (I've recently posted this answer which might help you with wp_enqueue_script().)

UPDATE

If you want the default thumbnail to be loaded immediately, then you should manually add the <img> to the HTML:

<!-- With this version, no need for data-default_thumb -->
<div class="img_container">
        <img id="imageToggleDef" src="<?php echo esc_url( $default_thumb ); ?>" alt="" style="max-width: 100%; max-height: 100%" />
</div>

You can try a demo here and be sure to use the JS there and not the one originally posted above.

7
  • This works perfectly. Thanks so much for taking the time to write such a comprehensive answer! This has also solved another issue I was having with the thumbnails showing up intermittently and requiring multiple page refreshes. I will follow your instructions for the external script as I think that may also solve the slight delay in the default thumbnail showing up.
    – aye cee
    Nov 14, 2019 at 0:34
  • I'm not sure about that, but I forgot that (in the JS) the $imageList.find('li') should be $imageList.find('li[data-image]') :)
    – Sally CJ
    Nov 14, 2019 at 1:33
  • Ok have changed that. I have used wp_enqueue_scripts hook and wp_enqueue_script() to load the script in my functions.php. Is there any need to include a script src url in the php file where the rest of the code is? The php is included into a drop down menu. Also as the default thumbnail is delayed, the category columns float left and then shift back once it loads. Is there something I have done wrong with the enqueuing? Is it necessary to load jquery library in wordpress?
    – aye cee
    Nov 14, 2019 at 2:00
  • Check the updated answer (basically just that "UPDATE" part) and if you still have questions, let me know. Edit: That second fiddle/demo still has data-default_thumb, but it's not needed - I just forgot to remove it.
    – Sally CJ
    Nov 14, 2019 at 2:06
  • 1
    Thanks again Sally. Ive looked into it and its called "image reflow" incase anyone else has the same issue.
    – aye cee
    Nov 15, 2019 at 2:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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