Home > Internet > Nesting FORM tags in (X)HTML

Nesting FORM tags in (X)HTML

Due to ASP.NET’s requirements with regards to server side controls being in one big FORM tag, I started looking at nesting FORM tags in one (X)HTML document.

Formalities

First, let’s have a look at the official documents.

HTML 4 specification says
A form can contain text and markup (paragraphs, lists, etc.) in addition to form controls.

DTD says that FORM element can contain aby block element apart from another FORM
<!ENTITY % block
"P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT | BLOCKQUOTE | FORM | HR | TABLE | FIELDSET | ADDRESS">
<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->

However, following the nesting path, there should be no problem in using some block element and nesting another FORM within that. Let’s take DIV for example.
<!ELEMENT DIV - - (%flow;)* -- generic language/style container -->
<!ENTITY % flow "%block; | %inline;">

We can see that DTD officially forbids only nesting FORM directly within another FORM. It doesn’t say anything about nesting FORMs within block elements, within other FORMs, or even deeper within the DOM tree.

But let’s see how does it work in the real world.

Experiments

Let’s try this practically. We’ll use the standard W3 validator and Firefox DOM inspector to examine site content at each step.

Attempt 1 – the obvious

First, we’ll use a simple XHTML document with one FORM tag.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>Test doc</title>
  </head>
  <body>
    <form action="#" method="post" id="container-form">
      <fieldset>
        <input type="text" id="container-input" name="container-input"/>
        <input type="submit" id="container-submit" value="Submit container form"/>
      </fieldset>
    </form>
  </body>
</html>

Of course, validator says it’s a correct document.

Within the DOM tree, we can see our FORM successfully.

Attempt 2 – wrong

Now, let’s add another FORM within the first one

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>Test doc</title>
  </head>
  <body>
    <form action="#" method="post" id="container-form">
      <fieldset>
        <input type="text" id="container-input" name="container-input"/>
        <input type="submit" id="container-submit" value="Submit container form"/>
      </fieldset>
      <form action="#" method="post" id="inner-form-1">
        <fieldset>
          <input type="text" id="inner1-input" name="inner1-input"/>
          <input type="submit" id="inner1-submit" value="Submit inner form 1"/>
        </fieldset>
      </form>
    </form>
  </body>
</html>

As expected, validator says it’s incorrect – true, we cannot nest FORM directly within a FORM.

If we look at the DOM tree in a browser, we see how the browser is trying to glue both FORMs together

Attempt 3 – theoretically correct

Now, let’s put the inner FORM within a block level container. This markup is correct according to specification and DTD.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>Test doc</title>
  </head>
  <body>
    <form action="#" method="post" id="container-form">
      <fieldset>
        <input type="text" id="container-input" name="container-input"/>
        <input type="submit" id="container-submit" value="Submit container form"/>
        <form action="#" method="post" id="inner-form-1">
          <fieldset>
            <input type="text" id="inner1-input" name="inner1-input"/>
            <input type="submit" id="inner1-submit" value="Submit inner form 1"/>
          </fieldset>
        </form>
      </fieldset>
    </form>
  </body>
</html>

Validator indeed says the document is OK.

Let’s try the DOM inspector.

What happens? The browser is still gluing the two forms together, although the markup is correct. This is something, that should not happen. Let’s experiment a bit more

Attempt 4 – the quirks begin

Now, let’s add some more properly marked up nested forms and see what happens.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>Test doc</title>
  </head>
  <body>
    <form action="#" method="post" id="container-form">
      <fieldset>
        <input type="text" id="container-input" name="container-input"/>
        <input type="submit" id="container-submit" value="Submit container form"/>
        <form action="#" method="post" id="inner-form-1">
          <fieldset>
            <input type="text" id="inner1-input" name="inner1-input"/>
            <input type="submit" id="inner1-submit" value="Submit inner form 1"/>
          </fieldset>
        </form>
        <form action="#" method="post" id="inner-form-2">
          <fieldset>
            <input type="text" id="inner2-input" name="inner2-input"/>
            <input type="submit" id="inner2-submit" value="Submit inner form 2"/>
          </fieldset>
        </form>
        <form action="#" method="post" id="inner-form-3">
          <fieldset>
            <input type="text" id="inner3-input" name="inner3-input"/>
            <input type="submit" id="inner3-submit" value="Submit inner form 3"/>
          </fieldset>
        </form>
        <form action="#" method="post" id="inner-form-4">
          <fieldset>
            <input type="text" id="inner4-input" name="inner4-input"/>
            <input type="submit" id="inner4-submit" value="Submit inner form 4"/>
          </fieldset>
        </form>
      </fieldset>
    </form>
  </body>
</html>

Again – validator says it’s fine.

Let’s see what DOM insepctor tells us this time.
.
Now we can see some weird behaviour here. The browser is gluing the first inner form, but processes the other forms properly

Conclusions

Despite the fact that nesting forms is theoretically possible, browsers do not render it properly. A workaround needs to be used. On the other hand, specifications can be updated to clearly prohibit nesting FORM tags.

Workaround

The basic workaround is to nest additional, unnecessary form, just for this bug’s sake. Say, for 1 main and 2 nested forms, you would create such document then

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>Test doc</title>
  </head>
  <body>
    <form action="#" method="post" id="container-form">
      <fieldset>
        <input type="text" id="container-input" name="container-input"/>
        <input type="submit" id="container-submit" value="Submit container form"/>
        <form action="#" method="post" id="nested-form-bug"></form>
        <form action="#" method="post" id="inner-form-1">
          <fieldset>
            <input type="text" id="inner1-input" name="inner1-input"/>
            <input type="submit" id="inner1-submit" value="Submit inner form 1"/>
          </fieldset>
        </form>
        <form action="#" method="post" id="inner-form-2">
          <fieldset>
            <input type="text" id="inner2-input" name="inner2-input"/>
            <input type="submit" id="inner2-submit" value="Submit inner form 2"/>
          </fieldset>
        </form>
      </fieldset>
    </form>
  </body>
</html>

Then you just have to hide the dummy form

#nested-form-bug {
	position: absolute !important;
	left: -2500px !important;
	width: 20px !important;
}

Validator still treats is as valid document.

In the DOM tree, we get what we want – 3 forms, 2 nested into the container form.

To do

  1. A validator bug is already reported.
  2. Check if it’s browser-consistent
  3. Check what browsers are sending to server for such nested forms
Categories: Internet Tags: , , , ,
  1. mamu
    June 26th, 2009 at 02:12 | #1

    looks like this does not work with ie 8

  2. June 26th, 2009 at 08:08 | #2

    The workaround – you mean? I’ll have a look

  3. Ivan
    July 14th, 2009 at 12:05 | #3

    It means that IE 8 have some issues when displaying nested forms, which usually break the design for some reason.

    This is how I got this page btw, finding a way to display properly nested form on IE 8.

    Hopefully when ASP.NET sites are converted to the next version of the framework, this will no longer be issue, since the next version no longer *always* requires a from.

    It does not support multiple forms, however, but there is still time to fix this before the release.

    — Ivan

  4. July 14th, 2009 at 16:47 | #4

    Yes, but having multiple forms is sometimes the simplest way to desing an application, istead of writing one big page that’s doing everything, you can send users to specific pages.

  5. September 14th, 2009 at 20:28 | #5

    I had to submit a post to a different domain within the confines of a DotNetNuke module, tricky when the server handling the request expected the form to be named differently than the form for DNN. This technique worked beautifully. Thanks for the post Jakub.

  6. Richard
    October 21st, 2010 at 14:32 | #6

    Working in a .NET application, everything gets wrapped in a form. So there’s nested forms all over the place. The cringeworthy workaround works for me, thank you so much.

  7. December 8th, 2010 at 09:51 | #7

    I am looking for a solution but I can do a workaround since I have a bit of relaxed requirements. I want to use bassistance validation on asp.net form for my Popup form. i think i can put the form AFTER asp.net’s runat server form. And that should work? Cuz I am showing the other form in a popup so there shouldn’t be any issue of positioning, I’ll position it using jquery ui.

    So possibly if you can show things in popups and you can put the second form AFTER first form then due to model popups, positioning issue should be resolved.

    Thanks

  8. December 8th, 2010 at 13:18 | #8

    well, please try this workaround and see if it works. please post back to share your results.

  9. Sandro
    July 4th, 2011 at 17:53 | #9

    This trick doesn’t seem to work in Chrome. The outer form is terminated at the end of the dummy nested form. The lack of support for nested forms is quite annoying, and is yet another unjustified and arbitrary restriction.

  10. Trishul
    May 11th, 2012 at 17:09 | #10

    Thanks a lot. This seemed to work in ie8 as well.

  1. March 12th, 2010 at 22:13 | #1
  2. May 8th, 2012 at 09:49 | #2