Recently I had a project where a client needed to offer a variable product with extra information about each variation displayed, and this needed to change as the selector changed the variation. The product page would be linked to from another page on their site, with the desired variation pre-loaded in the drop-down selector on arrival. However, once there you could of course play with the drop-down to select other variations.
Let’s break down what I did to get everything working.
Here’s a quick summary:
- create a textarea that gets added to the variations editor in the Woocommerce product
- hook it in properly so it responds to javascript and normal page loading
- get the contents of the textareas and inject them into the product page on the front end
- hide all of them except the one we want to show based on the drop-down selector
The variable product custom field text area:
First, I wanted to create a textarea for each product variation for the client to be able to enter a description. Originally, I simply grabbed a plugin that did just that. But after we had a bunch of nightmares with malware and site hacking, we wiped everything and started over on a new host where we wanted to stay as clean as possible, limit our plugins, and by then that plugin had been removed from the repository. I found great instruction on adding custom fields to variable products by Remi Corson which looked easy to implement. Using his post as a guide I added a function to create the textarea and hook in properly to Woocommerce. And, as he points out, we have to deal with the Ajax creation of new variations, so everything is done twice as you’ll see. This goes in the functions file.
//Product Variation Description
//Display Fields
add_action( 'woocommerce_product_after_variable_attributes', 'variable_fields', 10, 3 );
//JS to add fields for new variations
add_action( 'woocommerce_product_after_variable_attributes_js', 'variable_fields_js' );
//Save variation fields
add_action( 'woocommerce_process_product_meta_variable', 'save_variable_fields', 10, 1 );
//Create new fields for variations
function variable_fields( $loop, $variation_data, $variation ) {
?>
<tr>
<td>
<?php
// Textarea
woocommerce_wp_textarea_input(
array(
'id' => '_textarea['.$loop.']',
'label' => __( 'Description', 'woocommerce' ),
'placeholder' => '',
'value' => get_post_meta( $variation->ID, '_textarea', true ),
)
);
?>
</td>
</tr>
<?php
}
// Create new fields for new variations
function variable_fields_js() {
?>
<tr>
<td>
<?php
// Textarea
woocommerce_wp_textarea_input(
array(
'id' => '_textarea[ + loop + ]',
'label' => __( 'Description', 'woocommerce' ),
'placeholder' => '',
'value' => '',
)
);
?>
</td>
</tr>
<?php
}
//Save new fields for variations
function save_variable_fields( $post_id ) {
if (isset( $_POST['variable_sku'] ) ) :
$variable_sku = $_POST['variable_sku'];
$variable_post_id = $_POST['variable_post_id'];
// Textarea
$_textarea = $_POST['_textarea'];
for ( $i = 0; $i < sizeof( $variable_sku ); $i++ ) :
$variation_id = (int) $variable_post_id[$i];
if ( isset( $_textarea[$i] ) ) {
update_post_meta( $variation_id, '_textarea', stripslashes( $_textarea[$i] ) );
}
endfor;
endif;
}
The code gave me this in my variable products:
Get the Description to use on the Product Page:
To get the description I had created for them and add it to the product page, I’m using the all-powerful get_post_meta, and also grabbing the price, since they are all different. I’m doing this from the functions file and hooking in to the ‘before add to cart’ in Woocommerce. As you’ll notice this is going to write them all out on the page, which is not what we want, but I added some javascript to hide them all, then turn on each one depending on what’s selected in the drop-down. This also goes in the functions file.
function my_theme_function_woocommerce_before_add_to_cart_form() {
// get the product
global $product;
// Get the post IDs of all the product variations
$variation_ids = $product->children;
// check if we have variations
if ( empty( $variation_ids ) )
return;
// walk the variations
foreach( $variation_ids as $variation_id ) {
$variable_product= new WC_Product_Variation( $variation_id );
$description = get_post_meta($variation_id, '_textarea');
$price = $variable_product ->regular_price;
echo '<div id="variation-' . $variation_id . '" style="display: none;" class="camp-description">';
echo '<p>'.$description[0].'</p>';
echo '<b> $'.$price.'</b>';
echo '</div>';
}
}
add_action( 'woocommerce_before_add_to_cart_form', 'my_theme_function_woocommerce_before_add_to_cart_form', 5 );
jQuery to show the one we want:
I needed this solution fast, so I resorted to some jQuery trickery to display the one description we want out of all the descriptions hidden on the page, based on what option was selected in the drop-down. This goes in a custom.js file that adds various extra jQuery I need on the site. It should be wrapped in the usual ‘jQuery(document).ready(function ($) {}’.
//Summer Camp products get variation ID to show/hide descriptions
setTimeout(function(){
variationid= '';
if(typeof $('input[name=variation_id]').val() != "undefined" && $('input[name=variation_id]').val() !== null) {
variationid = $('input[name=variation_id]').val();
$('div#variation-'+variationid).show();
}
}, 100);
setTimeout(function(){
$('body').on('change', '#camps',function(){
$('div#variation-'+variationid).hide();
setTimeout(function(){
variationid = $('input[name="variation_id"]').val();
$('div#variation-'+variationid).show();
}, 100)
});
}, 200);
SIDE NOTE: to land on the page in the first place the user is clicking through from another page. The link they click is set up to leverage Woocommerce pre-filling the drop-down with a certain variation. The href in the link looks like this:
/summer-camp-registration/?attribute_camps=early-childhood-session-1-latin-celebrations
Wrap up:
I’m sure there are some more elegant ways to do things, but this got me what I needed in a hurry, and seems to be working fine. I’m not crazy about having the timeouts in the js to allow for all the other Woocommerce js to work, but again it’s not too much of a drag on the user experience I think. If I get time I’ll go through more explanation of how things work on here. Here is the product page where you can see this working: https://www.waldorfsandiego.org/summer-camp-registration/?attribute_camps=early-childhood-session-1-latin-celebrations