Radio button value is not properly sent when using btn-group
Asked Answered
O

1

10

When I show fancy radio buttons using btn-group and the latest Twitter Bootstrap, the value submitted is not always the same as the value that is marked on screen. The problem is that the browser marks the boxes as checked (it adds the active class), but the actual element remains unchecked.

Both in Firefox and Chrome this problem occurs. It does not always occur, but it occurs around 7% of the time (tested 150 times). I click a bunch of buttons to mark all of the elements with a score of say 5, and it will look like this:

All marked with score 5

But when I submit and show the result, then you can see that it does not send the same!

Array with results, not the same

This behavior is erratic and unpredictable, adding to my confusion, but it is reproducible, It just does not happen all the time. To see, please try either of two options. KyleMit was so friendly as to make this into a jsfiddle. Please try that one out. Submit the form some 30 times (changing the value each time you submit), and you will hit the error. I am showing a screenshot below.

The second option is to run the code below which has more input field. Change all of the fields and they will not come out the same. An error will appear after about 6 tries.

<?php
if ($_POST) {
  var_dump($_POST['quality']);
}
?>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    <title>Test</title>
    <style type="text/css">
        .btn { opacity:1 }
    </style>
  </head>
  <body>
    <div class="row">
      <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
        <form method="post" class="form form-horizontal">
          <fieldset>
            <legend>Quality</legend>
            <div class="row">
              <div class="col-sm-3">Test 1</div>
              <div class="col-sm-9">
                <div class="btn-group" data-toggle="buttons">
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q156" name="quality[25]" value="1" /> 1
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q157" name="quality[25]" value="2" /> 2
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q158" name="quality[25]" value="3" /> 3
                    </label> 
                    <label class="tooltip btn btn-default">
                    <input type="radio" id="q159" name="quality[25]" value="4" /> 4
                        </label> 
                    <label class="tooltip btn btn-default active">
                        <input type="radio" id="q160" name="quality[25]" checked="checked" value="5" /> 5
                    </label>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-3">Test 2</div>
              <div class="col-sm-9">
                <div class="btn-group" data-toggle="buttons">
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q128" name="quality[21]" value="1" /> 1
                    </label> 
                    <label class="tooltip btn btn-default active">
                        <input type="radio" id="q129" name="quality[21]" checked="checked" value="2" /> 2
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q130" name="quality[21]" value="3" /> 3
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q131" name="quality[21]" value="4" /> 4
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q132" name="quality[21]" value="5" /> 5
                    </label>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-3">Test 3</div>
              <div class="col-sm-9">
                <div class="btn-group" data-toggle="buttons">
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q149" name="quality[24]" value="1" /> 1
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q150" name="quality[24]" value="2" /> 2
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q151" name="quality[24]" value="3" /> 3
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q152" name="quality[24]" value="4" /> 4
                    </label> 
                    <label class="tooltip btn btn-default active">
                        <input type="radio" id="q153" name="quality[24]" checked="checked" value="5" /> 5
                    </label>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-3">Test 4</div>
              <div class="col-sm-9">
                <div class="btn-group" data-toggle="buttons">
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q142" name="quality[23]" value="1" /> 1
                    </label> 
                    <label class="tooltip btn btn-default active">
                        <input type="radio" id="q143" name="quality[23]" checked="checked" value="2" /> 2
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q144" name="quality[23]" value="3" /> 3
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q145" name="quality[23]" value="4" /> 4
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q146" name="quality[23]" value="5" /> 5
                    </label>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-3">Test 5</div>
              <div class="col-sm-9">
                <div class="btn-group" data-toggle="buttons">
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q121" name="quality[20]" value="1" /> 1
                    </label> 
                    <label class="tooltip btn btn-default active">
                        <input type="radio" id="q122" name="quality[20]" checked="checked" value="2" /> 2
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q123" name="quality[20]" value="3" /> 3
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q124" name="quality[20]" value="4" /> 4
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q125" name="quality[20]" value="5" /> 5
                    </label>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-3">Test 6</div>
              <div class="col-sm-9">
                <div class="btn-group" data-toggle="buttons">
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q135" name="quality[22]" value="1" /> 1
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q136" name="quality[22]" value="2" /> 2
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q137" name="quality[22]" value="3" /> 3
                    </label> 
                    <label class="tooltip btn btn-default">
                        <input type="radio" id="q138" name="quality[22]" value="4" /> 4
                    </label> 
                    <label class="tooltip btn btn-default active">
                        <input type="radio" id="q139" name="quality[22]" checked="checked" value="5" /> 5
                    </label>
                </div>
              </div>
            </div>
          </fieldset>
          <button type="submit" class="btn btn-primary">Save</button>
        </form>
      </div>
    </div>
  </body>
</html>

Fiddle results:

Submit value 5, received differently

Opisthognathous answered 22/8, 2014 at 13:12 Comment(5)
In order to help you more, we probably need a Minimal, Complete, and Verifiable example. This is neither minimal nor verifiable. You need to be able to repeatedly get the same error with as little code as possible. Here's a fiddle to get you started. Also, bootstrap doesn't do anything except style the buttons. Underlying the radiobutton inputs is just regular html that gets serialized the same way on a form submit. The only thing I can think of is that the active class isn't keeping up with quick clicksRonnie
@Ronnie Thank you very much. Please note though that, as I mentioned, this behavior is erratic and unpredictable. If I could repeat and verify it every time I think the problem would be easier to solve. But it is not. I had left a whole bunch of inputs on the page (and nothing else) because it only happens once every so many times and that way one can try out many at once. But I'll add the fiddle to my question. Thanks.Opisthognathous
@Ronnie I submitted the form 70 more times and it occurred 14 times, even when not clicking quickly. So I took that part out. I am still at a complete loss what could cause this though...Opisthognathous
I can get it happening fairly consistently with this fiddle. Then when I remove bootstrap.js it starts working as it should. From my results it seems to change the active class but not the checked attribute. Had a look at the Bootstrap JS Source but can't see anything that would prevent the checked attribute from changing. Also not 100% sure but it seems to only be happening when there are multiple sets of radio inputs.Bullfight
Actually just looked at the bottom of that source code, perhaps it is something to do with e.preventDefault() interfering?Bullfight
B
11

Solved! Solution:

[data-toggle="buttons"] > .btn > input[type="radio"],
[data-toggle="buttons"] > .btn > input[type="checkbox"] {
    visibility: hidden;
}

So turns out that it was only happening when you click on a certain area of the button. Even though it was invisible, a tiny bit of the actual radio button was clickable (just to the right of the number) and when you clicked it the Bootstrap javascript was preventing it from changing its checked state but the javascript continued and added the active class. I noticed this because the mouse hand changed from the pointer to the default cursor when you hovered over the small radio button area that was clickable.

While I can't think of any, my solution might have negative effects. I'm not sure of the reasoning behind Bootstrap choosing opacity: 0; over visibility: hidden; but perhaps someone more knowledgable might be able to provide some.

Update

Ok after doing a little more digging I found out why they use opacity instead of visibility.

In order to support the browser's form validation feedback, powered by the required attribute, we have to "hide" the inputs via opacity. We cannot use display: none; or visibility: hidden; as that also hides the popover. This way, we ensure a DOM element is visible to position the popover from.

See https://github.com/twbs/bootstrap/pull/12794 for more.

So pretty much you can't use visibility: hidden; if you want to be able to use native browser validation.

It actually seems to be a known bug, see this Github issue.

Update 2

This solution is probably better:

[data-toggle="buttons"] > .btn > input[type="radio"],
[data-toggle="buttons"] > .btn > input[type="checkbox"] {
    width: 1px;
}

and this solution might be even better:

[data-toggle="buttons"] > .btn:after,
[data-toggle="buttons"] > .btn:after {
    content: "";
    display: block;
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
}

Update 3

And this is probably the best solution of all for now. As suggested by Heinrich Fenkart here.

[data-toggle="buttons"] > .btn > input[type="radio"],
[data-toggle="buttons"] > .btn > input[type="checkbox"] {
    clip: rect(1px, 1px, 1px, 1px);
    pointer-events: none;
}

Also it looks like Bootstrap will probably have this fixed in the next release.

Bullfight answered 26/8, 2014 at 0:2 Comment(4)
Wow, great find! I also can't understand why Bootstrap may have chosen this. Wouldn't many (if not all) btn-groups be at risk of not saving the right information? I will assign you the bounty if no other answers provide a better explanation of why this was done in that way (because there could potentially be better solutions with no negative effects). Thanks!Opisthognathous
Updated my answer, hopefully that is more helpful?Bullfight
VERY detailed and useful! Great find! Bounty awarded!Opisthognathous
@ejcortes glad I could help :)Bullfight

© 2022 - 2024 — McMap. All rights reserved.