php - How to convert select options (product attributes) to clickable divs in variable product page in WooCommerce?
问题描述
I am trying to redesign the woocommerce single product page where instead of showign a dropdown for variation options, I would like them to be placed in separate divs and be displayed in flex boxes side by side. I dont understand what part is making the data take the 'dropdown list' form?
An illustration of what I am trying to achieve:
This is the html part (taken from wc-template-functions.php inside the includes folder) where instead of the select and option tags i need to insert each variation inside a separate div. I really need this. Any help?
This is my attempt inside woocommerce -> includes -> wc-template-functions.php, inside the function wc_dropdown_variation_attribute_options. It shows the variations but they arent clickable and cant pull the data to select and forward on add to cart / buy now event:
function wc_dropdown_variation_attribute_options( $args = array() ) {
$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' => '',
)
);
// Get selected value.
if ( false === $args['selected'] && $args['attribute'] && $args['product'] instanceof WC_Product ) {
$selected_key = 'attribute_' . sanitize_title( $args['attribute'] );
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$args['selected'] = isset( $_REQUEST[ $selected_key ] ) ? wc_clean( wp_unslash( $_REQUEST[ $selected_key ] ) ) : $args['product']->get_variation_default_attribute( $args['attribute'] );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
$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'];
if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) {
$attributes = $product->get_variation_attributes();
$options = $attributes[ $attribute ];
}
if ( ! empty( $options ) ) {
if ( $product && taxonomy_exists( $attribute ) ) {
// Get terms if this is a taxonomy - ordered. We need the names too.
$terms = wc_get_product_terms(
$product->get_id(),
$attribute,
array(
'fields' => 'all',
)
);
foreach ( $terms as $term ) {
if ( in_array( $term->slug, $options, true ) ) {
$html .= '<div value="' . esc_attr( $term->slug ) . '" ' . selected( sanitize_title( $args['selected'] ), $term->slug, false ) . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name, $term, $attribute, $product ) ) . '</div>';
}
}
} else {
foreach ( $options as $option ) {
// This handles < 2.4.0 bw compatibility where text attributes were not sanitized.
$selected = sanitize_title( $args['selected'] ) === $args['selected'] ? selected( $args['selected'], sanitize_title( $option ), false ) : selected( $args['selected'], $option, false );
$html .= '<div value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option, null, $attribute, $product ) ) . '</div>';
}
}
}
$html .= '</div>';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo apply_filters( 'woocommerce_dropdown_variation_attribute_options_html', $html, $args );
}
解决方案
我考虑过不使用任何挂钩将属性选择选项转换为单独的 div。我宁愿使用 jQuery。
原因很简单。WooCommerce 使用基于选择选项元素的多个脚本,用 div 替换选项可能会产生意外冲突。
想法是这样的:创建选择选项产品属性的副本(对 用户隐藏) 并通过 jQuery函数替换 div 中的所有选择选项。unwrap()
我受到这个答案的启发:是否可以将选择菜单转换为按钮?
每个产品属性的所有选择选项都将转换为 div(甚至多个)。
更新
之前的代码不允许自动隐藏与任何产品变体不对应的属性选项 (div)。代码现已更新,允许用户:
- 通过单击同一元素来选择和取消选择div
- 自动隐藏与任何产品变体不对应的属性(基于一个或多个选定的 div)
该代码适用于产品页面上的任意数量的属性。
查询
add_action( 'wp_footer', 'converts_product_attributes_from_select_options_to_div' );
function converts_product_attributes_from_select_options_to_div() {
?>
<script type="text/javascript">
jQuery(function($){
// clones select options for each product attribute
var clone = $(".single-product div.product table.variations select").clone(true,true);
// adds a "data-parent-id" attribute to each select option
$(".single-product div.product table.variations select option").each(function(){
$(this).attr('data-parent-id',$(this).parent().attr('id'));
});
// converts select options to div
$(".single-product div.product table.variations select option").unwrap().each(function(){
if ( $(this).val() == '' ) {
$(this).remove();
return true;
}
var option = $('<div class="custom_option is-visible" data-parent-id="'+$(this).data('parent-id')+'" data-value="'+$(this).val()+'">'+$(this).text()+'</div>');
$(this).replaceWith(option);
});
// reinsert the clone of the select options of the attributes in the page that were removed by "unwrap()"
$(clone).insertBefore('.single-product div.product table.variations .reset_variations').hide();
// when a user clicks on a div it adds the "selected" attribute to the respective select option
$(document).on('click', '.custom_option', function(){
var parentID = $(this).data('parent-id');
if ( $(this).hasClass('on') ) {
$(this).removeClass('on');
$(".single-product div.product table.variations select#"+parentID).val('').trigger("change");
} else {
$('.custom_option[data-parent-id='+parentID+']').removeClass('on');
$(this).addClass('on');
$(".single-product div.product table.variations select#"+parentID).val($(this).data("value")).trigger("change");
}
});
// if a select option is already selected, it adds the "on" attribute to the respective div
$(".single-product div.product table.variations select").each(function(){
if ( $(this).find("option:selected").val() ) {
var id = $(this).attr('id');
$('.custom_option[data-parent-id='+id+']').removeClass('on');
var value = $(this).find("option:selected").val();
$('.custom_option[data-parent-id='+id+'][data-value='+value+']').addClass('on');
}
});
// when the select options change based on the ones selected, it shows or hides the respective divs
$('body').on('check_variations', function(){
$('div.custom_option').removeClass('is-visible');
$('.single-product div.product table.variations select').each(function(){
var attrID = $(this).attr("id");
$(this).find('option').each(function(){
if ( $(this).val() == '' ) {
return;
}
$('div[data-parent-id="'+attrID+'"][data-value="'+$(this).val()+'"]').addClass('is-visible');
});
});
});
});
</script>
<?php
}
该代码已经过测试并且可以工作。将它添加到您的活动主题的functions.php。
CSS
/* adds style to divs */
/* by default all divs are hidden */
div.custom_option {
display: none;
border: 2px solid #ccc;
margin-right: 5px;
padding: 2px 5px;
cursor: pointer;
}
/* show only divs with class "is-visible" */
div.custom_option.is-visible {
display:inline-block;
}
/* adds the style to the selected div */
div.custom_option.on {
background-color: #777;
color: white;
}
在活动主题的样式表中添加 CSS。
前
后
用法
推荐阅读
- twilio - Twilio 使用自定义参数将呼叫转接到另一个 Twilio 号码
- python - 是否可以模拟 os.scandir 及其属性?
- c++ - 在c++中运行程序时没有输出
- r - 如何使用 ggplot 为每个带有“+”或“NA”值的标题着色?
- lua - Garry'sMod 检测两个玩家之间是否有墙
- linux - 使用 Windows ACL (Samba) 设置共享
- c# - 如何将属性添加到让用户从列表中选择的自定义组件
- python-3.x - 安装模块时 DLL 加载失败
- sql - Microsoft SQL Server - 限制用户访问单个视图
- java - 使用正则表达式检测字符串中的脚注标记