Select Page

Overriding Form Submission Properties Using Button Attributes In Native HTML

Cyberdime
Published: January 15, 2023

For the last few weeks, I’ve been [very slowly] looking into the Hotwire framework from Basecamp. One of the guiding principles of Hotwire seems to be, “HTML has a bunch of great stuff, let’s use it!” Case in point, I was reading through a Thoughtbot article on rendering live previews by Sean Doyle when I saw something that I had never seen before: submit buttons with form “action” and “method” attributes. Apparently, this has been supported by browsers going back to IE 10; but, since I’ve never seen it before, I wanted to try it out for myself in ColdFusion.

Every <form> you ever create has a method, an action, and a target attribute (among many others). Even if you don’t define these attributes explicitly, they are there implicitly with default values. These attributes determine how and where a form submission is sent to the server.

Typically, when you embed a button of type="submit" within a form, clicking on said button will simply trigger the form submission using the method, action, and target attributes on the <form> tag. However – and this is the thing I just learned – you can have your submit buttons override the form attributes.

The submit button element can include the following override attributes (not an exhaustive list):

  • formMethod – Overrides the method attribute on the parent form.

  • formAction – Overrides the action attribute on the parent form.

  • formTarget – Overrides the target attribute on the parent form.

To see this in action, let’s create a super silly ColdFusion form page that submits back to itself. The form has nothing but a name field; and, upon submission, all we’re going to do is display a message tailored for the given name value:

<cfscript>
	param name="form.name" type="string" default="";
</cfscript>
<cfoutput>
	<!--- Render the message! This is a silly example, sorry. --->
	<cfif form.name.len()>
		<p>
			Hello #encodeForHtml( form.name )#, I hope you are well.
		</p>
	</cfif>
	<!--- NOTE: This form just posts BACK TO ITSELF. --->
	<form method="post">
		<p>
			Name:<br />
			<input type="text" name="name" size="30" />
			<button type="submit">
				Submit Form
			</button>
		</p>
	</form>
</cfoutput>

Like I said, this is a super silly example; but, I’m trying to keep this super simple. This <form> has no action attribute. As such, it will, by default, post back to itself (think cgi.script_name). And, when the form.name value is populated, we just output a “Hello” message.

If we render this form and submit “Sarah”, we get the following output:

A form is submitted with the name, Sarah. The page refreshes and the 'Hello' message is displayed for Sarah.

As you can see, the page is refreshed with the form submission and the appropriate “Hello” message is displayed.

Now, let’s add another submit button. But, this time, we’re going to have it override the submission behavior, sending the user to a preview page within a different browser tab:

<cfscript>
	param name="form.name" type="string" default="";
</cfscript>
<cfoutput>
	<!--- Render the message! This is a silly example, sorry. --->
	<cfif form.name.len()>
		<p>
			Hello #encodeForHtml( form.name )#, I hope you are well.
		</p>
	</cfif>
	<!--- NOTE: This form just posts BACK TO ITSELF. --->
	<form method="post">
		<p>
			Name:<br />
			<input type="text" name="name" size="30" />
			<button type="submit">
				Submit Form
			</button>
		</p>
		<p>
			<!---
				CAUTION: I'm putting this submit button AFTER the primary submit button so
				that hitting "Enter" while focused on the Input will trigger the primary
				submit button. The browser will take the first Submit found in the form
				when submitting via "Enter"; and we don't want to accidentally make this
				"Preview" button the primary button.
			--->
			<button type="submit" formaction="./preview.cfm" formtarget="_blank">
				Preview
			</button>
		</p>
	</form>
</cfoutput>

As you can see, our new submit includes two additional attributes:

  • formAction="./preview.cfm" – We’re going to override the destination of the form submission, sending the data to the Preview page, not the form processing page.

  • formTarget="blank" – We’re going to post this data to a new browser tab so that we leave the current form in place.

This new preview.cfm page does nothing but render the message:

<cfscript>
	param name="form.name" type="string" default="";
</cfscript>
<cfoutput>
	<p>
		Hello #encodeForHtml( form.name )#, I hope you are well.
	</p>
</cfoutput>

And now, let’s try using the form with both submit buttons:

A form is submitted using the Preview button with the name, Kimmi. A new browser tab opens, showing the message addressed to Kimmie. Thew new browser tab is closed, revealing the original tab with the unsubmitted form. The unsubmitted form is submitted using the original submit button. The page refreshes and the 'Hello' message is displayed for Kimmie.

As you can see, when I click on the new Preview submit button, the form is submitted to the preview.cfm template using a new browser tab. This leaves the original form unsubmitted and in place. We can close the new browser tab and the submit the original form using our original submit button and the “Hello” message is displayed as normal.

This is pretty cool! I can’t believe I didn’t know that this was a feature of native HTML. I can definitely see this being a useful feature, especially when progressively enhancing a user interface (UI) with Hotwire – but, I’m not there yet.

using multiple submission buttons to build up complex form data, I included an “off screen” (ie, visually hidden) submission button as the very first rendered element in my form.

To keep things simple, I just moved my Preview submission button to the bottom so that I didn’t have to worry about order.

Want to use code from this post?
Check out the license.

Source: www.bennadel.com