Sunday, December 22, 2013

Using Bootstrap Dropdown Buttons as a Form Select Box


The Bootstrap Button Dropdown is a nice control that can be customized to do a variety of tasks above and beyond what a normal select box can achieve. You're not limited by presenting a list of options to the user in the menu. You can add additional functionality to the menu like a search box and other logic. However, by default, the Button Dropdown is really just a menu of actions not really designed to represent a selected data value. Wiring up the control to behave like a select box is not too difficult. A little Javascript is necessary to handle a selection and change the label on the button:

<div class="btn-group btn-input clearfix">
  <button type="button" class="btn btn-default dropdown-toggle form-control" data-toggle="dropdown">
    <span data-bind="label">Select One</span> <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu">
    <li><a href="#">Item 1</a></li>
    <li><a href="#">Another item</a></li>
    <li><a href="#">This is a longer item that will not fit properly</a></li>
  </ul>
</div>


Using that markup will enable the built-in dropdown widget so the menu will open when the button is clicked. In addition to that default behavior, add a listener for click events in the menu:

   $( document.body ).on( 'click', '.dropdown-menu li', function( event ) {

      var $target = $( event.currentTarget );

      $target.closest( '.btn-group' )
         .find( '[data-bind="label"]' ).text( $target.text() )
            .end()
         .children( '.dropdown-toggle' ).dropdown( 'toggle' );

      return false;

   });


When an item is selected, this code will change the button label and hide the menu. Now we have a select box, however, the styling on Dropdown Button does not match the styling on other form controls. When the text in the button is too short, the button shrinks to fit. When that text is too long, the button expands outside the bounds of its parent:



A and B represent the default behavior. C and D show what happens if you simply add a .form-control class to the button. And E and F illustrate the desired behavior after adding some additional styles to the .btn-group class and contents of the button (the text and caret spans):

.btn-input {
   display: block;
}

.btn-input .btn.form-control {
    text-align: left;
}

.btn-input .btn.form-control span:first-child {
   left: 10px;
   overflow: hidden;
   position: absolute;
   right: 25px;
}

.btn-input .btn.form-control .caret {
   margin-top: -1px;
   position: absolute;
   right: 10px;
   top: 50%;
}



The two main difficulties I faced making this work properly was the display: inline-block on the .btn-group and the overflow of the text inside the button. The most significant change in the styles is using absolute positioning inside the button to align the label and caret to better control their size and position. I placed this example in a jsFiddle if you'd like to experiment with it more. If these modified controls are contained inside a grid cell, they will probably work pretty well. Other use cases may require additional tweaks to make functional.