最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to hook on a WooCommerce checkout field?

programmeradmin0浏览0评论

Each field of the default Woocommerce form checkout has this markup:

<p class="form-row form-row-first validate-required" id="billing_first_name_field" data-priority="10">
     <label for="billing_first_name" class="">Name&nbsp;
           <abbr class="required" title="required">*</abbr>
     </label>
     <span class="woocommerce-input-wrapper">
          <input type="text" class="input-text " name="billing_first_name" id="billing_first_name" placeholder="" value="" autocomplete="given-name">
     </span>
</p>

Now, I want:

  1. Wrap each field in a wrapper like <div class="single-field-wrapper"><p [...] </div>
  2. Wrap the first two fields, 'billing_first_name' and 'billing_last_name' in a wrapper like <div class="first-and-second-field-wrapper"> [...] </div>

To achieve this, I've tried to use the filter hooks made available by function woocommerce_form_field( $key, $args, $value = null ) called in form-billing.php template in this way:

<?php
        $fields = $checkout->get_checkout_fields( 'billing' );

        foreach ( $fields as $key => $field ) {
            if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) {
                $field['country'] = $checkout->get_value( $field['country_field'] );
            }
            woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
        }
    ?>

So in functions.php I wrote:

function change_woocommerce_field_markup($field, $key, $args, $value){

  $field = '<div class="single-field-wrapper">'.$field.'</div>';

  if($key === 'billing_first_name') {

      $field = '<div class="first-and-second-field-wrapper">'.$field;
   }
  else if ($key === 'billing_last_name') {

      $field = $field.'</div>';
   }

   else {
      $field = $field;
   }

    return $field;
   } 

  add_filter("woocommerce_form_field","change_woocommerce_field_markup", 10, 4);

Unexpectedly, I get this markup:

<div class="first-and-second-field-wrapper">
       <div class="single-field-wrapper">
            <div class="single-field-wrapper">
            [here there are all the fields, one under the other ]
            </div>
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div>   
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
 </div>

Anyone can explain why this unexpected behavior?

Each field of the default Woocommerce form checkout has this markup:

<p class="form-row form-row-first validate-required" id="billing_first_name_field" data-priority="10">
     <label for="billing_first_name" class="">Name&nbsp;
           <abbr class="required" title="required">*</abbr>
     </label>
     <span class="woocommerce-input-wrapper">
          <input type="text" class="input-text " name="billing_first_name" id="billing_first_name" placeholder="" value="" autocomplete="given-name">
     </span>
</p>

Now, I want:

  1. Wrap each field in a wrapper like <div class="single-field-wrapper"><p [...] </div>
  2. Wrap the first two fields, 'billing_first_name' and 'billing_last_name' in a wrapper like <div class="first-and-second-field-wrapper"> [...] </div>

To achieve this, I've tried to use the filter hooks made available by function woocommerce_form_field( $key, $args, $value = null ) called in form-billing.php template in this way:

<?php
        $fields = $checkout->get_checkout_fields( 'billing' );

        foreach ( $fields as $key => $field ) {
            if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) {
                $field['country'] = $checkout->get_value( $field['country_field'] );
            }
            woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
        }
    ?>

So in functions.php I wrote:

function change_woocommerce_field_markup($field, $key, $args, $value){

  $field = '<div class="single-field-wrapper">'.$field.'</div>';

  if($key === 'billing_first_name') {

      $field = '<div class="first-and-second-field-wrapper">'.$field;
   }
  else if ($key === 'billing_last_name') {

      $field = $field.'</div>';
   }

   else {
      $field = $field;
   }

    return $field;
   } 

  add_filter("woocommerce_form_field","change_woocommerce_field_markup", 10, 4);

Unexpectedly, I get this markup:

<div class="first-and-second-field-wrapper">
       <div class="single-field-wrapper">
            <div class="single-field-wrapper">
            [here there are all the fields, one under the other ]
            </div>
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div>   
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
       <div class="single-field-wrapper"></div> 
 </div>

Anyone can explain why this unexpected behavior?

Share Improve this question edited Jul 28, 2018 at 8:48 AmintaCode asked Jul 27, 2018 at 10:19 AmintaCodeAmintaCode 1771 gold badge4 silver badges16 bronze badges 2
  • Can you show us the code that adds the div tag with the "single-field-wrapper" class? – user141080 Commented Jul 28, 2018 at 6:18
  • Sure, I missed it, now I've edited the post. It's in change_woocommerce_field_markup function.. – AmintaCode Commented Jul 28, 2018 at 8:49
Add a comment  | 

2 Answers 2

Reset to default 5

There is no hook woocommerce_form_field, there is hook woocommerce_form_field_{$args[type]} (doc).

$args[type] can be (look here for available options):

  • text,
  • checkbox,
  • country,
  • ...

Code below will wrap 'billing_first_name' and 'billing_last_name' fields in a wrapper like <div class="first-and-second-field-wrapper"> [...] </div>.

function change_woocommerce_field_markup($field, $key, $args, $value) {

   $field = '<div class="single-field-wrapper">'.$field.'</div>';

   if($key === 'billing_first_name')
      $field = '<div class="first-and-second-field-wrapper">'.$field;
   else if ($key === 'billing_last_name')
      $field = $field.'</div>';

    return $field;
} 

add_filter("woocommerce_form_field_text","change_woocommerce_field_markup", 10, 4);

It will also wrap text type fields with <div class="single-field-wrapper">...</div>.
BUT
some text fields that have own type (like state or email) require additional hooks, for example:

add_filter("woocommerce_form_field_country","change_woocommerce_field_markup", 10, 4);
add_filter("woocommerce_form_field_email","change_woocommerce_field_markup", 10, 4);

UPDATE #1

Above code works in WC-v3.3.7.

In WC-v3.4.xx you get this:

<div class="first-and-second-field-wrapper">
    <div class="single-field-wrapper">
        <div class="single-field-wrapper">
            [here there are all the fields, one under the other ]
        </div>
    <div class="single-field-wrapper"></div> 
    <div class="single-field-wrapper"></div> 
    <div class="single-field-wrapper"></div> 
    ....
    <div class="single-field-wrapper"></div>
</div>

because javascript sorts form rows inside .woocommerce-billing-fields__field-wrapper. Look at the file woocommerce/assets/js/frontend/address-i18n.js, from line 99.
JS finds all HTML tags with ".form-row" class inside wrapper, takes parent of first item (tag with class .form-row), sort items by priority and insert them into previously selected parent element.

For the test, change in file address-i18.js, in line 99
var fieldsets = $('.woocommerce-billing-fields__field-wrapper, ...
to
var fieldsets = $('.woocommerce-billing-fields__field-wrapper2, ...
and upload as address-i18.min.js.

Removing JS sorting is not a solution, it is just a test. Whithout sorting by JS you will get this:

<div class="first-and-second-field-wrapper">
    <div class="single-field-wrapper">
        <p class="form-row ..." id="billing_first_name_field"> <label for="billing_first_name"> ... </p>
    </div>
    <div class="single-field-wrapper">
        <p class="form-row ..." id="billing_last_name_field"> <label for="billing_last_name">... </p>
    </div>
</div>
<div class="single-field-wrapper">
   <p class="form-row ..." id="billing_company_field"> <label for="billing_company">...  </p>
</div>

I just ran into the exact same issue that I hadn't spotted before. I originally opened this ticket on the WC GitHub back in 2015, so it's great that they added the filter... but the JS-based reshuffling is a pain. However, nmr's answer pointed me in the right direction:

As the JS reshuffle works on the .form-row element, you just need to a) remove it from the field you want to wrap, and b) add it to your own new field wrapper:

function change_woocommerce_field_markup( $field, $key, $args, $value ) {

    //  Remove the .form-row class from the current field wrapper

    $field = str_replace('form-row', '', $field);

    //  Wrap the field (and its wrapper) in a new custom div, adding .form-row so the reshuffling works as expected, and adding the field priority

    $field = '<div class="form-row single-field-wrapper" data-priority="' . $args['priority'] . '">' . $field . '</div>';

    return $field;
}

add_filter( 'woocommerce_form_field', 'change_woocommerce_field_markup', 10, 4 );
发布评论

评论列表(0)

  1. 暂无评论