Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Mark Drew and Michael Hnat
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Mark Drew Michael Hnat

Overriding Form Submission Properties Using Button Attributes In Native HTML

By
Published in Comments (5)

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.

The Order of Submit Buttons Matters

When I first implemented this form, I put the Preview button right next to the Name input. This worked fine if you clicked on the various submission buttons with your mouse. But, the problem is that if you focused the Name input and hit "Enter" on the keyboard, the browser used the Preview submission button, not the original submission button.

When you submit a form via the "Enter" key, the browser uses the first submission button in the DOM (Document Object Model) branch contained within the Form. As such, it would use the Preview button as the submission button. This is why in a previous article, on 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.

Reader Comments

247 Comments

Mind Blown! I've been writing HTML for 30+ years and (thought) I understood the submit button. Why would I ever have to look up those specs?! Haha...makes me wonder how much I'm missing because I never question my assumptions. I assumed I understood the submit button...nope! Thanks Ben! Feeling humble.

15,902 Comments

@Chris,

Dude, I often feel the same way! This is especially true for methods on the DOM nodes - I feel like there's so many I never heard about. Every now and then, I'll scroll through You Might Not Need jQuery, which is a sobering reminder that there are methods I never user and never remember exist.

25 Comments

Add me to the list of developers who had no idea this level of flexibility existed in vanilla HTML. My first thought was that I might be able to use formmethod to tell a form to use fancier HTTP methods like PUT, PATCH, DELETE, etc... but unfortunately, the MSDN specs say we are still limited to POST and GET. That was wishful thinking on my part. :)

15,902 Comments

@David,

It's funny you mention that. I came across this stuff because I'm looking into Hotwire, and in Hotwire you can change the methods on links to be DELETE ... but even the documentation is like, You probably shouldn't be doing this!!!! 🤣

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel