From b1df7f0a8973ba3ba6ffbddc8708571feb293044 Mon Sep 17 00:00:00 2001 From: boo-code Date: Sat, 5 Sep 2026 23:24:48 +0200 Subject: [PATCH] Stop nesting form elements in the checkout address step The address step wrapped the whole block in a
and rendered full address elements inside it, and the address form's own buttons block opened yet another around the Continue button. The HTML parser resolves nesting by dropping the inner start tag and letting the inner
close the outer form, so every control after the address form loses its form owner. Measured on 9.2.0 with classic 3.1.2: with two saved addresses, a separate invoice address and the delivery address open for editing, the Continue button, both invoice radios and the not-valid-addresses input all report form === null. Attach those controls to the step form through the form attribute instead, leave the step form element empty so the address forms stay siblings, and replace the buttons-block form with a div, matching what hummingbird already does. --- templates/checkout/_partials/address-form.tpl | 6 +- .../_partials/address-selector-block.tpl | 3 +- .../checkout/_partials/steps/addresses.tpl | 158 +++++++++--------- 3 files changed, 89 insertions(+), 78 deletions(-) diff --git a/templates/checkout/_partials/address-form.tpl b/templates/checkout/_partials/address-form.tpl index e1286083..27a49a4b 100644 --- a/templates/checkout/_partials/address-form.tpl +++ b/templates/checkout/_partials/address-form.tpl @@ -34,13 +34,15 @@ {l s='Cancel' d='Shop.Theme.Actions'} {else} -
+ {* WHY: not a . This block is rendered inside the address form, so a nested form here is + dropped by the HTML parser while its
closes the address form early. *} +
{if $customer.addresses|count > 0} {l s='Cancel' d='Shop.Theme.Actions'} {/if} - +
{/if} {/block} diff --git a/templates/checkout/_partials/address-selector-block.tpl b/templates/checkout/_partials/address-selector-block.tpl index e895b6c1..c04f250f 100644 --- a/templates/checkout/_partials/address-selector-block.tpl +++ b/templates/checkout/_partials/address-selector-block.tpl @@ -35,6 +35,7 @@ type="radio" name="{$name}" value="{$address.id}" + {if isset($form_id)}form="{$form_id}"{/if} {if $address.id == $selected}checked{/if} > @@ -66,7 +67,7 @@ {/foreach} {if $interactive}

- +

{/if} {/block} diff --git a/templates/checkout/_partials/steps/addresses.tpl b/templates/checkout/_partials/steps/addresses.tpl index 7cf23950..9b90b0d9 100644 --- a/templates/checkout/_partials/steps/addresses.tpl +++ b/templates/checkout/_partials/steps/addresses.tpl @@ -25,113 +25,121 @@ {extends file='checkout/_partials/steps/checkout-step.tpl'} {block name='step_content'} + {$addresses_form_id = 'checkout-addresses-form'}
+ {* WHY: this step also renders full address
elements (checkout/_partials/address-form.tpl). + Wrapping those in this form produced nested tags: the HTML parser drops the inner start + tag and lets the inner
close THIS form instead, which orphaned the address selector and + the Continue button that follow it. This form therefore stays empty and its controls are + attached explicitly through the form attribute. *}
+ >
- {if $use_same_address} + {if $use_same_address} +

+ {if $cart.is_virtual} + {l s='The selected address will be used as your personal address (for invoice).' d='Shop.Theme.Checkout'} + {else} + {l s='The selected address will be used both as your personal address (for invoice) and as your delivery address.' d='Shop.Theme.Checkout'} + {/if} +

+ {else} +

{l s='Shipping Address' d='Shop.Theme.Checkout'}

+ {/if} + + {if $show_delivery_address_form} +
+ {render file = 'checkout/_partials/address-form.tpl' + ui = $address_form + use_same_address = $use_same_address + type = "delivery" + form_has_continue_button = $form_has_continue_button + } +
+ {elseif $customer.addresses|count > 0} +
+ {include file = 'checkout/_partials/address-selector-block.tpl' + addresses = $customer.addresses + name = "id_address_delivery" + selected = $id_address_delivery + type = "delivery" + form_id = $addresses_form_id + interactive = !$show_delivery_address_form and !$show_invoice_address_form + } +
+ + {if isset($delivery_address_error)} +

{$delivery_address_error.exception}

+ {else} + + {/if} + +

+ {l s='add new address' d='Shop.Theme.Actions'} +

+ + {if $use_same_address && !$cart.is_virtual}

- {if $cart.is_virtual} - {l s='The selected address will be used as your personal address (for invoice).' d='Shop.Theme.Checkout'} - {else} - {l s='The selected address will be used both as your personal address (for invoice) and as your delivery address.' d='Shop.Theme.Checkout'} - {/if} + + {l s='Billing address differs from shipping address' d='Shop.Theme.Checkout'} +

- {else} -

{l s='Shipping Address' d='Shop.Theme.Checkout'}

{/if} - {if $show_delivery_address_form} -
+ {/if} + + {if !$use_same_address} + +

{l s='Your Invoice Address' d='Shop.Theme.Checkout'}

+ + {if $show_invoice_address_form} +
{render file = 'checkout/_partials/address-form.tpl' ui = $address_form use_same_address = $use_same_address - type = "delivery" + type = "invoice" form_has_continue_button = $form_has_continue_button }
- {elseif $customer.addresses|count > 0} -
+ {else} +
{include file = 'checkout/_partials/address-selector-block.tpl' addresses = $customer.addresses - name = "id_address_delivery" - selected = $id_address_delivery - type = "delivery" + name = "id_address_invoice" + selected = $id_address_invoice + type = "invoice" + form_id = $addresses_form_id interactive = !$show_delivery_address_form and !$show_invoice_address_form }
- {if isset($delivery_address_error)} -

{$delivery_address_error.exception}

+ {if isset($invoice_address_error)} +

{$invoice_address_error.exception}

{else} - + {/if}

- {l s='add new address' d='Shop.Theme.Actions'} + {l s='add new address' d='Shop.Theme.Actions'}

- - {if $use_same_address && !$cart.is_virtual} -

- - {l s='Billing address differs from shipping address' d='Shop.Theme.Checkout'} - -

- {/if} - {/if} - {if !$use_same_address} - -

{l s='Your Invoice Address' d='Shop.Theme.Checkout'}

- - {if $show_invoice_address_form} -
- {render file = 'checkout/_partials/address-form.tpl' - ui = $address_form - use_same_address = $use_same_address - type = "invoice" - form_has_continue_button = $form_has_continue_button - } -
- {else} -
- {include file = 'checkout/_partials/address-selector-block.tpl' - addresses = $customer.addresses - name = "id_address_invoice" - selected = $id_address_invoice - type = "invoice" - interactive = !$show_delivery_address_form and !$show_invoice_address_form - } -
- - {if isset($invoice_address_error)} -

{$invoice_address_error.exception}

- {else} - - {/if} + {/if} -

- {l s='add new address' d='Shop.Theme.Actions'} -

- {/if} - - {/if} - - {if !$form_has_continue_button} -
- - -
- {/if} + {if !$form_has_continue_button} +
+ + +
+ {/if} - {hook h='displayAddressSelectorBottom'}
{/block}