Skip to content

Instantly share code, notes, and snippets.

@corsonr
Created September 28, 2016 08:33
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save corsonr/37e5430896df10252b0e03c60f00d6a3 to your computer and use it in GitHub Desktop.
Save corsonr/37e5430896df10252b0e03c60f00d6a3 to your computer and use it in GitHub Desktop.
Display WooCommerce product variations dropdown select on the shop page
<?php
// Display variations dropdowns on shop page for variable products
add_filter( 'woocommerce_loop_add_to_cart_link', 'woo_display_variation_dropdown_on_shop_page' );
function woo_display_variation_dropdown_on_shop_page() {
global $product;
if( $product->is_type( 'variable' )) {
$attribute_keys = array_keys( $product->get_attributes() );
?>
<form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $product->get_available_variations() ) ) ?>">
<?php do_action( 'woocommerce_before_variations_form' ); ?>
<?php if ( empty( $product->get_available_variations() ) && false !== $product->get_available_variations() ) : ?>
<p class="stock out-of-stock"><?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
<?php else : ?>
<table class="variations" cellspacing="0">
<tbody>
<?php foreach ( $product->get_attributes() as $attribute_name => $options ) : ?>
<tr>
<td class="label"><label for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label></td>
<td class="value">
<?php
$selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
echo end( $attribute_keys ) === $attribute_name ? apply_filters( 'woocommerce_reset_variations_link', '<a class="reset_variations" href="#">' . __( 'Clear', 'woocommerce' ) . '</a>' ) : '';
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
<div class="single_variation_wrap">
<?php
/**
* woocommerce_before_single_variation Hook.
*/
do_action( 'woocommerce_before_single_variation' );
/**
* woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
* @since 2.4.0
* @hooked woocommerce_single_variation - 10 Empty div for variation data.
* @hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
*/
do_action( 'woocommerce_single_variation' );
/**
* woocommerce_after_single_variation Hook.
*/
do_action( 'woocommerce_after_single_variation' );
?>
</div>
<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
<?php endif; ?>
<?php do_action( 'woocommerce_after_variations_form' ); ?>
</form>
<?php } else {
echo sprintf( '<a rel="nofollow" href="%s" data-quantity="%s" data-product_id="%s" data-product_sku="%s" class="%s">%s</a>',
esc_url( $product->add_to_cart_url() ),
esc_attr( isset( $quantity ) ? $quantity : 1 ),
esc_attr( $product->id ),
esc_attr( $product->get_sku() ),
esc_attr( isset( $class ) ? $class : 'button' ),
esc_html( $product->add_to_cart_text() )
);
}
}
@MadhuriUlli
Copy link

hi
i want to display price also based on the selection of variations

@joshgriffey
Copy link

Is anyone having any issues with this after latest woocommerce updates?

@mattmintun
Copy link

mattmintun commented Jul 20, 2018

Hey all, I've seen in this thread many who want to show the PRICE of the variation once selected. Very easy to do! The issue is that the JS that updates the variation price isn't enqueued on any other page but the product page. So enqueue it!

Add this:
wp_enqueue_script('wc-add-to-cart-variation');

right after:
<?php if( $product->is_type( 'variable' )) {

Now prices will show up! I did realize that when I changed the DOM structure of the variation table (literally just removing the table and making it a div instead) it broke the JS. And for those who want updated code based on what @lexzz changed (so this actually works), here's the full updated code that actually works (my "else" statement might differ from the OP):

<?php if( $product->is_type( 'variable' )) {
	
  wp_enqueue_script('wc-add-to-cart-variation');

  $attribute_keys = array_keys( $product->get_variation_attributes() );

  ?>

  <form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $product->get_available_variations() ) ) ?>">
    <?php do_action( 'woocommerce_before_variations_form' ); ?>

    <?php if ( empty( $product->get_available_variations() ) && false !== $product->get_available_variations() ) : ?>
      <p class="stock out-of-stock">
        <?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?>
      </p>
    <?php else : ?>
      <table class="variations" cellspacing="0">
        <tbody>
          <?php foreach ( $product->get_variation_attributes() as $attribute_name => $options ) : ?>
          <tr>
            <td class="label"><label for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label></td>
            <td class="value">
              <?php
                $selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
                wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
              ?>
            </td>
          </tr>
          <?php endforeach;?>
        </tbody>
      </table>

      <?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>

      <div class="single_variation_wrap">
        <?php
          /**
           * woocommerce_before_single_variation Hook.
           */
          do_action( 'woocommerce_before_single_variation' );

          /**
           * woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
           * @since 2.4.0
           * @hooked woocommerce_single_variation - 10 Empty div for variation data.
           * @hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
           */
          do_action( 'woocommerce_single_variation' );

          /**
           * woocommerce_after_single_variation Hook.
           */
          do_action( 'woocommerce_after_single_variation' );
        ?>
      </div>

      <?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
      
    <?php endif; ?>

    <?php do_action( 'woocommerce_after_variations_form' ); ?>
    
  </form>

  <?php } else { ?>
   
    <form class="cart" action="<?php echo esc_url( get_permalink() ); ?>" method="post" enctype='multipart/form-data'>
      <span class="price"><?php echo $product->get_price_html(); ?></span>
      <?php
        woocommerce_quantity_input( array(
          'min_value'   => apply_filters( 'woocommerce_quantity_input_min', $product->get_min_purchase_quantity(), $product ),
          'max_value'   => apply_filters( 'woocommerce_quantity_input_max', $product->get_max_purchase_quantity(), $product ),
          'input_value' => isset( $_POST['quantity'] ) ? wc_stock_amount( $_POST['quantity'] ) : $product->get_min_purchase_quantity(),
        ) );
      ?>
      <button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="single_add_to_cart_button alt"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>

    </form>
    
<?php } ?>

@gangesh
Copy link

gangesh commented Sep 5, 2018

@mattmintun Thank you for your code.
I tried but JS doesn't seem to work. On selection Add to cart button remains disabled and price doesn't show.
You might want to re-check code and share?

Thanks

@DNAOnline
Copy link

Any chance this code could include not showing variations that are out of stock in the drop down?

@PolVales
Copy link

Thank you for your solution!
It's only a problem, the clear variations is not working. When you pick any variations, it change all predefined variations.

Thanks

@karthikw3cert
Copy link

Nandri Sago...

Great Solution for my Issue...

@jgroeier
Copy link

jgroeier commented Aug 2, 2019

Hi, This is working for me but is it also possible to do it with AJAX add to cart?

@jgroeier
Copy link

jgroeier commented Aug 2, 2019

I am using this code for my simple products to AJAX add to cart:

//Ajax add to cart
add_filter( 'woocommerce_loop_add_to_cart_link', 'quantity_inputs_for_loop_ajax_add_to_cart', 10, 2 );
function quantity_inputs_for_loop_ajax_add_to_cart( $html, $product ) {
    if ( $product && $product->is_type( 'simple' ) && $product->is_purchasable() && $product->is_in_stock() && ! $product->is_sold_individually() ) {
        // Get the necessary classes
        $class = implode( ' ', array_filter( array(
            'button',
            'product_type_' . $product->get_type(),
            $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',
            $product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : '',
        ) ) );

        // Adding embeding <form> tag and the quantity field
        $html = sprintf( '%s%s<a rel="nofollow" href="%s" data-quantity="%s" data-product_id="%s" data-product_sku="%s" class="%s">%s</a>%s',
            '<form class="cart">',
            woocommerce_quantity_input( array(), $product, false ),
            esc_url( $product->add_to_cart_url() ),
            esc_attr( isset( $quantity ) ? $quantity : 1 ),
            esc_attr( $product->get_id() ),
            esc_attr( $product->get_sku() ),
            esc_attr( isset( $class ) ? $class : 'button' ),
            esc_html( $product->add_to_cart_text() ),
            '</form>'
        );
    }
    return $html;
}

@santanup789
Copy link

In if condition the button text is not coming properly.
Screenshot_2020-02-14 Donate - FUSECHANGE

So I changed the code in few lines. And the else part is not working in some places. So changed it too. But i didn't make it globally. I made a snippet for a single category and paste it in that snippet.

           `<?php if( $product->is_type( 'variable' )) { wp_enqueue_script('wc-add-to-cart-variation');
		  $attribute_keys = array_keys( $product->get_variation_attributes() );
		  ?>
		  <form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $product->get_available_variations() ) ) ?>">
			<?php do_action( 'woocommerce_before_variations_form' ); ?>
			<?php if ( empty( $product->get_available_variations() ) && false !== $product->get_available_variations() ) : ?>
			  <p class="stock out-of-stock">
				<?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?>
			  </p>
			<?php else : ?>
			  <table class="variations" cellspacing="0">
				<tbody>
				  <?php foreach ( $product->get_variation_attributes() as $attribute_name => $options ) : ?>
				  <tr>
					<td class="label"><label for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label></td>
					<td class="value">
					  <?php
						$selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
						wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
					  ?>
					</td>
				  </tr>
				  <?php endforeach;?>
				</tbody>
			  </table>
			<div class="single_variation_wrap">
				<?php $result = woocommerce_single_variation_add_to_cart_button();?> 
			</div>
			  <?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
			<?php endif; ?>
			<?php do_action( 'woocommerce_after_variations_form' ); ?>
		  </form>
		  <?php } else {
			do_action( 'woocommerce_before_add_to_cart_form' ); ?>
			<form class="cart" action="<?php echo esc_url( apply_filters( 'woocommerce_add_to_cart_form_action', $product->get_permalink() ) ); ?>" method="post" enctype='multipart/form-data'>
				<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
				<?php
				do_action( 'woocommerce_before_add_to_cart_quantity' );
				woocommerce_quantity_input( array(
					'min_value'   => apply_filters( 'woocommerce_quantity_input_min', $product->get_min_purchase_quantity(), $product ),
					'max_value'   => apply_filters( 'woocommerce_quantity_input_max', $product->get_max_purchase_quantity(), $product ),
					'input_value' => isset( $_POST['quantity'] ) ? wc_stock_amount( wp_unslash( $_POST['quantity'] ) ) : $product->get_min_purchase_quantity(), // WPCS: CSRF ok, input var ok.
				) );
				do_action( 'woocommerce_after_add_to_cart_quantity' );
				?>
				<button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="single_add_to_cart_button button alt xx"><?php echo esc_html( $product->add_to_cart_text() ); ?></button>
				<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
			</form>
			<?php do_action( 'woocommerce_after_add_to_cart_form' );
		} ?>`

And the final result in 'if condition' is's so satisfying.. just see
Screenshot_2020-02-14 Donate - FUSECHANGE(1)

Similar feeling in else part..
Screenshot_2020-02-14 Donate - FUSECHANGE(2)

All fields are dynamic. I developed one kind of donation system using woocommerce. And this code helped me to solved my issue.
Thanks a lot man.

@sssperling
Copy link

I used the code and it sort of works it puts the attribute (days needed) but it won't show the values it's just blank the options are 1 day, 2 day, weekend week. Any suggestions on a fix? Has anyone else had this issue?

Screen Shot 2020-03-11 at 11 15 55 AM

@santanup789
Copy link

@sssperling
Copy link

sssperling commented Mar 11, 2020

santanup789 . Thanks I did try your code too, I got errors when I used it the error I received is "syntax error, unexpected '' (T_ENCAPSED_AND_WHITESPACE), expecting '-' or identifier (T_STRING) or variable (T_VARIABLE) or number (T_NUM_STRING)"
Not sure what I would cross check, sorry for the stupid question.

@MrTeaRSA
Copy link

MrTeaRSA commented Apr 1, 2020

sssperling - Did you manage to fix it?

santanup789 - Please assist, i have tried all the codes on this page and all of it crash my website, please help, please paste a working code.

Thanks a mill

@sssperling
Copy link

MrTeaRSA, not it didn't work I ended up buying a plugin to do it. I tried a ton of differnt codes and nothing worked on my stie.

@santanup789
Copy link

I did work again for your issue using my previous code. It's still working for me. That's all I have. Can you share your code?? And make sure that you created attributes of products that are showing in the details page and working.

@MrTeaRSA
Copy link

MrTeaRSA commented Apr 2, 2020

santanup789 Hi, thanks for your reply.

I copied your code, pasted into my theme folder, functions.php at the end. I'n no expert in coding so i dont know what to do here. There was a error in your code when i pasted it.

I have created my variations and it says select option on the shop page, the price is gone.

image - See the image.

Please let me know what i need to do step by step if you can and repost the exact code i need to copy.

Thanks a lot for your help.

@drinklogo
Copy link

Can you make this type of drop down list with content quantity price discount and two-stage? https://packhelp.com/mailer-box-eco/

@thiagoRcosta
Copy link

Hello, i'm using variable product, but when i click in buy button, the quantity value change in all products. Exists one method for reset the quantity values after clicked in buy?

My code to show variable products:

function iconic_change_loop_add_to_cart() {
remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10 );
add_action( 'woocommerce_after_shop_loop_item', 'iconic_template_loop_add_to_cart', 10 );
}

add_action( 'init', 'iconic_change_loop_add_to_cart', 10 );

/**

  • Use single add to cart button for variable products.
    */
    function iconic_template_loop_add_to_cart() {
    global $product;

    if ( ! $product->is_type( 'variable' ) ) {
    woocommerce_template_loop_add_to_cart();
    return;
    }

add_action( 'woocommerce_single_variation', 'woocommerce_single_variation_add_to_cart_button', 20 );

woocommerce_template_single_add_to_cart();

}

function iconic_add_to_cart_form_action( $redirect ) {
if ( ! is_archive() ) {
return $redirect;
}

return '';

}

add_filter( 'woocommerce_add_to_cart_form_action', 'iconic_add_to_cart_form_action' );

This image show state of products after added to cart
fugure

@gerrgg
Copy link

gerrgg commented Jul 9, 2020

If you just want the select without any of the additional markup.

$product_id = 1234;
wcfbt_get_variation_dropdown( $product_id );

function wcfbt_get_variation_dropdown( $product_id ){
    /**
     * Create a inline dropdown for selecting available product variations
     */
    $product = wc_get_product( $product_id );

    // if product exists and has available variations to list.
    if( $product && ! empty( $product->get_variation_attributes() ) ){

        foreach ( $product->get_variation_attributes() as $attribute_name => $options ) : ?>

            <?php
                echo '<strong>' . wc_attribute_label( $attribute_name ) . ': </strong>';
                $selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
                wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
            ?>
            
        <?php endforeach;
        
    }

}

@dermotgar
Copy link

Hi, this is a great solution. I am trying to get the "Add to basket" button for variable products work using ajax so not to reload the page. Does any body have any advice on this? Thanks in advance.

@ignitos
Copy link

ignitos commented Dec 22, 2020

Hi,
first of all many thanks for this code.
need some help to make some change in the code, i'm not that good in the coding.
want to move the price above the dropdown box, currently it shows below the dropdown box.

thanks in advance.

@morceaudebois
Copy link

Having the same problem than @thiagoRcosta here, works great but when I add a product to the cart, the pages refreshes and all variation selects on the page end up with the same choice selected.

@morceaudebois
Copy link

For those who have the same problem as @thiagoRcosta and I, you can add that bit of JS somewhere on your site:

document.addEventListener("DOMContentLoaded", function() {

    let quantityInputs = document.querySelectorAll('input[name="quantity"]');
    quantityInputs.forEach(function(quantityInput) {
        quantityInput.value = '1';
    });

    let variationChoices = document.querySelectorAll('.value select');
    variationChoices.forEach(function(variationChoice) {
        variationChoice.value = '';
    });

});

It definitely isn't the most elegant solution, but at least it hides the problem until someone smarter than me can fix it 😬

@mtachaudhary
Copy link

How I can display product variations dropdown select on the Cart page?
Cart-–-MeetOcto

@sonnesworld
Copy link

sonnesworld commented Sep 2, 2021

Hi, this code works exactly how I want. Many thanks! I have only one problem (using Jupiter X theme with Elementor editor and JetWooBuilder): For single products it shows 2 add to cart buttons. How can I remove this? I don't want to add single products with variations to fix this bug. It seems the first add to cart button ignores the stock status, the second doesn't ignore it but redirects to product page which I don't want to show. It has to show and work like the product above. So the second button works correct but I want to show the price and stock status like for variable products and don't want to redirect to product page if not enough products are on stock. Many thanks.

Video: https://drive.google.com/file/d/1q-UE7ohYhykpTEU2_GjtHqPTvcwE8TEo/view?usp=sharing

Single product:
image

Added code:
// Display variations dropdowns on shop page for variable products
add_filter( 'woocommerce_loop_add_to_cart_link', 'woo_display_variation_dropdown_on_shop_page' );

function woo_display_variation_dropdown_on_shop_page() {

global $product;

if( $product->is_type( 'variable' )) {
    
wp_enqueue_script('wc-add-to-cart-variation');    

$attribute_keys = array_keys( $product->get_attributes() );
?>

<form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $product->get_available_variations() ) ) ?>">
	<?php do_action( 'woocommerce_before_variations_form' ); ?>

	<?php if ( empty( $product->get_available_variations() ) && false !== $product->get_available_variations() ) : ?>
		<p class="stock out-of-stock"><?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
	<?php else : ?>
		<table class="variations" cellspacing="0">
			<tbody>
				<?php foreach ( $product->get_variation_attributes() as $attribute_name => $options ) : ?>
					<tr>
						<td class="label"><label for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label></td>
						<td class="value">
							<?php
								$selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
								wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
								echo end( $attribute_keys ) === $attribute_name ? apply_filters( 'woocommerce_reset_variations_link', '<a class="reset_variations" href="#">' . __( 'Clear', 'woocommerce' ) . '</a>' ) : '';
							?>
						</td>
					</tr>
				<?php endforeach;?>
			</tbody>
		</table>

		<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>

		<div class="single_variation_wrap">
			<?php
				/**
				 * woocommerce_before_single_variation Hook.
				 */
				do_action( 'woocommerce_before_single_variation' );

				/**
				 * woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
				 * @since 2.4.0
				 * @hooked woocommerce_single_variation - 10 Empty div for variation data.
				 * @hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
				 */
				do_action( 'woocommerce_single_variation' );

				/**
				 * woocommerce_after_single_variation Hook.
				 */
				do_action( 'woocommerce_after_single_variation' );
			?>
		</div>

		<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
	<?php endif; ?>

	<?php do_action( 'woocommerce_after_variations_form' ); ?>
</form>
	
<?php } else {
	
echo sprintf( '<a rel="nofollow" href="%s" data-quantity="%s" data-product_id="%s" data-product_sku="%s" class="%s">%s</a>',
		esc_url( $product->add_to_cart_url() ),
		esc_attr( isset( $quantity ) ? $quantity : 1 ),
		esc_attr( $product->id ),
		esc_attr( $product->get_sku() ),
		esc_attr( isset( $class ) ? $class : 'button' ),
		esc_html( $product->add_to_cart_text() )
	);
}

}

@DAHOTOYIII
Copy link

can this be added as shortcode?

@titodevera
Copy link

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