woocommerce widget "filter products by attribute" displays unavailable products

I'll be direct : by default, you can't. It's not related to a certain theme or plugin, but to Woocommerce itself. This is a problem that exists in woocommerce for a very long time. It's not possible by default in woocommerce to handle variable products stock visibility (stock status) based on it's variations visibility as it's something necessary and needed by Woocommerce1.

A way of doing this is to add this action function :

add_action('woocommerce_before_shop_loop_item', 'out_of_stock_variations_loop');
function out_of_stock_variations_loop()
{
    global $product;
    $filter = 'size';
    if ($product->product_type === 'variable') {
        $available = $product->get_available_variations();
        if ($available) {
            foreach ($available as $instockvar) {
                if (isset($instockvar[ 'attributes' ][ 'attribute_pa_' . $filter ])) {
                    if ($_GET[ 'filter_' . $filter ]) {
                        if ( !in_array( $instockvar[ 'attributes' ][ 'attribute_pa_' . $filter ], explode(',', $_GET[ 'filter_' . $filter ]) , true ) || ($instockvar[ 'max_qty' ] <= 0) ) {
                            echo "<style>.post-" . $product->get_id() . " {display: none}</style>";
                        } else {
                            echo "<style>.post-" . $product->get_id() . " {display: list-item !important}</style>";
                        }
                    }
                }
            }
        }
    }
}
?>

Which will only show the list of products in stock. The problem with this is that it will leave blank spaces where the not-in-stock products are deleted by this query, and it can be difficult to circumvent this problem only with css because, on each row, first and last products have first and last classes that determines their layout in CSS. To overcome this, add this jQuery to your child-theme js script:

function openLayeredNavFilterIfSelected() {

  if (jQuery('.wc-layered-nav-term').hasClass('woocommerce-widget-layered-nav-list__item--chosen')) {

    /*keep layered nav filter open, if at least an attribute is selected*/
    jQuery('.woocommerce-widget-layered-nav-list__item--chosen').parent().parent().show();
    if (!jQuery('.filter-icon').hasClass('arrow_up')) {
      jQuery('.filter-icon').addClass('arrow_up');
    }

    /*redistribute products rows to avoid missing spaces*/
    jQuery('.site-main ul.products.columns-3 li.product').removeClass('first').removeClass('last');
    jQuery('.site-main ul.products.columns-3 li.product:visible').each(function(i) {
      if (i % 3 == 0) jQuery(this).addClass('first'); //add class first to firsts products of rows
      if (i % 3 == 2) jQuery(this).addClass('last'); //add class last to lasts products of rows
    });
  }
}

openLayeredNavFilterIfSelected();
jQuery(window).resize(openLayeredNavFilterIfSelected);

And you should be good to go.

1 WOOF Products Filter plugin and out of stock variations issue in Woocommerce

Related :

- filter products by attribute and hide out of stock items of variable products

- How to prevent `out of stock` items to show as a filter option in the sidebar (layered nav widget ) in WooCommerce?

- https://wordpress.org/support/topic/hide-out-of-stock-variations-when-filtering/#post-7718128

- https://xtemos.com/forums/topic/filter-attributes-dont-show-out-of-stock-variations/

- https://wordpress.org/support/topic/exclude-out-of-stock-products-from-filter/