reynish
3/6/2013 - 9:47 PM

A CodePen by Alex Reynish. fauxSelector - Change a <select> into a searchable list!

A CodePen by Alex Reynish. fauxSelector - Change a into a searchable list!

@import "compass";
@import "compass";

.wrapper {
  width: 50%;
  margin: 0 auto;
  padding: 2em;
}

.fauxSelector {
  @include transition-property(width);
}

.fs-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: none;
  border: 1px solid #000;
  &.active {
    display: block;
  }
  li {

  }
  a {
    display: block;
    padding: .25em;
  }
}

/*
 * Hide from both screenreaders and browsers: h5bp.com/u
 */

.hidden {
    display: none !important;
    visibility: hidden;
}
  
/*
 * Hide only visually, but have it available for screenreaders: h5bp.com/v
 */
.visuallyhidden {
    border: 0;
    clip: rect(0 0 0 0);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;
}

/*
 * Extends the .visuallyhidden class to allow the element to be focusable
 * when navigated to via the keyboard: h5bp.com/p
 */

.visuallyhidden.focusable:active,
.visuallyhidden.focusable:focus {
    clip: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    position: static;
    width: auto;
}

@mixin animation($value) {
    @include experimental(animation, $value,
        -moz, -webkit, -o, not -ms, not -khtml, official);
}

@mixin keyframes($name){
    @-webkit-keyframes #{$name} { @content; }
    @-moz-keyframes #{$name} { @content; }
    @-o-keyframes #{$name} { @content; }
    @keyframes #{$name} { @content; }
}

@include keyframes(fold) {
    0% {
        margin-bottom: -8px;
        visibility: visible;
        line-height: 0;
        @include transform-origin(0, 0, 0);
        @include transform(rotate3d(1, 0, 0, -90deg));
    }
    33.333% {
        margin-bottom: 4px;
        line-height: 16px;
        @include transform(rotate3d(1, 0, 0, 0deg));
    }
    66.667% {
        margin-bottom: 2px;
        @include transform(rotate3d(1, 0, 0, 25deg));
    }
    100% {
        margin-bottom: 0;
        visibility: visible;
        height: auto;
        @include transform-origin(0, 0, 0);
        @include transform(rotate3d(1, 0, 0, 0deg));
    }
}


body {
@include perspective(400);
}

.fs-list {
  @include animation(fold ease-in 500ms);
}
;(function ( $, window, document, undefined ) {

    // Create the defaults once
    var pluginName = "fauxSelector",
        defaults = {
            inputPlaceholder: "Type to search",
            inputWrapper: ".wrapper",
            inputAttrView: "viewing"
        };

    // The actual plugin constructor
    function Plugin( element, options ) {
        this.element = element;
        this.options = $.extend( {}, defaults, options );
        this._defaults = defaults;
        this._name = pluginName;
        this.init();
    }

    Plugin.prototype = {

        init: function() {
          var self = this;
          
          // Build elements to add to the DOM
          var input = '<input type="text" placeholder="' + self.options.inputPlaceholder + '" class="fs-input" />';
          var list = '<ul class="fs-list"></ul>';
          var stuff = input + list + "";

          // Add them
          $(stuff).insertAfter(this.element);
          
          // Build a list thing
          var optionsList = this.optionsIterate();
          var aList = this.buildList(optionsList);
          console.log();
          // Add it
          $(this.element).nextAll(".fs-list").html(aList.join(""));
          
          
          // Hide select
          $(this.element).removeClass("fs-init").addClass("visuallyhidden focusable fs-ready");
          
          // Set viewing to 0;
          self.viewing("init");
          
          // Setup the Input element
          var inputDOM = $(this.element).nextAll(".fs-input");
          inputDOM.on("focus blur keyup", function (event) {
            
            var el = $(this);
            
            var viewing = el.data(self.options.inputAttrView);
            
            var list = $(this).nextAll(".fs-list");
            
            if (event.type === "focus") {
              self.viewing("up");
              list.addClass("active");
            }
            
            if (event.type === "blur") {
              self.viewing("down");
            }
            
            if (event.type === "keyup") {
              var query = $(this).val();
              self.queryList(query);
            };
            
            
          });
          
          // Setup the list
            var listDOM = $(this.element).nextAll(".fs-list");
            listDOM
              .on("focusin mouseenter", function (event) {
                self.viewing("up");
              })
              .on("focusout mouseleave", function (event) {
                self.viewing("down");
              })
              .on("click", "a", function (event) {
                self.select($(event.target));
              });
          
        },
        
        // Name: optionsIterate
        // Desc: Iterates over the child <option> of this.element
        // Returns: Object of options
        optionsIterate: function() {
          var options = $(this.element).children("option");
          var listOptions = [];
          var numOfOpt = 0;
          options.each(function(){
            var listOption = {
              value: $(this).val(),
              text: $(this).text()
            } 
            listOptions.push(listOption);
          });
          return listOptions;
        },
        buildList: function(listOptions) {
          var list = [];
          for (var key in listOptions){
            var opt = listOptions[key];
            list.push('<li><a data-key="' + key + '" data-value="' + opt.value + '">' + opt.text + '</a></li>');
          };
          return list;
        },
        queryList: function(query) {
          var self = this;
          var query = query.toLowerCase();
          var list = $(this.element).nextAll(".fs-list");
              if (query.length >= 1) {
                $(self.element).trigger("fs-searchstart");
                var listAs = list.find("a");
                listAs.each(function() {
                  var text = $(this).text().toLowerCase()
                  if (!$(this).is(':contains("' + query + '")')) {
                    $(this).hide();
                  } else {
                    $(this).show();
                  };
                  //console.log($(this).is(':contains("' + query + '")'));
                });
              } else {
                list.find("a").show();
              };
              $(self.element).trigger("fs-searchend");
        },
      list: function(option){
        var list = $(this.element).nextAll(".fs-list");
        if(option === "hide"){
          list.removeClass("active");
        }
      },
      select: function(item) {
        var index = item.attr("data-key");
        var value = item.attr("data-value");
        var text = item.text();
        $(this.element).val(value).trigger("change");
        $(this.element).nextAll(".fs-input").attr("placeholder", text);
        
      },
        viewing: function(option) {
          var self = this;
          var wrapper = $(this.element).parents(self.options.inputWrapper);
          var viewing = wrapper.data(self.options.inputAttrView);
          
          if (option === "init") {
            wrapper.data(self.options.inputAttrView, 0);
          }
          
          if (option === "up" && viewing !== 2) {
            wrapper.data(self.options.inputAttrView, ++viewing);
          }
          
          if (option === "down" && viewing !== 0) {
            wrapper.data(self.options.inputAttrView, --viewing);
          }
  
          if (wrapper.data(self.options.inputAttrView) === 0) {
            self.list("hide");
          }
  
          console.log(wrapper.data(self.options.inputAttrView));
        }
    };
  
    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName, new Plugin( this, options ));
            }
        });
    };

})( jQuery, window, document );

$('.fauxSelector').fauxSelector();
$('.fauxSelector').on("change", function (event) {
  console.log($(this).val());
});
%h2 Something
%section.wrapper
  %h1 fauxSelector
  %select.fauxSelector
    %option{:value => "PS"} Please select
    %option{:value => "S"} Soemthing
    %option{:value => "SE"} Something else

%section.wrapper
  %h1 fauxSelector
  %select.fauxSelector
    %option{:value => "Blah"} Blah
    %option{:value => "Blog"} Blog
    %option{:value => "Dog"} Dog
    %option{:value => "Fog"} Fog
    %option{:value => "Bog"} Bog