<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="style.css">
</head>
<body>
<style>
body {
padding: 100px; text-align:center;
background: linear-gradient(90deg,#03A9F4, #4CAF50);
font-family: sans-serif;
}
</style>
<h3>Multiple Option Selector</h3>
<input type="text" id="justAnInputBox" placeholder="Select"/>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<script type="text/javascript">
var SampleJSONData = [
{id: 1, title: 'demo1'},
{id: 2, title: 'demo2'},
{id: 3, title: 'demo3'},
{id: 4, title: 'demo4'},
{id: 5, title: 'demo5'}
];
var comboTree1, comboTree2;
jQuery(document).ready(function($) {
comboTree1 = $('#justAnInputBox').comboTree({
source : SampleJSONData,
isMultiple: true
});
comboTree2 = $('#justAnotherInputBox').comboTree({
source : SampleJSONData,
isMultiple: false
});
});
</script>
</body>
</html>
.comboTreeWrapper{
position: relative;
text-align: left !important;
}
.comboTreeInputWrapper{
position: relative;
}
.comboTreeArrowBtn {
position: absolute;
right: 1px;
bottom: 1px;
top: 1px;
box-sizing: border-box;
border: none;
border-left: 1px solid #c7c7c7;
border-radius: 0 3px 3px 0;
}
.comboTreeDropDownContainer {
display: none;
background: #fff;
border: 1px solid #aaa;
max-height: 250px;
overflow-y: auto;
position: absolute;
width: 100%;
box-sizing: border-box;
}
.comboTreeDropDownContainer ul{
padding: 0px;
margin: 0;
}
.comboTreeDropDownContainer li{
list-style-type: none;
padding-left: 5px;
cursor: pointer;
}
.comboTreeDropDownContainer li:hover{
background-color: #ddd;}
.comboTreeDropDownContainer li:hover ul{
background-color: #fff;}
.comboTreeDropDownContainer li span.comboTreeItemTitle.comboTreeItemHover{
background-color: #418EFF;
color: #fff;}
span.comboTreeItemTitle{
display: block;
padding: 2px 4px;
}
.comboTreeDropDownContainer label{
cursor: pointer;
width: 100%;
display: block;
}
.comboTreeDropDownContainer .comboTreeItemTitle input {
position: relative;
top: 2px;
margin: 0px 4px 0px 0px;
}
.comboTreeParentPlus{
position: relative;
left: -12px;
top: 4px;
width: 4px;
float: left;
}
.comboTreeInputBox {
padding: 7px;
border-radius: 3px;
border: none;
width: 100%;
box-sizing: border-box;
padding-right: 24px;
height: 40px;
white-space: nowrap;
text-overflow: ellipsis;
}
.comboTreeArrowBtnImg{
font-size: 10px;
}
.comboTreeWrapper {
display: inline-block!important;
}
/*!
* jQuery ComboTree Plugin
* Author: Erhan FIRAT
* Mail: erhanfirat@gmail.com
* Licensed under the MIT license
*/
;(function ( $, window, document, undefined ) {
// Create the defaults once
var comboTreePlugin = 'comboTree',
defaults = {
source: [],
isMultiple: false
};
// The actual plugin constructor
function ComboTree( element, options ) {
this.elemInput = element;
this._elemInput = $(element);
this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = comboTreePlugin;
this.init();
}
ComboTree.prototype.init = function () {
// Setting Doms
this.comboTreeId = 'comboTree' + Math.floor(Math.random() * 999999);
this._elemInput.addClass('comboTreeInputBox');
if(this._elemInput.attr('id') === undefined)
this._elemInput.attr('id', this.comboTreeId + 'Input');
this.elemInputId = this._elemInput.attr('id');
this._elemInput.wrap('<div id="'+ this.comboTreeId + 'Wrapper" class="comboTreeWrapper"></div>');
this._elemInput.wrap('<div id="'+ this.comboTreeId + 'InputWrapper" class="comboTreeInputWrapper"></div>');
this._elemWrapper = $('#' + this.comboTreeId + 'Wrapper');
this._elemArrowBtn = $('<button id="' + this.comboTreeId + 'ArrowBtn" class="comboTreeArrowBtn"><span class="comboTreeArrowBtnImg">▼</span></button>');
this._elemInput.after(this._elemArrowBtn);
this._elemWrapper.append('<div id="' + this.comboTreeId + 'DropDownContainer" class="comboTreeDropDownContainer"><div class="comboTreeDropDownContent"></div>');
// DORP DOWN AREA
this._elemDropDownContainer = $('#' + this.comboTreeId + 'DropDownContainer');
this._elemDropDownContainer.html(this.createSourceHTML());
this._elemItems = this._elemDropDownContainer.find('li');
this._elemItemsTitle = this._elemDropDownContainer.find('span.comboTreeItemTitle');
// VARIABLES
this._selectedItem = {};
this._selectedItems = [];
this.bindings();
};
// *********************************
// SOURCES CODES
// *********************************
ComboTree.prototype.removeSourceHTML = function () {
this._elemDropDownContainer.html('');
};
ComboTree.prototype.createSourceHTML = function () {
var htmlText = this.createSourceSubItemsHTML(this.options.source);
return htmlText;
};
ComboTree.prototype.createSourceSubItemsHTML = function (subItems) {
var subItemsHtml = '<UL>';
for (var i=0; i<subItems.length; i++){
subItemsHtml += this.createSourceItemHTML(subItems[i]);
}
subItemsHtml += '</UL>'
return subItemsHtml;
}
ComboTree.prototype.createSourceItemHTML = function (sourceItem) {
var itemHtml = "",
isThereSubs = sourceItem.hasOwnProperty("subs");
itemHtml = '<LI class="ComboTreeItem' + (isThereSubs?'Parent':'Chlid') + '"> ';
if (isThereSubs)
itemHtml += '<span class="comboTreeParentPlus">−</span>';
if (this.options.isMultiple)
itemHtml += '<span data-id="' + sourceItem.id + '" class="comboTreeItemTitle"><input type="checkbox">' + sourceItem.title + '</span>';
else
itemHtml += '<span data-id="' + sourceItem.id + '" class="comboTreeItemTitle">' + sourceItem.title + '</span>';
if (isThereSubs)
itemHtml += this.createSourceSubItemsHTML(sourceItem.subs);
itemHtml += '</LI>';
return itemHtml;
};
// BINDINGS
// *****************************
ComboTree.prototype.bindings = function () {
var _this = this;
this._elemArrowBtn.on('click', function(e){
e.stopPropagation();
_this.toggleDropDown();
});
this._elemInput.on('click', function(e){
e.stopPropagation();
if (!_this._elemDropDownContainer.is(':visible'))
_this.toggleDropDown();
});
this._elemItems.on('click', function(e){
e.stopPropagation();
if ($(this).hasClass('ComboTreeItemParent')){
_this.toggleSelectionTree(this);
}
});
this._elemItemsTitle.on('click', function(e){
e.stopPropagation();
if (_this.options.isMultiple)
_this.multiItemClick(this);
else
_this.singleItemClick(this);
});
this._elemItemsTitle.on("mousemove", function (e) {
e.stopPropagation();
_this.dropDownMenuHover(this);
});
// KEY BINDINGS
this._elemInput.on('keyup', function(e) {
e.stopPropagation();
switch (e.keyCode) {
case 27:
_this.closeDropDownMenu(); break;
case 13:
case 39: case 37: case 40: case 38:
e.preventDefault();
break;
default:
if (!_this.options.isMultiple)
_this.filterDropDownMenu();
break;
}
});
this._elemInput.on('keydown', function(e) {
e.stopPropagation();
switch (e.keyCode) {
case 9:
_this.closeDropDownMenu(); break;
case 40: case 38:
e.preventDefault();
_this.dropDownInputKeyControl(e.keyCode - 39); break;
case 37: case 39:
e.preventDefault();
_this.dropDownInputKeyToggleTreeControl(e.keyCode - 38);
break;
case 13:
if (_this.options.isMultiple)
_this.multiItemClick(_this._elemHoveredItem);
else
_this.singleItemClick(_this._elemHoveredItem);
e.preventDefault();
break;
default:
if (_this.options.isMultiple)
e.preventDefault();
}
});
// ON FOCUS OUT CLOSE DROPDOWN
$(document).on('mouseup.' + _this.comboTreeId, function (e){
if (!_this._elemWrapper.is(e.target) && _this._elemWrapper.has(e.target).length === 0 && _this._elemDropDownContainer.is(':visible'))
_this.closeDropDownMenu();
});
};
// EVENTS HERE
// ****************************
// DropDown Menu Open/Close
ComboTree.prototype.toggleDropDown = function () {
this._elemDropDownContainer.slideToggle(50);
this._elemInput.focus();
};
ComboTree.prototype.closeDropDownMenu = function () {
this._elemDropDownContainer.slideUp(50);
};
// Selection Tree Open/Close
ComboTree.prototype.toggleSelectionTree = function (item, direction) {
var subMenu = $(item).children('ul')[0];
if (direction === undefined){
if ($(subMenu).is(':visible'))
$(item).children('span.comboTreeParentPlus').html("+");
else
$(item).children('span.comboTreeParentPlus').html("−");
$(subMenu).slideToggle(50);
}
else if (direction == 1 && !$(subMenu).is(':visible')){
$(item).children('span.comboTreeParentPlus').html("−");
$(subMenu).slideDown(50);
}
else if (direction == -1){
if ($(subMenu).is(':visible')){
$(item).children('span.comboTreeParentPlus').html("+");
$(subMenu).slideUp(50);
}
else {
this.dropDownMenuHoverToParentItem(item);
}
}
};
// SELECTION FUNCTIONS
// *****************************
ComboTree.prototype.singleItemClick = function (ctItem) {
this._selectedItem = {
id: $(ctItem).attr("data-id"),
title: $(ctItem).text()
};
this.refreshInputVal();
this.closeDropDownMenu();
};
ComboTree.prototype.multiItemClick = function (ctItem) {
this._selectedItem = {
id: $(ctItem).attr("data-id"),
title: $(ctItem).text()
};
var index = this.isItemInArray(this._selectedItem, this._selectedItems);
if (index){
this._selectedItems.splice(parseInt(index), 1);
$(ctItem).find("input").prop('checked', false);
}
else {
this._selectedItems.push(this._selectedItem);
$(ctItem).find("input").prop('checked', true);
}
this.refreshInputVal();
};
ComboTree.prototype.isItemInArray = function (item, arr) {
for (var i=0; i<arr.length; i++)
if (item.id == arr[i].id && item.title == arr[i].title)
return i + "";
return false;
}
ComboTree.prototype.refreshInputVal = function () {
var tmpTitle = "";
if (this.options.isMultiple) {
for (var i=0; i<this._selectedItems.length; i++){
tmpTitle += this._selectedItems[i].title;
if (i<this._selectedItems.length-1)
tmpTitle += ", ";
}
}
else {
tmpTitle = this._selectedItem.title;
}
this._elemInput.val(tmpTitle);
}
ComboTree.prototype.dropDownMenuHover = function (itemSpan, withScroll) {
this._elemItems.find('span.comboTreeItemHover').removeClass('comboTreeItemHover');
$(itemSpan).addClass('comboTreeItemHover');
this._elemHoveredItem = $(itemSpan);
if (withScroll)
this.dropDownScrollToHoveredItem(this._elemHoveredItem);
}
ComboTree.prototype.dropDownScrollToHoveredItem = function (itemSpan) {
var curScroll = this._elemDropDownContainer.scrollTop();
this._elemDropDownContainer.scrollTop(curScroll + $(itemSpan).parent().position().top - 80);
}
ComboTree.prototype.dropDownMenuHoverToParentItem = function (item) {
var parentSpanItem = $($(item).parents('li.ComboTreeItemParent')[0]).children("span.comboTreeItemTitle");
if (parentSpanItem.length)
this.dropDownMenuHover(parentSpanItem, true);
else
this.dropDownMenuHover(this._elemItemsTitle[0], true);
}
ComboTree.prototype.dropDownInputKeyToggleTreeControl = function (direction) {
var item = this._elemHoveredItem;
if ($(item).parent('li').hasClass('ComboTreeItemParent'))
this.toggleSelectionTree($(item).parent('li'), direction);
else if (direction == -1)
this.dropDownMenuHoverToParentItem(item);
}
ComboTree.prototype.dropDownInputKeyControl = function (step) {
if (!this._elemDropDownContainer.is(":visible"))
this.toggleDropDown();
var list = this._elemItems.find("span.comboTreeItemTitle:visible");
i = this._elemHoveredItem?list.index(this._elemHoveredItem) + step:0;
i = (list.length + i) % list.length;
this.dropDownMenuHover(list[i], true);
},
ComboTree.prototype.filterDropDownMenu = function () {
var searchText = this._elemInput.val();
if (searchText != ""){
this._elemItemsTitle.hide();
this._elemItemsTitle.siblings("span.comboTreeParentPlus").hide();
list = this._elemItems.find("span:icontains('" + this._elemInput.val() + "')").each(function (i, elem) {
$(this).show();
$(this).siblings("span.comboTreeParentPlus").show();
});
}
else{
this._elemItemsTitle.show();
this._elemItemsTitle.siblings("span.comboTreeParentPlus").show();
}
}
// Retuns Array (multiple), Integer (single), or False (No choice)
ComboTree.prototype.getSelectedItemsId = function () {
if (this.options.isMultiple && this._selectedItems.length>0){
var tmpArr = [];
for (i=0; i<this._selectedItems.length; i++)
tmpArr.push(this._selectedItems[i].id);
return tmpArr;
}
else if (!this.options.isMultiple && this._selectedItem.hasOwnProperty('id')){
return this._selectedItem.id;
}
return false;
}
// Retuns Array (multiple), Integer (single), or False (No choice)
ComboTree.prototype.getSelectedItemsTitle = function () {
if (this.options.isMultiple && this._selectedItems.length>0){
var tmpArr = [];
for (i=0; i<this._selectedItems.length; i++)
tmpArr.push(this._selectedItems[i].title);
return tmpArr;
}
else if (!this.options.isMultiple && this._selectedItem.hasOwnProperty('id')){
return this._selectedItem.title;
}
return false;
}
ComboTree.prototype.unbind = function () {
this._elemArrowBtn.off('click');
this._elemInput.off('click');
this._elemItems.off('click');
this._elemItemsTitle.off('click');
this._elemItemsTitle.off("mousemove");
this._elemInput.off('keyup');
this._elemInput.off('keydown');
this._elemInput.off('mouseup.' + this.comboTreeId);
$(document).off('mouseup.' + this.comboTreeId);
}
ComboTree.prototype.destroy = function () {
this.unbind();
this._elemWrapper.before(this._elemInput);
this._elemWrapper.remove();
this._elemInput.removeData('plugin_' + comboTreePlugin);
}
$.fn[comboTreePlugin] = function ( options) {
var ctArr = [];
this.each(function () {
if (!$.data(this, 'plugin_' + comboTreePlugin)) {
$.data(this, 'plugin_' + comboTreePlugin, new ComboTree( this, options));
ctArr.push($(this).data()['plugin_' + comboTreePlugin]);
}
});
if (this.length == 1)
return ctArr[0];
else
return ctArr;
}
})( jQuery, window, document );
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.