Как структурировать HTML-формы

Вы читаете английскую версию этой статьи, так как пока нет перевода на данный язык. Помогите нам перевести эту статью!

Получив базовые знания, теперь мы более подробно рассмотрим элементы, используемые для придания структуры и значения различным частям форм.

Уровень подготовки: Основы компьютерной грамотности, и базовые знания HTML.
Цель: Разобраться как структурировать HTML формы и задавать им семантику для того, чтобы они были удобны и доступны в использовании.

Гибкость HTML форм делает их одной из самых сложных структур в HTML; вы можете создать любую форму, используя элементы и атрибуты форм. Использование правильной структуры, при создании HTML форм, поможет гарантировать их удобство и доступность.

Элемент <form>

Элемент <form> формально определяет форму и атрибуты, которые определяют поведение этой формы. Каждый раз, когда вы хотите создать HTML-форму, вам нужно начать с создания элемента <form>, поместив внутрь него всё содержимое. Многие вспомогательные технологии или браузерные плагины могут обнаруживать элементы <form> и реализовывать специальные хуки, чтобы их было проще использовать.

Мы уже встречались с этим в предыдущей статье.

Примечание: Строго запрещается размещать форму внутри другой формы. Такое размещение может привести к непредсказуемому поведению форм, в зависимости от браузера. 

Стоит учесть, что всегда можно использовать элементы формы вне <form>. Тогда по умолчанию этот элемент формы не имеет ничего общего со всеми формами. Вы можете связать его с формой с помощью аттрибута form. В HTML5 был представлен аттрибут form для элементов HTML форм, который позволяет  явно связать элемент с формой, даже если он не заключён внутри <form>.

Элементы <fieldset> и <legend>

Элемент <fieldset> - это удобный способ стилистической и семантической группировки элементов формы. Вы можете установить заголовок <fieldset>, добавив элемент <legend> сразу после открывающего тега <fieldset>. Текст элемента <legend> формально описывает назначение содержимого <fieldset>.

Различные вспомогательные технологии будут использовать <legend> как часть метки label всех элементов внутри <fieldset>. Например, такие экранные дикторы как Jaws или NVDA произносят заголовок формы <legend> перед произношением названия меток элементов.

Небольшой пример:

<form>
  <fieldset>
    <legend>Fruit juice size</legend>
    <p>
      <input type="radio" name="size" id="size_1" value="small">
      <label for="size_1">Small</label>
    </p>
    <p>
      <input type="radio" name="size" id="size_2" value="medium">
      <label for="size_2">Medium</label>
    </p>
    <p>
      <input type="radio" name="size" id="size_3" value="large">
      <label for="size_3">Large</label>
    </p>
  </fieldset>
</form>

Примечание: Вы можете найти этот пример в fieldset-legend.html (также посмотрите на результат).

Читая эту форму, экранный диктор произнесёт "Fruit juice size small" для первого элемента, "Fruit juice size medium" - для второго, "Fruit juice size large" - для третьего.

Вариант использования в этом примере является одним из наиболее важных. Каждый раз, когда у вас есть набор переключателей, вам нужно поместить их внутри <fieldset>. Также <fieldset> можно использовать для разделения формы. В идеале, длинную форму разделяют на несколько страниц, однако, если она должна находиться на одной странице, распределение связанных элементов в разные <fieldset> может повысить удобство использования.

Из-за своего влияния на вспомогательные технологии элемент <fieldset> является одним из ключевых элементов для построения доступных форм; однако вы не должны им злоупотреблять. Если возможно, старайтесь проверять, как экранный диктор интерпретирует вашу форму. 

The <label> element

As we saw in the previous article, The <label> element is the formal way to define a label for an HTML form widget. This is the most important element if you want to build accessible forms — when implemented properly, screenreaders will speak a form element's label along with any related instructions. Take this example, which we saw in the previous article:

<label for="name">Name:</label> <input type="text" id="name" name="user_name">

With the <label> associated correctly with the <input> via their for and id attributes respectively (the label for attribute references the id attribute of the corresponding widget), a screenreader will read out something like "Name, edit text".

If the label isn't set up correctly, a screenreader will only read out something like "Edit text blank", which isn't very helpful at all.

Note that a widget can be nested inside its <label> element, like so:

<label for="name">
  Name: <input type="text" id="name" name="user_name">
</label>

Even in such cases however, it is considered best practice to set the for attribute because some assistive technologies do not understand implicit relationships between labels and widgets.

Labels are clickable, too!

Another advantage of properly set up labels is that you can click the label to activate the corresponding widget, in all browsers. This is useful for examples like text inputs, where you can click the label as well as the input to focus it, but it is especially useful for radio buttons and checkboxes — the hit area of such a control can be very small, so it is useful to make it as big as possible.

For example:

<form>
  <p>
    <label for="taste_1">I like cherry</label>
    <input type="checkbox" id="taste_1" name="taste_cherry" value="1">
  </p>
  <p>
    <label for="taste_2">I like banana</label>
    <input type="checkbox" id="taste_2" name="taste_banana" value="2">
  </p>
</form>

Note: You can find this example in checkbox-label.html (see it live also).

Multiple labels

Strictly speaking, you can put multiple labels on a single widget, but this is not a good idea as some assistive technologies can have trouble handling them. In the case of multiple labels, you should nest a widget and its labels inside a single <label> element.

Let's consider this example:

<p>Required fields are followed by <abbr title="required">*</abbr>.</p>

<!-- So this: -->
<div>
  <label for="username">Name:</label>
  <input type="text" name="username">
  <label for="username"><abbr title="required">*</abbr></label>
</div>

<!-- would be better done like this: -->
<div>
  <label for="username">
    <span>Name:</span>
    <input id="username" type="text" name="username">
    <abbr title="required">*</abbr>
  </label>
</div>

<!-- But this is probably best: -->
<div>
  <label for="username">Name: <abbr title="required">*</abbr></label>
  <input id="username" type="text" name="username">
</div>

The paragraph at the top defines the rule for required elements. It must be at the beginning to make sure that assistive technologies such as screen readers will display or vocalize it to the user before they find a required element. That way, they will know what the asterisk means. A screen reader will speak the star as "star" or "required", depending on the screen reader's settings — in any case, what will be spoken is made clear in the first paragraph).

  • In the first example, the label is not read out at all with the input — you just get "edit text blank", plus the actual labels are read out separately. The multiple <label> elements confuse the screenreader.
  • In the second example, things are a bit clearer — the label read out along with the input is "name star name edit text", and the labels are still read out separately. Things are still a bit confusing, but it's a bit better this time because the input has a label associated with it.
  • The third example is best — the actual label is read out all together, and the label read out with the input is "name star edit text".

Note: You might get slightly different results, depending on your screenreader. This was tested in VoiceOver (and NVDA behaves similarly). We'd love to hear about your experiences too.

Note: You can find this example on GitHub as required-labels.html (see it live also). don't run the example with 2 or 3 of the versions uncommented — screenreaders will definitely get confused if you have multiple labels AND multiple inputs with the same ID!

Common HTML structures used with forms

Beyond the structures specific to HTML forms, it's good to remember that forms are just HTML. This means that you can use all the power of HTML to structure an HTML form.

As you can see in the examples, it's common practice to wrap a label and its widget with a <div> element. <p> elements are also commonly used, as are HTML lists (the latter is most common for structuring multiple checkboxes or radio buttons).

In addition to the <fieldset> element, it's also common practice to use HTML titles (e.g. <h1>, <h2>) and sectioning (e.g. <section>) to structure a complex form.

Above all, it is up to you to find a style that you find comfortable to code with, and which also results in accessible, usable forms.

This has each separate section of functionality contained in <section> elements, and a <fieldset> to contain the radio buttons.

Active learning: building a form structure

Let's put these ideas into practice and build a slightly more involved form structure — a payment form. This form will contain a number of widget types that you may not yet understand — don't worry about this for now; you'll find out how they work in the next article (The native form widgets). For now, read the descriptions carefully as you follow the below instructions, and start to form an appreciation of which wrapper elements we are using to structure the form, and why.

  1. To start with, make a local copy of our blank template file and the CSS for our payment form in a new directory on your computer.
  2. First of all, apply the CSS to the HTML by adding the following line inside the HTML <head>:
    <link href="payment-form.css" rel="stylesheet">
  3. Next, start your form off by adding the outer <form> element:
    <form>
    
    </form>
  4. Inside the <form> tags, start by adding a heading and paragraph to inform users how required fields are marked:
    <h1>Payment form</h1>
    <p>Required fields are followed by <strong><abbr title="required">*</abbr></strong>.</p>
  5. Next we'll add a larger section of code into the form, below our previous entry. Here you'll see that we are wrapping the contact information fields inside a distinct <section> element. Moreover, we have a set of two radio buttons, each of which we are putting inside its own list (<li>) element. Last, we have two standard text <input>s and their associated <label> elements, each contained inside a <p>, plus a password input for entering a password. Add this code to your form now:
    <section>
        <h2>Contact information</h2>
        <fieldset>
          <legend>Title</legend>
          <ul>
              <li>
                <label for="title_1">
                  <input type="radio" id="title_1" name="title" value="M." >
                  Mister
                </label>
              </li>
              <li>
                <label for="title_2">
                  <input type="radio" id="title_2" name="title" value="Ms.">
                  Miss
                </label>
              </li>
          </ul>
        </fieldset>
        <p>
          <label for="name">
            <span>Name: </span>
            <strong><abbr title="required">*</abbr></strong>
          </label>
          <input type="text" id="name" name="username">
        </p>
        <p>
          <label for="mail">
            <span>E-mail: </span>
            <strong><abbr title="required">*</abbr></strong>
          </label>
          <input type="email" id="mail" name="usermail">
        </p>
        <p>
          <label for="pwd">
            <span>Password: </span>
            <strong><abbr title="required">*</abbr></strong>
          </label>
          <input type="password" id="pwd" name="password">
        </p>
    </section>
  6. Now we'll turn to the second <section> of our form — the payment information. Here we have three distinct widgets along with their labels, each contained inside a <p>. The first is a drop down menu (<select>) for selecting credit card type. the second is an <input> element of type number, for entering a credit card number. The last one is an <input> element of type date, for entering the expiration date of the card (this one will come up with a date picker widget in supporting browsers, and fall back to a normal text input in non-supporting browsers). Again, enter the following below the previous section:
    <section>
        <h2>Payment information</h2>
        <p>
          <label for="card">
            <span>Card type:</span>
          </label>
          <select id="card" name="usercard">
            <option value="visa">Visa</option>
            <option value="mc">Mastercard</option>
            <option value="amex">American Express</option>
          </select>
        </p>
        <p>
          <label for="number">
            <span>Card number:</span>
            <strong><abbr title="required">*</abbr></strong>
          </label>
          <input type="number" id="number" name="cardnumber">
        </p>
        <p>
          <label for="date">
            <span>Expiration date:</span>
            <strong><abbr title="required">*</abbr></strong>
            <em>formatted as mm/yy</em>
          </label>
          <input type="date" id="date" name="expiration">
        </p>
    </section>
  7. The last section we'll add is a lot simpler, containing only a <button> of type submit, for submitting the form data. Add this to the bottom of your form now:
    <p> <button type="submit">Validate the payment</button> </p>

You can see the finished form in action below (also find it on GitHub — see our payment-form.html source and running live):

Summary

You now have all the knowledge you'll need to properly structure your HTML forms; the next article will dig into implementing all the different types of form widgets you'll want to use to collect information from your users.

See Also

In this module