Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 713
User402841 Avatar asked Aug 22 '14 13:08

User402841


1 Answers

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.

like image 188
joshhunt Avatar answered Nov 13 '22 22:11

joshhunt