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
<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:
- Wrap each field in a wrapper like
<div class="single-field-wrapper"><p [...] </div>
- 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
<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:
- Wrap each field in a wrapper like
<div class="single-field-wrapper"><p [...] </div>
- 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 |2 Answers
Reset to default 5There 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 );
change_woocommerce_field_markup
function.. – AmintaCode Commented Jul 28, 2018 at 8:49