WooCommerce Variations as Radio Buttons

Is it possible within WooCommerce to change the variations dropdown into radio buttons without having to work with a plugin? I would like to have the following setup on the variations section:

  • 1 liter (10€)
  • 2 liter (20€)
  • 3 Liter (25€)

The price at the bottom should be automatically changed when you select an option.

Related posts

4 comments

  1. EDIT: added variation_check() and JS variation checking thanks to @ThomasB!

    EDIT2: make sure variation_check() also checks for backorder status to allow selection when a product can be backordered.

    The best way I managed to get this working is to add radio button markup directly after the select dropdowns and then hide the select dropdowns using CSS. You’ll also need some custom JS to trigger the hidden select value changes so that your price will change according to the radio button you select.

    Here’s how I added the markup:

    function variation_radio_buttons($html, $args) {
      $args = wp_parse_args(apply_filters('woocommerce_dropdown_variation_attribute_options_args', $args), array(
        'options'          => false,
        'attribute'        => false,
        'product'          => false,
        'selected'         => false,
        'name'             => '',
        'id'               => '',
        'class'            => '',
        'show_option_none' => __('Choose an option', 'woocommerce'),
      ));
    
      if(false === $args['selected'] && $args['attribute'] && $args['product'] instanceof WC_Product) {
        $selected_key     = 'attribute_'.sanitize_title($args['attribute']);
        $args['selected'] = isset($_REQUEST[$selected_key]) ? wc_clean(wp_unslash($_REQUEST[$selected_key])) : $args['product']->get_variation_default_attribute($args['attribute']);
      }
    
      $options               = $args['options'];
      $product               = $args['product'];
      $attribute             = $args['attribute'];
      $name                  = $args['name'] ? $args['name'] : 'attribute_'.sanitize_title($attribute);
      $id                    = $args['id'] ? $args['id'] : sanitize_title($attribute);
      $class                 = $args['class'];
      $show_option_none      = (bool)$args['show_option_none'];
      $show_option_none_text = $args['show_option_none'] ? $args['show_option_none'] : __('Choose an option', 'woocommerce');
    
      if(empty($options) && !empty($product) && !empty($attribute)) {
        $attributes = $product->get_variation_attributes();
        $options    = $attributes[$attribute];
      }
    
      $radios = '<div class="variation-radios">';
    
      if(!empty($options)) {
        if($product && taxonomy_exists($attribute)) {
          $terms = wc_get_product_terms($product->get_id(), $attribute, array(
            'fields' => 'all',
          ));
    
          foreach($terms as $term) {
            if(in_array($term->slug, $options, true)) {
              $id = $name.'-'.$term->slug;
              $radios .= '<input type="radio" id="'.esc_attr($id).'" name="'.esc_attr($name).'" value="'.esc_attr($term->slug).'" '.checked(sanitize_title($args['selected']), $term->slug, false).'><label for="'.esc_attr($id).'">'.esc_html(apply_filters('woocommerce_variation_option_name', $term->name)).'</label>';
            }
          }
        } else {
          foreach($options as $option) {
            $id = $name.'-'.$option;
            $checked    = sanitize_title($args['selected']) === $args['selected'] ? checked($args['selected'], sanitize_title($option), false) : checked($args['selected'], $option, false);
            $radios    .= '<input type="radio" id="'.esc_attr($id).'" name="'.esc_attr($name).'" value="'.esc_attr($option).'" id="'.sanitize_title($option).'" '.$checked.'><label for="'.esc_attr($id).'">'.esc_html(apply_filters('woocommerce_variation_option_name', $option)).'</label>';
          }
        }
      }
    
      $radios .= '</div>';
        
      return $html.$radios;
    }
    add_filter('woocommerce_dropdown_variation_attribute_options_html', 'variation_radio_buttons', 20, 2);
    
    function variation_check($active, $variation) {
      if(!$variation->is_in_stock() && !$variation->backorders_allowed()) {
        return false;
      }
      return $active;
    }
    add_filter('woocommerce_variation_is_active', 'variation_check', 10, 2);
    

    And here’s the JS I used:

    $(document).on('change', '.variation-radios input', function() {
      $('.variation-radios input:checked').each(function(index, element) {
        var $el = $(element);
        var thisName = $el.attr('name');
        var thisVal  = $el.attr('value');
        $('select[name="'+thisName+'"]').val(thisVal).trigger('change');
      });
    });
    $(document).on('woocommerce_update_variation_values', function() {
      $('.variation-radios input').each(function(index, element) {
        var $el = $(element);
        var thisName = $el.attr('name');
        var thisVal  = $el.attr('value');
        $el.removeAttr('disabled');
        if($('select[name="'+thisName+'"] option[value="'+thisVal+'"]').is(':disabled')) {
          $el.prop('disabled', true);
        }
      });
    });
    
  2. I extended further the JavaScript part of cfx’ answer to include cases of multiple variations. The idea is to hide and show available variations (radio buttons) based on the select inputs.

    <script>
        jQuery(document).on('change', '.variation-radios input', function() {
          jQuery('.variation-radios input:checked').each(function(index, element) {
            let el = jQuery(element);
            jQuery('select[name="' + el.attr('name') + '"]').val(el.attr('value')).trigger('change');
            recreateRadioInputs();
          });
        });
        
        jQuery(document).on('woocommerce_update_variation_values', function() {
          jQuery('.variation-radios input').each(function(index, element) {
            let el = jQuery(element);
            el.removeAttr('disabled');
            if(jQuery('select[name="' + el.attr('name') + '"] option[value="' + el.attr('value') + '"]').is(':disabled')) {
              $el.prop('disabled', true);
            }
          });
        });
        
        // recreate readio inputs based on the select inputs
        function recreateRadioInputs() {
            jQuery('.variation-radios input, .variation-radios label').hide();
            jQuery('.variations select').each(function() {
                let inputName = jQuery(this).attr('name');
                jQuery(this).find('option').each(function() {
                    let inputVal = jQuery(this).val();
                    let radioInput = jQuery('.variation-radios input[value="' + inputVal + '"]');
                    jQuery('.variation-radios label[for="' + radioInput.attr('id') + '"]').show();
                    radioInput.show();
                });
            });
        }
    </script>

Comments are closed.