views:

160

answers:

4

What's the best way to use <input type="radio"> in HTML?

I'm looking for HTML that's semantically good, whose formatting is configurable via CSS.

I want to be able to style/render it to look something like:

    Car: (o) Yes
         (X) No
         (o) Maybe

  Train: (o) Yes
         (o) No
         (X) Maybe

Address: [An input text box     ]

Thinking of the CSS, I think that I'd like the labels on the left (e.g. "Car" and "Bus") to be in some kind of text-align: right block?

I don't know about the radio buttons on the right: in some kind of <span> perhaps, with "display: inline-block"? Or "white-space: pre"?

What kind of block-level tags (e.g. <p> or <div>) and/or other tags (e.g. <span> or <br/>) would you recommend?


Edit:

How about the following.

HTML uses <legend>, like HTML is supposed to and as recommended in the alistapart article:

<fieldset>
<legend>Car</legend>
<label><input type="radio" name="car" value="yes"/> Yes</label>
<label><input type="radio" name="car" value="no"/> No</label>
<label><input type="radio" name="car" value="maybe"/> Maybe</label>
</fieldset>

To make it easer for Firefox to access/position the contents of the <legend>, place it within a <span>:

<fieldset>
<legend><span>Car</span></legend>
<label><input type="radio" name="car" value="yes"/> Yes</label>
<label><input type="radio" name="car" value="no"/> No</label>
<label><input type="radio" name="car" value="maybe"/> Maybe</label>
</fieldset>

Then, use the browser-specific CSS described in Legends of Style Revised to position the contents of the span to left of the fieldset.

Does the CSS really have to be so complicated and browser-specific? What's the simplest CSS which ought theoretically to work, instead of the more-complicated CSS required to actually work with those imperfect browsers? If <legend> is hard to position then what's a good (semantic) alternative?

A: 

Mmmm.... Car and Train should definitely be <label>s. Check out this classic A List Apart article for a really nice example: Prettier Accessible Forms

As for the radio button labels: Good question! I'd say <label> s again, but a <span> would do as well.

Pekka
@Pekka: interesting, the article uses a `<label>` element for the radio options (yes/no), but a `<legend>` element for the caption. I would definitely say `<label>` for the radio button labels, especially for accessibility (correctly using the `for` attribute).
Andy E
alistapart is using `<fieldset>`, with `<legend>` (not `<label>`) for `Car` and `Train`.
ChrisW
@Andy I think using `label for` won't work for each individual radio button because they all have the same name. Still, `label` is probably the best choice here. As @Chris correctly notes, ALA are using a `fieldset` for the radio options. Not sure whether that makes 100% sense to me.
Pekka
Unfortunately it's using `display:block` for the labels, so its markup ensures that the legend is above (not to the left of, which is what I want) all the radio buttons and their labels.
ChrisW
@ChrisW: `legend` is written on the `fieldset` border.
Amadan
@Amadan - you're right: so it is. Is the display of that configurable, or, must I therefore want to use something other than `<legend>`?
ChrisW
@Pekka — the `for` attribute keys off the `id`, not the `name`.
David Dorward
A label can be associated with one, and only one input. You can't associate a label "Car" with the fields "Yes", "No" **and** "Maybe"
David Dorward
@Pekka: `label for` looks up `id`, having same `name` doesn't matter. `label` is made for labelling boxes - they are small and clickable only with difficulty, so `label` extends the targeting area.
Amadan
@Pekka: the `for` is applicable to the `id` attribute, not the `name` attribute -- [spec](http://www.w3.org/TR/html401/interact/forms.html#h-17.9.1). Also, ALA are wrapping the label element around the input element instead, which I thought had issues in IE but I could be wrong. The `<label>` element's purpose is to associate with an `<input>` element for accessibility - clicking on the label will than perform an action on the associated input, either focusing it (text input/select) or checking it (checkbox/radio).
Andy E
@Andy @Amadan ah, makes sense. Wasn't aware it was possible to target `id` s as well.
Pekka
@David I see - that's why ALA are using the fieldset in their example. Thanks for the clarification.
Pekka
@Pekka — not *as well*. The for attribute does not target the name attribute, ever. Just the id attribute.
David Dorward
@David @Andy hah, I've been doing this wrong for years now! :) I stand corrected. Cheers!
Pekka
` ` @Pekka: :-)
Andy E
+7  A: 

This is what I usually do with my radio buttons and checkboxes. It allows the associated text to be clickable in most browsers without having to do any work, which makes the form a little easier to use. The CSS cursor change helps to alert the user to this feature.

CSS

label { cursor: pointer; }

HTML

<label><input type="radio" name="option" value="yes"> Yes</label>
<label><input type="radio" name="option" value="no"> No</label>
<label><input type="radio" name="option" value="maybe"> Maybe</label>

Alternatively, use a fieldset legend for cars and a ul for the list of radio buttons:

<fieldset>
    <legend>Cars</legend>
    <ul class="radio-list">
        <li><label><input type="radio" name="option" value="yes"> Yes</label></li>
        <li><label><input type="radio" name="option" value="no"> No</label></li>
        <li><label><input type="radio" name="option" value="maybe"> Maybe</label></li>
    </ul>
<fieldset>

CSS

.radio-list li { list-style: none; }

Stylizing a fieldset/legend to be consistent across browsers isn't too difficult; however, it does require one IE conditional if you want a border around the legend. The only extra HTML that is necessary is a wrapper span within the legend.

CSS

<style>
    fieldset {
        position: relative;
        border: 1px solid #000;
        background: #f8f8f8;
        padding: 1.6em 10px 0px;
        margin: 0;
    }
    legend {
        position: absolute;
        font-weight: bold;
        font-size: 1.2em;
    }
    legend span {
        position: absolute;
        top: -1.1em;
        white-space: nowrap;
    }

    /* This isn't necessary, just here for list aesthetics */
    ul, li {
        margin: 0;
        padding: 0;
        list-style-type: none;
    }
</style>

<!--[if IE]>
    <style>
    legend {
        border-bottom: 1px solid #000;
    }
    </style>
<![endif]-->

HTML

<fieldset>
    <legend><span>Did you enjoy your SO experience?</span></legend>
    <form>
        <ul>
            <li><label><input type="radio" name="option" value="yes"> Yes</label></li>
            <li><label><input type="radio" name="option" value="no"> No</label></li>
            <li><label><input type="radio" name="option" value="maybe"> Maybe</label></li>
        </ul>
    </form>
</fieldset>

That's about as simple as I can get it. Live example

Justin Johnson
Hey, nice idea!
Pekka
+1, exactly what I would do except for one addition.
Josh K
In the first example, how do you get the labels to arrange vertically to the right of the legend? In the second example, is having `<ul>` and `<li>`s a lot of extra display-related non-semantic markup?
ChrisW
@Chris: It's a list of radio buttons. It's *completely* semantic markup and entirely valid. What *isn't* valid is trying to turn it into a list by using CSS on the labels. Labels aren't block level elements by nature, use them accordingly.
Josh K
@Chris: I added a live example. Depending on *precisely* how you want your form laid out this may work. Needing the lineup the way you have indicated would mean removing the border around a fieldset and floating the legend left while providing the ul with an additional margin to compensate.
Josh K
@Chris: I updated that with a second form that styles it more like your original question.
Josh K
It is more like original the question; except that (using IE8 and using Firefox) the `<legend>` is above as well as to the left of the radio buttons. I'd prefer it to be level with the corresponding button[s] on the right, in order to match other things on the form (e.g. where a label on the left is level with a corresponding `<input type="text">` on the right).
ChrisW
@Chris: That page is bare. By bare I mean no CSS reset or the like. Including something like [Blueprint](http://www.blueprintcss.org) would probably alleviate all or some of those issues.
Josh K
@Josh I ammended my OP to reference an article which shows how to position a `<legend>`; but that CSS seems complicated to me, and browser-specific ... but using `<legend>` is probably the most semantically correct; so, what a dilemma.
ChrisW
@Chris: I included the blueprint styles, let me know if that fixes the rendering.
Josh K
It doesn't fix it, in either browser. http://www.tyssendesign.com.au/articles/css/legends-of-style/ suggests something more complicated.
ChrisW
@chrisw Adding `display:block` to `label`'s is perfectly reasonable.
Justin Johnson
@Justin Johnson I agree with adding `display:block` to `label`s is reasonable, is, but I don't know why you're suggesting it: doing that wouldn't help with getting the legend positioned to the left of the radio buttons.
ChrisW
Sorry, that should have been in response to JoshK's first comment.
Justin Johnson
A: 

css: (ugly class names need to be changed)

p.radio { height: 4em; }
label.top {
  display: block;
  width: 4em; /* or something else */
  float: left;
  text-align: right;
  padding-right: 1em;
  height: 4em;
}

html:

<p class="radio">
  <label class="top" for="car">Car:</label>
  <input type="radio" value="yes" name="car" id="car_y" />
  <label for="car_y">Yes</label><br />
  <input type="radio" value="no" name="car" id="car_n" />
  <label for="car_n">No</label><br />
  <input type="radio" value="maybe" name="car" id="car_m" />
  <label for="car_m">Maybe</label><br />
</p>

EDIT: didn't see the other answer. Using a fieldset instead of a paragraph and legend for the "top-level" label seems to be a good idea IMO.

EDIT2: according to comments, and I agree, using a list would be cleaner here. The new version would be :

css:

fieldset {
  border: 0;
  padding: 0;
}
legend {
  padding: 0 0.5em 0 0;
  margin: 0;
  display: block;
  width: 3.5em;
  float: left;
  text-align: right;
}
ul {
  margin: 0;
  padding: 0 0 0 4em;
}
ul li {
  list-style-type: none;
  list-style-position: outer;
}

html:

<fieldset>
  <legend>Car:</legend>
  <ul>
    <li>
      <input type="radio" value="yes" name="car" id="car_y" />
      <label for="car_y">Yes</label>
    </li>
    <li>
      <input type="radio" value="no" name="car" id="car_n" />
      <label for="car_n">No</label>
    </li>
    <li>
      <input type="radio" value="maybe" name="car" id="car_m" />
      <label for="car_m">Maybe</label>
    </li>
  </ul>
</fieldset>

That would be much more elegant. But even with display:block firefox doesn't seem to want to set the width of a legend element. Strange bug.

p4bl0
So you're getting the top-most legend to the left by doing `float: left`, instead of getting the radio buttons to the right using `display: inline-block`. Yea, that could be a good way to do it.
ChrisW
@Josh K - Why is it a list? Are `<input type="radio">` element usually supposed to be contained within list items?
ChrisW
There is no element with the id *car*, so the first label is not associated with any form controls.
David Dorward
@Chris: It doesn't need to be, but according to how you are presenting, it's a list of radio buttons.
Josh K
@David Dorward - That's true. What would you do to correct that: replace it with a `<span>`?
ChrisW
legend, as per earlier answers
David Dorward
@David It's difficult to position a legend to the left of the radio buttons: see e.g. [Legends of Style Revised](http://www.alistapart.com/articles/prettyaccessibleforms).
ChrisW
A: 

I'll use <fieldset>.

The difficulty with positioning a <label> in different browsers is described in the "Legends of Style Revised" article; so instead of using a <label> and trying to position it, I might use a <span class="label"> outside the <fieldset>.

ChrisW