Multiple HTML submit buttons – a shortcoming of Microsoft Internet Explorer and what you can do about it

David Dorward

Introduction

It is often useful to have multiple submit buttons in a single form and take a different action depending upon which was used to send the form data to the server. For example, a web based front end to a database of users subscribed to a mailing list might have a “Delete this user” button for each subscriber, or a login screen might ask for a username and password then offer the option to “Log in” or “Create an account”.

HTML provides three tools to submit forms:

Of these, it is only the regular submit button that Microsoft’s browser manages to deal with properly, the others are more troublesome as the standard methods to distinguish which element triggered the submission of the form data do not work.

Server side image maps

When a server side image map is clicked by the user, three pieces of data should be submitted to the server:

For example, given <input type="submit" name="location" value="europe" src="europe.png" alt="Europe">, a click should produce something along the lines of location=europe&location.x=27&location.y=123. Unfortunately Internet Explorer will only send the X and Y co-ordinates.

Button elements

Meanwhile, the button element allows authors to produce buttons that submit data to the server that does not match the text displayed on the button. <button type="submit" name="row_to_delete" value="1">Delete</button> should submit to the server row_to_delete=1 but Internet Explorer will send row_to_delete=Delete. If IE could be persuaded to behave, then this would also be useful for multilingual sites – the text displayed could be translated for each language, but the value could remain the same so the server side logic wouldn’t need to check the translation table.

It gets worse though, Internet Explorer 6 has a further bug where it will treat all <button> elements as successful controls, and submit their data to the server, even if they were not clicked. This effectively makes using multiple button elements impossible.

Solutions

There are several solutions to the problem.

Interfaces could be redesigned so multiple submit buttons are not required. For example, by replacing them with radio buttons and a single submit button, but this requires an extra click from the user.

A separate form could be used for each instance, with a hidden input providing the data the the submit button would normally carry. This can be a good solution when you have a simple “Delete this row” problem, but with more complicated forms (e.g. a shopping cart where the user can change the quantities or delete a row entirely) it won’t fit.

JavaScript could generate a hidden input based on which submit button was clicked, but this adds a dependancy on JavaScript.

The most reliable and unobtrusive solution is to hide the value inside the name of the control, and this is the approach that this document will discuss in detail.

Using a different name for each input

The HTML

Since the value attribute is useless to us, at least as far as passing the data we want in concerned, we have to store that data somewhere else – as part of the name.

Rather then saying:

<input type="image" name="action" value="someAction" alt="someAction">

or

<button type="submit" name="row_to_delete" value="1">Delete</button>

We say:

<input type="image" name="action_someAction" alt="someAction">

or

<input type="submit" name="row_to_delete_1" value="Delete">

The server side code

Once the data has arrived at the server we have to look to see which submit button was activated. Normally we could just check its name and look at its value, but here we don’t know what the name is – so we have to loop over all the names until we find one that matches the pattern we used to store the data.

For the image input, we can’t trust the that key/value will be submitted, so we have to look for one of the co-ordinate values instead.

The button input is easier, in the above case row_to_delete_NUMBER. Then we just need to extract that number.

I’m going to give examples in a couple of different languages, but the principles will be the same no matter what you are using to writing your form handler. This means that if you aren’t using Perl or PHP then you can look at the logic and write your own port.

Dealing with image maps

Perl (using CGI.pm)
my %params = $q->Vars;
my $action;
foreach my $key (keys %params) {
    if ($key =~ /^action_(.+)\.y$/) {
        $action = $1; 
    }
}
Alternative Perl (using CGI.pm)
my %params = $q->Vars;
my ($action) = map { 
      /^action_(.+)\.y$/ 
   } keys %params;
PHP

PHP changes the full stop character in the input name into an underscore, so the regular expression must account for that.

<?PHP
    $expression = '/^action_(.+)_y$/';
    $unextracted_actions = preg_grep($expression, array_keys($_REQUEST));
    preg_match($expression, $unextracted_actions[0], $action_matches);
    $action = $action_matches[1];
?>

Dealing with buttons

Perl (using CGI.pm)
my %params = $q->Vars;
my $row_to_delete;
foreach my $key (keys %params) {
    if ($key =~ /^row_to_delete_(\d+)$/) {
        $action = $1; 
    }
}
Alternative Perl (using CGI.pm)
my %params = $q->Vars;
my ($row_to_delete) = map { 
      /^row_to_delete_(\d+)$/ 
   } keys %params;
PHP
<?PHP
    $expression = '/^row_to_delete_(.+)$/';
    $unextracted_rows = preg_grep($expression, array_keys($_REQUEST));
    preg_match($expression, $unextracted_rows[0], $row_matches);
    $row = $row_matches[1];
?>