• Resolved tamerkilinc

    (@tamerkilinc)


    Hi everyone!

    I have a problem for more than a week and I can’t find any solution.

    I am actually developing a Woocommerce theme. It is almost done, but two things (that are exactly the same things on different pages) are avoiding me from finishing the theme.

    The woocommerce_form_field() function adds <span class="woocommerce-input-wrapper"> before the input element and a </span> after it. The problem is that because of the span element the input box is not below but right next to the label which looks really bad. I want to have the label on top and the input field below (just how it is in Storefront theme). Unfortunately every solution I have tried did not work.

    Here is the function and the line which adds the span element: https://github.com/woocommerce/woocommerce/blob/7b13527ba5deb8d56ea901208f46ea5be068f401/includes/wc-template-functions.php#L2646

    I hope someone can help me. Thank you for your time!

    The page I need help with: [log in to see the link]

Viewing 6 replies - 1 through 6 (of 6 total)
  • Thread Starter tamerkilinc

    (@tamerkilinc)

    Additional information:

    The function is been called in the /myaccount/form-edit-address.php template.
    It is called with the following args: woocommerce_form_field( $key, $field, wc_get_post_data_by_key( $key, $field['value'] ) );

    Unfortunately, it looks like there is no option to add an argument to remove the span element. For me there are actually two options which do not sound good.

    Nr.1) Rewrite the function (remove the following line: $field_html .= '<span class="woocommerce-input-wrapper">' . $field; and call the new function in a custom form-edit-address – template.

    Nr.2) Copy the HTML output of the woocommerce_form_field() function, remove all span elements and insert the HTML directly into the form-edit-address – template.

    The reason why I have opened this topic is that I would like to see if there are better and smarter options to solve this problem.

    Best regards, Tamer Kilinc

    jessepearson

    (@jessepearson)

    Automattic Happiness Engineer

    @tamerkilinc I see your dilemma. One option, like you mentioned, is to copy the function and remove the lines that add the html you are referring to. The other is to use a regular expression to remove the content using the woocommerce_form_field filter.

    I would personally do the latter if you cannot get this resolved via CSS, it will last longer with future compatibility.

    Thread Starter tamerkilinc

    (@tamerkilinc)

    Hey @jessepearson,

    Thank you for your answer. Can you please explain the second option you are mentioning

    The other is to use a regular expression to remove the content using the woocommerce_form_field filter.

    Right now I do not quite understand what you exactly mean.. maybe an example could help.

    Thank you for your time and help!

    jessepearson

    (@jessepearson)

    Automattic Happiness Engineer

    @tamerkilinc A function can be created and called with the woocommerce_form_field filter. All form fields in the function you mentioned are passed through this filter, so the contents can be edited with it.

    Filters are described here:
    https://developer.www.ads-software.com/plugins/hooks/filters/

    Regular expressions in PHP allow you to search, find, replace, etc with certain parameters. This is too in depth for me to cover here, but you would be able to search for the first part of the span <span class="woocommerce-input-wrapper"> and replace it, then search for the last </span> and replace that, as well.

    Regular expressions are covered here:
    https://php.net/manual/en/reference.pcre.pattern.syntax.php

    You may be able to ask on Stack Overflow for assistance with developing a function to remove those two bits of html from output via regex, but that’s too advanced for us to assist with here.

    Thread Starter tamerkilinc

    (@tamerkilinc)

    Thank you for your help! As I do not have the time to learn regular expressions and replace the span element, I have copied the function and changed the lines that were responsible for the span element.

    I hope that I can go with it without updating the function a lot. It would be very helpful if the woocommerce_form_field function would get an argument for the future which can add or remove the span element.

    Topic can be closed.

    Here is the code for the custom_woocommerce_form_field function:

    function custom_woocommerce_form_field( $key, $args, $value = null ) {
    	$defaults = array(
    		'type'              => 'text',
    		'label'             => '',
    		'description'       => '',
    		'placeholder'       => '',
    		'maxlength'         => false,
    		'required'          => false,
    		'autocomplete'      => false,
    		'id'                => $key,
    		'class'             => array(),
    		'label_class'       => array(),
    		'input_class'       => array(),
    		'return'            => false,
    		'options'           => array(),
    		'custom_attributes' => array(),
    		'validate'          => array(),
    		'default'           => '',
    		'autofocus'         => '',
    		'priority'          => '',
    	);
    	$args = wp_parse_args( $args, $defaults );
    	$args = apply_filters( 'custom_woocommerce_form_field_args', $args, $key, $value );
    	if ( $args['required'] ) {
    		$args['class'][] = 'validate-required';
    		$required        = '&nbsp;<abbr class="required" title="' . esc_attr__( 'required', 'woocommerce' ) . '">*</abbr>';
    	} else {
    		$required = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
    	}
    	if ( is_string( $args['label_class'] ) ) {
    		$args['label_class'] = array( $args['label_class'] );
    	}
    	if ( is_null( $value ) ) {
    		$value = $args['default'];
    	}
    	// Custom attribute handling.
    	$custom_attributes         = array();
    	$args['custom_attributes'] = array_filter( (array) $args['custom_attributes'], 'strlen' );
    	if ( $args['maxlength'] ) {
    		$args['custom_attributes']['maxlength'] = absint( $args['maxlength'] );
    	}
    	if ( ! empty( $args['autocomplete'] ) ) {
    		$args['custom_attributes']['autocomplete'] = $args['autocomplete'];
    	}
    	if ( true === $args['autofocus'] ) {
    		$args['custom_attributes']['autofocus'] = 'autofocus';
    	}
    	if ( $args['description'] ) {
    		$args['custom_attributes']['aria-describedby'] = $args['id'] . '-description';
    	}
    	if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
    		foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
    			$custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
    		}
    	}
    	if ( ! empty( $args['validate'] ) ) {
    		foreach ( $args['validate'] as $validate ) {
    			$args['class'][] = 'validate-' . $validate;
    		}
    	}
    	$field           = '';
    	$label_id        = $args['id'];
    	$sort            = $args['priority'] ? $args['priority'] : '';
    	$field_container = '<p class="form-row %1$s" id="%2$s" data-priority="' . esc_attr( $sort ) . '">%3$s</p>';
    	switch ( $args['type'] ) {
    		case 'country':
    			$countries = 'shipping_country' === $key ? WC()->countries->get_shipping_countries() : WC()->countries->get_allowed_countries();
    			if ( 1 === count( $countries ) ) {
    				$field .= '<strong>' . current( array_values( $countries ) ) . '</strong>';
    				$field .= '<input type="hidden" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="' . current( array_keys( $countries ) ) . '" ' . implode( ' ', $custom_attributes ) . ' class="country_to_state" readonly="readonly" />';
    			} else {
    				$field = '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="country_to_state country_select form-control' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . '><option value="">' . esc_html__( 'Select a country&hellip;', 'woocommerce' ) . '</option>';
    				foreach ( $countries as $ckey => $cvalue ) {
    					$field .= '<option value="' . esc_attr( $ckey ) . '" ' . selected( $value, $ckey, false ) . '>' . $cvalue . '</option>';
    				}
    				$field .= '</select>';
    				$field .= '<noscript><button type="submit" name="woocommerce_checkout_update_totals" value="' . esc_attr__( 'Update country', 'woocommerce' ) . '">' . esc_html__( 'Update country', 'woocommerce' ) . '</button></noscript>';
    			}
    			break;
    		case 'state':
    			/* Get country this state field is representing */
    			$for_country = isset( $args['country'] ) ? $args['country'] : WC()->checkout->get_value( 'billing_state' === $key ? 'billing_country' : 'shipping_country' );
    			$states      = WC()->countries->get_states( $for_country );
    			if ( is_array( $states ) && empty( $states ) ) {
    				$field_container = '<p class="form-row %1$s" id="%2$s" style="display: none">%3$s</p>';
    				$field .= '<input type="hidden" class="hidden" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="" ' . implode( ' ', $custom_attributes ) . ' placeholder="' . esc_attr( $args['placeholder'] ) . '" readonly="readonly" />';
    			} elseif ( ! is_null( $for_country ) && is_array( $states ) ) {
    				$field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="state_select form-control' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '">
    						<option value="">' . esc_html__( 'Select a state&hellip;', 'woocommerce' ) . '</option>';
    				foreach ( $states as $ckey => $cvalue ) {
    					$field .= '<option value="' . esc_attr( $ckey ) . '" ' . selected( $value, $ckey, false ) . '>' . $cvalue . '</option>';
    				}
    				$field .= '</select>';
    			} else {
    				$field .= '<input type="text" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $value ) . '"  placeholder="' . esc_attr( $args['placeholder'] ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
    			}
    			break;
    		case 'textarea':
    			$field .= '<textarea name="' . esc_attr( $key ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . ( empty( $args['custom_attributes']['rows'] ) ? ' rows="2"' : '' ) . ( empty( $args['custom_attributes']['cols'] ) ? ' cols="5"' : '' ) . implode( ' ', $custom_attributes ) . '>' . esc_textarea( $value ) . '</textarea>';
    			break;
    		case 'checkbox':
    			$field = '<label class="checkbox ' . implode( ' ', $args['label_class'] ) . '" ' . implode( ' ', $custom_attributes ) . '>
    						<input type="' . esc_attr( $args['type'] ) . '" class="input-checkbox ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="1" ' . checked( $value, 1, false ) . ' /> ' . $args['label'] . $required . '</label>';
    			break;
    		case 'text':
    		case 'password':
    		case 'datetime':
    		case 'datetime-local':
    		case 'date':
    		case 'month':
    		case 'time':
    		case 'week':
    		case 'number':
    		case 'email':
    		case 'url':
    		case 'tel':
    			$field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-text form-control' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '"  value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
    			break;
    		case 'select':
    			$field   = '';
    			$options = '';
    			if ( ! empty( $args['options'] ) ) {
    				foreach ( $args['options'] as $option_key => $option_text ) {
    					if ( '' === $option_key ) {
    						// If we have a blank option, select2 needs a placeholder.
    						if ( empty( $args['placeholder'] ) ) {
    							$args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'woocommerce' );
    						}
    						$custom_attributes[] = 'data-allow_clear="true"';
    					}
    					$options .= '<option value="' . esc_attr( $option_key ) . '" ' . selected( $value, $option_key, false ) . '>' . esc_attr( $option_text ) . '</option>';
    				}
    				$field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="select form-control' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '">
    							' . $options . '
    						</select>';
    			}
    			break;
    		case 'radio':
    			$label_id = current( array_keys( $args['options'] ) );
    			if ( ! empty( $args['options'] ) ) {
    				foreach ( $args['options'] as $option_key => $option_text ) {
    					$field .= '<input type="radio" class="input-radio form-control' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $option_key ) . '" name="' . esc_attr( $key ) . '" ' . implode( ' ', $custom_attributes ) . ' id="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '"' . checked( $value, $option_key, false ) . ' />';
    					$field .= '<label for="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '" class="radio ' . implode( ' ', $args['label_class'] ) . '">' . $option_text . '</label>';
    				}
    			}
    			break;
    	}
    	if ( ! empty( $field ) ) {
    		$field_html = '';
    		if ( $args['label'] && 'checkbox' !== $args['type'] ) {
    			$field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . $args['label'] . $required . '</label>';
    		}
    		$field_html .= $field;
    		if ( $args['description'] ) {
    			$field_html .= '<span class="description" id="' . esc_attr( $args['id'] ) . '-description" aria-hidden="true">' . wp_kses_post( $args['description'] ) . '</span>';
    		}
    		//$field_html .= '</span>';
    		$container_class = esc_attr( implode( ' ', $args['class'] ) );
    		$container_id    = esc_attr( $args['id'] ) . '_field';
    		$field           = sprintf( $field_container, $container_class, $container_id, $field_html );
    	}
    	/**
    		 * Filter by type.
    		 */
    	$field = apply_filters( 'custom_woocommerce_form_field_' . $args['type'], $field, $key, $args, $value );
    	/**
    		 * General filter on form fields.
    		 *
    		 * @since 3.4.0
    		 */
    	$field = apply_filters( 'custom_woocommerce_form_field', $field, $key, $args, $value );
    	if ( $args['return'] ) {
    		return $field;
    	} else {
    		echo $field; // WPCS: XSS ok.
    	}
    }

    Best regards,
    Tamer Kilinc

    Howdy Tamer,

    I would just like to point out that the woocommerce_form_field is a pluggable function so you don’t need to rename it and you can use it in either your theme’s functions.php file or another plugin just so long as the file that contains the function loads before WooCommerce loads.

    So far as requesting that the default behavior be changed, there are a couple of places that you could open a feature request:

    1. The WooCommerce ideas board here: https://ideas.woocommerce.com/forums/133476-woocommerce
    2. The WooCommerce github issue tracker here: https://github.com/woocommerce/woocommerce/issues – if you were to open an issue there for consideration it would help your cause if you have data to back up why you are requesting this type of change. Given that the function in question is pluggable I do not believe it would even be considered since you can change the way the function works for your own WooCommerce instance but any changes would have an effect on all people using WooCommerce so it is very unlikely to be changed in WooCommerce without mountains of justification.

    Kind regards,

Viewing 6 replies - 1 through 6 (of 6 total)
  • The topic ‘Remove span element from woocommerce_form_field() function’ is closed to new replies.