71

I would like to have a set of radio buttons for a donation form, however I want them to look like buttons instead of the circle dials.

What is the best approach to making something like that? Also, keep in mind, it has to work with IE8.

Here's what I have so far, http://jsfiddle.net/YB8UW/

.donate-now {
     list-style-type:none;
     margin:25px 0 0 0;
     padding:0;
}

.donate-now li {
     float:left;
     margin:0 5px 0 0;
}

.donate-now label {
     padding:5px;
     border:1px solid #CCC; 
     cursor:pointer;
}

.donate-now label:hover {
     background:#DDD;
}

Thank you

Richard
  • 1,414
  • 2
  • 16
  • 27
  • 1
    Have you tried searching for "custom radio buttons" on SO? [eg. http://stackoverflow.com/questions/9266868/custom-radio-buttons-ie-will-kill-me] – Orbling Apr 26 '13 at 18:33
  • try this http://jqueryui.com/button/#radio, this could ne what you are looking for. – PSL Apr 26 '13 at 18:36
  • There are a certain set of expectations a user has regarding buttons, are you sure this is a wise thing to do? – cimmanon Apr 26 '13 at 18:53
  • @cimmanon Well, I'm sure if you styled it right, it could be made pretty clear to the user that these aren't regular buttons. My answer probably does NOT do this, but I was simply showing an example for him to expand upon. That having been said, your point is a good one to remember. – VoidKing Apr 26 '13 at 19:24
  • Richard, This question is 3 years old. Can you please mark an answer as accepted? @PlantTheIdea's answer is very nice. – grokpot Nov 30 '16 at 17:23
  • https://stackoverflow.com/questions/4641752/css-how-to-style-a-selected-radio-buttons-label – Anchellon Sep 12 '20 at 07:05

6 Answers6

148

It can be done with CSS and without the need of a large framework. I have done this with checkboxes and radio buttons.

This works without adding new HTML, or bringing in any JS libraries. => jsFiddle

THE NEW ORDER OF THE HTML

This is the ten minute hacky version, you can clean it up even further, but it shows a good example of how simple it is.

.donate-now {
  list-style-type: none;
  margin: 25px 0 0 0;
  padding: 0;
}

.donate-now li {
  float: left;
  margin: 0 5px 0 0;
  width: 100px;
  height: 40px;
  position: relative;
}

.donate-now label,
.donate-now input {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.donate-now input[type="radio"] {
  opacity: 0.01;
  z-index: 100;
}

.donate-now input[type="radio"]:checked+label,
.Checked+label {
  background: yellow;
}

.donate-now label {
  padding: 5px;
  border: 1px solid #CCC;
  cursor: pointer;
  z-index: 90;
}

.donate-now label:hover {
  background: #DDD;
}
<ul class="donate-now">
  <li>
    <input type="radio" id="a25" name="amount" />
    <label for="a25">$25</label>
  </li>
  <li>
    <input type="radio" id="a50" name="amount" />
    <label for="a50">$50</label>
  </li>
  <li>
    <input type="radio" id="a75" name="amount" checked="checked" />
    <label for="a75">$75</label>
  </li>
  <li>
    <input type="radio" id="a100" name="amount" />
    <label for="a100">$100</label>
  </li>
  <li>
    <input type="radio" id="other" name="amount" />
    <label for="other">other:</label>
  </li>
  <li>
    <input type="text" id="otherAmount" name="numAmount" />
  </li>
</ul>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
PlantTheIdea
  • 16,061
  • 5
  • 35
  • 40
  • nope ... but you can create a class `Checked` just toggle the class if checked or not in javascript. its like two lines of javascript, and only necessary as a fallback, rather than as a primary. the `+` selector works all the way back to IE7. i've modified the CSS to show you. – PlantTheIdea Apr 26 '13 at 19:07
  • 6
    Don't thank me, thank the checkmark if this did what u were looking for! – PlantTheIdea Apr 29 '13 at 14:25
  • Thank god for Bootstrap – MonoThreaded Nov 04 '15 at 13:05
  • I can't get this to work. I feel like I've followed the same structure and style layout but it does not seem to work. Have tried on Chrome and on Edge. Neither works. https://jsfiddle.net/eyc6fqrj/1/ – Chris Sep 11 '18 at 00:59
  • 1
    This is a beautiful solution, but in working in all out, I don't understand why you use `
      `. What purpose does it serve? The labels themselves when situated as `display:inline-block` function quite well.
    – Douglas Held Oct 18 '18 at 20:20
  • This answer really helped me but I had a lot of li and I needed the order to be from top to bottom instead of left to right. here is how to do it https://stackoverflow.com/a/57697868/3154274 - and a little example http://jsfiddle.net/bn17wj3h/ – MagTun Aug 28 '19 at 18:09
  • Great solution, but I am also curious what the reason to have the radio buttons in `
      ` is.
    – GeorgeCiesinski Sep 20 '22 at 00:36
  • @GeorgeCiesinski the reason to nest the inputs + labels into a `
      ` is because there is a _list_ of radio buttons! Semantically, this is the way. (I prefer putting them in an `
      `, since there usually _is_ an order to them!)
    – Brian Zelip Mar 26 '23 at 11:29
7

EDIT: this isn't a solution if you need to support IE browsers.


You could use CSS appearance property:

$(':radio').on('change', function() {
  console.log(this.id);
});
input[name=amount] {
  display: none
}

.donate-now {
  list-style-type: none;
  margin: 25px 0 0 0;
  padding: 0;
}

.donate-now li {
  float: left;
  margin: 0 5px 0 0;
}

.donate-now label {
  padding: 5px;
  cursor: pointer;
  -webkit-appearance: button;
  /* WebKit */
  -moz-appearance: button;
  /* Mozilla */
  -o-appearance: button;
  /* Opera */
  -ms-appearance: button;
  /* Internet Explorer */
  appearance: button;
  /* CSS3 */
}

.donate-now label:hover {
  background: #DDD;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="donate-now">
  <li>
    <label for="a25">
            <input type="radio" id="a25" name="amount">$25</label>
  </li>
  <li>
    <label for="a50">
            <input type="radio" id="a50" name="amount">$50</label>
  </li>
  <li>
    <label for="a75">
            <input type="radio" id="a75" name="amount" checked="checked">$75</label>
  </li>
  <li>
    <label for="a100">
            <input type="radio" id="a100" name="amount">$100</label>
  </li>
  <li>
    <label for="other">
            <input type="radio" id="other" name="amount">other:
            <input type="text" id="otherAmount" name="numAmount">
        </label>
  </li>
</ul>
-webkit-appearance: button; /* WebKit */
-moz-appearance: button; /* Mozilla */
-o-appearance: button; /* Opera */
-ms-appearance: button; /* Internet Explorer */
appearance: button; /* CSS3 */
Teocci
  • 7,189
  • 1
  • 50
  • 48
A. Wolff
  • 74,033
  • 9
  • 94
  • 155
  • 1
    This looks like great, but how do I know when an item is checked? – Richard Apr 26 '13 at 19:04
  • 6
    Unfortunately, `appearance` [isn't supported at all in IE](http://caniuse.com/#feat=css-appearance) (and the `-ms-appearance` property you include here does not exist), and quite possibly never will be. Additionally, `appearance` has been [dropped from the CSS specification](https://wiki.csswg.org/spec/css4-ui#dropped-css3-features). Unless you somehow know that you are targeting only browsers that support the property - which doesn't apply to ordinary web developers writing sites for the public web - this isn't a useful real-world solution. – Mark Amery May 13 '15 at 09:55
  • 1
    @MarkAmery You are correct. But at time i made this answer, `appearance` CSS property was in specification. Now, like you said, this has been removed (except apparently `appearance: none;`). – A. Wolff May 13 '15 at 14:58
3

/* Adapted from https://stackoverflow.com/a/16243163 */

.donate-now label,
.donate-now input {
  text-align: center;
  align-items: center;
  justify-content: center;
  position: absolute;
  display: flex;
  width: 100px;
  height: 50px;
  border: 1px solid #CCC;
  padding: 0;
  cursor: pointer;
}

.donate-now input[type="radio"] {
  display: none;
}

.donate-now label:hover {
  background: #DDD;
}

.donate-now input[type="radio"]:checked + label {
  background: yellow;
}

#a25 {
  top: 20px;
  left: 20px;
}

#a50 {
  top: 20px;
  left: 140px;
}

#a75 {
  top: 20px;
  left: 260px;
}

#a100 {
  top: 20px;
  left: 380px;
}

#other {
  top: 20px;
  left: 500px;
}

#otherAmount {
  top: 20px;
  left: 620px;
}
<!-- Adapted from https://stackoverflow.com/a/16243163 -->

<div class="donate-now">
  <input type="radio" id="a25" name="amount" checked="checked" />
    <label for="a25" id="a25">$25</label>
  <input type="radio" id="a50" name="amount" />
    <label for="a50" id="a50">$50</label>
  <input type="radio" id="a75" name="amount" />
    <label for="a75" id="a75">$75</label>
  <input type="radio" id="a100" name="amount" />
    <label for="a100" id="a100">$100</label>
  <input type="radio" id="other" name="amount" />
    <label for="other" id="other">other:</label>
  <input type="text" id="otherAmount" name="numAmount" />
</div>

An adapted version of https://stackoverflow.com/a/16243163 to work without having to have all of the buttons next to each other. Change the CSS as you see fit to adjust the positions. jsFiddle

2

OR IF YOU WANT TO DO THIS YOURSELF...

What I would do would be to create the buttons with the <button> element and a hidden form field element to remember which one is "pressed" like so:

<button type="button" id="btn1">Choice 1</button>
<button type="button" id="btn2">Choice 2</button>
<button type="button" id="btn3">Choice 3</button>
<input type="hidden" id="btnValue" value="" />

You will CSS to show that the button is "pressed down" or not "pressed down" so you would need them by default to be something like this:

button
{
    border-width: 2px;
    border-style: outset;
    border-color: /*Insert a darker version of your button color here*/
}

Then in jQuery (if you can do it in jQuery, you can do it in straight JavaScript, so keep that in mind if you don't want to use jQuery):

$("#btn1").click(function () {
    $(this).css("border-style", "inset")
    $("#btn2").css("border-style", "outset;");
    $("#btn3").css("border-style", "outset;");
    $("btnValue").val("btn1IsPressed");
});
$("#btn2").click(function () {
    $(this).css("border-style", "inset")
    $("#btn1").css("border-style", "outset;");
    $("#btn3").css("border-style", "outset;");
    $("btnValue").val("btn2IsPressed");
});
$("#btn3").click(function () {
    $(this).css("border-style", "inset")
    $("#btn1").css("border-style", "outset;");
    $("#btn2").css("border-style", "outset;");
    $("btnValue").val("btn3IsPressed");
});

Now all you need to do is request the value from #btnValue after POST (or GET or whatever), just like you normally would to tell which "button" they had pressed.

Be advised you will need to add a little more functionality to "unpress" the buttons, but I think you get the point. All you would need to do is read the value of #btnValue on click and, along with your other statements,use an if branch to handle whether it is already pressed or not and switch the borders accordingly (don't forget to clear ("") the value of #btnValue on the "unpressing" of a button so you can tell whether they left them all unpressed or not).

VoidKing
  • 6,282
  • 9
  • 49
  • 81
  • while i think jquery/javascript is unnecessary for this, i greatly appreciate someone willing to provide a DIY answer. – PlantTheIdea Apr 26 '13 at 18:46
  • I always try the DIY method first. I hate implementing code I don't understand, unless it is undeniably the best course of action. Also, learning DIY methods increases the amount of tools in your "programmers toolbox" (i.e., you grow in programming experience and skills over time). A great habit to get into IMO. – VoidKing Apr 26 '13 at 18:48
  • Best way is to try to understand the wheel – A. Wolff Apr 26 '13 at 18:53
  • @roasted The meaning of your comment was lost on me. What do you mean, exactly by, "Best way is to try to understand the wheel". – VoidKing Apr 26 '13 at 18:58
  • I just mean its great to DIY but often better to look at already done plugin and understand what author done. I'm sure you know, don't reinvent the wheel – A. Wolff Apr 26 '13 at 19:00
  • I do, however, the ones who need the knowledge are the ones who probably wouldn't understand even half of the statements in those massive plugins (especially jQuery UI). – VoidKing Apr 26 '13 at 19:04
  • yeah, that kind of thinking is how people end up with 12 plugins and 8 CSS files on their page. something simple like this doesn't require plugins, libraries, anything else that will slow the page way down. its not just about learning, its about speed. – PlantTheIdea Apr 26 '13 at 19:04
  • @PlantTheIdea Well said :) – VoidKing Apr 26 '13 at 19:08
  • @PlantTheidea Also, in regards to your first comment, given all that he wants to accomplish plus the fact that he needs to be compliant with IE 8 (GRRRRR IE 8!), I find it hard to believe he can accomplish this without any scripting whatsoever (can't use `:checked`). – VoidKing Apr 26 '13 at 19:10
  • @PlantTheidea Sure CSS3 is powerful enough, very likely. I hope to one day get to use it :/ Is it a sin to assassinate a browser and remove it from all existence and memory? If not, imma start prayin' :) – VoidKing Apr 26 '13 at 19:12
  • @VoidKing you can use it right now. IE8 and below accounts for less than 15% of web traffic in the US, and even less worldwide. I code all of my sites to CSS3/HTML5 standards, and have fallbacks for IE8 and below if the client absolutely demands it. often times it just involves changing the javascript files to include a few more functions (like adding a click function to show checked, for example). i know this whole thing is about not using libraries, but Modernizr is great for identifying the missing functionality and triggering those fallback functions. – PlantTheIdea Apr 26 '13 at 19:18
  • @PlantTheidea Never heard of Modernizr. Thanks for that. Also thanks for the rough percentage value, too, wasn't really sure where that stood. However, I am sadly making Intranet sites for my place of employment, in an environment where 32-bit machines, very much, still exist and their users just can't seem to part with IE – VoidKing Apr 26 '13 at 19:21
1

Unfortunately you can't style radio buttons directly in any browser. The way to do it is to use <label> elements with properly set for attributes, then hide the radio button itself using visibility: hidden rather than display: none. You can then position the labels wherever you want and they will act as radio buttons. You will need a bit of javascript to control the styling of the labels based on the state of the <input> element because IE8 won't support advanced CSS pseudoclasses like :checked.

It's a bit of a hack but it is the only way to use native radio buttons with custom graphics and still preserve accessibility.

nullability
  • 10,545
  • 3
  • 45
  • 63
  • 1
    Did you even look at the fiddle? He does that. – SeinopSys Apr 26 '13 at 18:34
  • He doesn't hide the radio buttons at all. You can't style them so you need to just hide them. – nullability Apr 26 '13 at 18:39
  • 1
    `visibility:hidden;` makes them unclickable, you need to set their `opacity:0.01`; to maintain clickability. see my answer for reference. – PlantTheIdea Apr 26 '13 at 18:53
  • If an radio button is selected, how can I style the label to reflect that? – Richard Apr 26 '13 at 19:07
  • I would just use javascript classes bound to a .change event in the input. Something like `$("input").change(function() { $(this).closest("label").addClass($(this).prop("checked")?"checked":"notchecked");});` – nullability Apr 26 '13 at 19:14
  • As is, your response is great if left as a comment. To be considered an answer on SO, you need to embed exact code to provide a solution. Describing how one would think about the solution is welcomed as a comment. Please update you "answer" to provide an example of code that would solve the OP's answer in order to avoid deletion by the SO platform as "not an answer". – SherylHohman Oct 27 '21 at 16:42
1

Try this simple logic with jQuery.

$.each($('.radio-btn'), function(key, value) {
  $(this).click(function(e) {
    $('.radio-btn-selected')
      .removeClass('radio-btn-selected')
      .addClass('radio-btn');

    $(this)
      .removeClass('radio-btn')
      .addClass('radio-btn-selected');

    //do whatever you want on click
  });
});
.radio-btn-row {
  display: flex;
  flex-direction: row;
  justify-content: center;
  margin-top: 20px;
}

.radio-btn-wrapper {
  margin: 0px 4px;
}

.radio-btn {
  background-color: #FFFFFF;
  border: 1px solid #4A4A4A;
  color: #4A5362;
  font-size: 14px;
  line-height: 26px;
  outline: none;
}

.radio-btn-selected {
  background-color: #FFFFFF;
  border: 1px solid #55BC7E;
  color: #55BC7E;
  font-size: 14px;
  line-height: 26px;
  outline: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="radio-btn-row">
  <div class="radio-btn-wrapper">
    <button id="bt1" class="radio-btn" type="button">Button 1</button>
  </div>
  <div class="radio-btn-wrapper">
    <button id="btn2" class="radio-btn" type="button">Button 2</button>
  </div>
  <div class="radio-btn-wrapper">
    <button id="btn3" class="radio-btn" type="button">Button 3</button>
  </div>
  <div class="radio-btn-wrapper">
    <button id="btn4" class="radio-btn" type="button">Button 4</button>
  </div>
  <!--No matter how many buttons do you want -->
</div>
Teocci
  • 7,189
  • 1
  • 50
  • 48
Serenkiy
  • 249
  • 3
  • 14