selectr

A jquery plugin for select boxes that doesn't suck

by Casey Webb

HTML

<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<select data-selectr-opts='{ "title": "What&#39;s for breakfast? (max 5)", "placeholder": "Search foods", "maxSelection": 5 }' multiple>
    <option data-selectr-color="rgb(0, 163, 0)" value="1">Bacon (Extra long name foo bar baz quz)</option>
    <option data-selectr-color="rgb(255, 0, 151)" value="2">Eggs</option>
    <option data-selectr-color="rgb(159, 0, 167)" value="3">Toast</option>
    <option data-selectr-color="rgb(185, 29, 71)" value="4">Oatmeal</option>
    <option data-selectr-color="rgb(227, 162, 26)" value="13">Steak</option>
    <option data-selectr-color="rgb(218, 83, 44)" value="14">Sausage</option>
    <option data-selectr-color="rgb(96, 60, 186)" value="23">Coffee</option>
    <option data-selectr-color="rgb(0, 171, 169)" value="24">Milk</option>
    <option data-selectr-color="rgb(45, 137, 239)" value="33">Fresh Fruit</option>
</select>

SCSS

// selectr was built using Bootstrap components, so the css required is
// rather lean. If your project is not using Bootstrap, include the selectrPolyfill
// css rather than this one, or write your own styles.

.selectr {

    margin: 0;  
    display: inline-block;
    max-width: 100%;

    .panel-heading.no-title {
        padding: 0;
        border-bottom: none;
    }
    
    .panel-body {
        position: relative;
    }
    
    .clear-search {
        position: absolute;
        right: 25px;
        top: 12px;
        font-size: 28px;
        font-weight: bold;
        color: #333;
        opacity: 0.8;
        cursor: pointer;
        &:hover {
            opacity: 1;
        }
    }
    
    .list-group {
        overflow-x: hidden;

        .list-group-item {
            padding: 0 !important;
            overflow: hidden;
            cursor: pointer;

            &.selected {
                background-color: #4679bd;
                color: white;
                font-weight: 700;
                .add-remove {
                    transform: rotate(0deg);
                }
            }
        }
    }

    &.multi {
        .list-group-item:hover {
            .option-name {
                    margin-right: 50px;
                    border-right: 1px solid #ddd;
            }
            .selected {
                .option-name {
                    border-right: 1px solid white;
                }  
            }
            .add-remove {
                display: block;
            }
        }
    }
    
    .option-name {
        height: 100%;
        padding: 10px 15px;
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
    }
        
    .color-code {
        width: 20px;
        height: 20px;
        margin: 10px;
        margin-right: 0;
        float: left;
        border-radius: 5px;
        &.no-color {
            width: 0;
            height: 0;
            margin: 0;
        }
    }

    .add-remove {
...

CoffeeScript

do ($, window) ->

  class Selectr
        
    defaults:
      Selectr: this
      title:              'Select Options'
      placeholder:        'Search'
      resetText:          'Clear All'
      width:              '300px'
      maxListHeight:      '250px'
      tooltipBreakpoint:  25
      maxSelection:       NaN

    constructor: (@el, @args) ->
      @$el = $(el)
      @args = $.extend @defaults, @args, @$el.data('selectr-opts')
      @$el.data 'selectr-opts', @args
        
      @container = @CreateContainer()
      @opts = @PrepareOpts($('option', @el))
      $('.list-group', @container).append(@opts)
        
      selectedCount = 0
      for opt in @opts
        selectedCount++ if opt.hasClass 'selected'
      $('.current-selection', @container).text(selectedCount if selectedCount > 0)
        
      @MonitorSource()
      
      @container.insertAfter(@el)
      @$el.hide()        
        
    CreateContainer: ->
      $(document.createElement 'div').attr({
        'class': "selectr panel panel-default #{if @$el.prop 'multiple' then 'multi'}",
        'style': "width: #{@args.width};"
      }).html("""
        <div class='panel-heading #{if @args.title is "" then "no-title"}'>
          <h4 class='panel-title'>
            #{@args.title}
          </h4>
        </div>
        <div class='panel-body'>
          <input class='form-control' placeholder='#{@args.placeholder}'>
          <span class='clear-search hidden'>&times;</span>
        </div>
        <ul class='list-group' style='max-height: #{@args.maxListHeight}'>
        </ul>
        <div class='panel-footer #{'hidden' if not @$el.prop 'multiple'}'>
          <button class='reset btn btn-sm btn-default'>
            #{@args.resetText}
          </button>
          #{if @$el.prop 'multiple' then "<span class='current-selection badge'></span>"}
        </div>
        """)

    PrepareOpts: (opts) ->
      for opt in opts
        $(document.createElement 'li').attr({
          'class':...