1. 程式人生 > >自定義select控制元件開發

自定義select控制元件開發

目的:select下拉框條目太多(上百),當用戶選擇具體項時會浪費使用者很多時間去尋找,因此需要一個搜尋框讓使用者輸入關鍵字來匹配列表,便於使用者選擇

示例圖:

1、html結構

<div class="custom-select-container" data-name="oilBrand" data-default-value="品牌系列" data-placeholder="品牌系列">
    <textarea style="display: none;">
        [{"id": "1", "name": "1嘉實多級護全合成油SN級5W-30"}, {"id": "11", "name": "111嘉實多級護全合成油SN級5W-30"}, {"id": "2", "name": "2實多級護全合成油SN級5W-30"}, {"id": "3", "name": "3實多級護全合成油SN級5W-30"}, {"id": "4", "name": "4實多級護全合成油SN級5W-30"}, {"id": "5", "name": "5實多級護全合成油SN級5W-30"}, {"id": "6", "name": "6實多級護全合成油SN級5W-30"}]
    
</textarea> </div>

說明:

初始化容器屬性:
data-name: 相當於原始select的name
data-default-value: input文字搜尋框的初始化值
data-placeholder: input文字搜尋框的佔位值

textarea:

裡面是一個JONS字串,儲存著自定義select的鍵值對,注意裡面的id才是需要傳遞給後端介面的,而name只是顯示文字

2、實現原理

將使用者輸入的關鍵字用正則去匹配資料,展示匹配後的資料下拉列表,供使用者選擇

3、重要互動實現點

3.1、使用者點選(或滑鼠聚焦)搜尋框,需要顯示所有的資料下拉列表
3.2、使用者每次輸入文字,即當文字框值有改變時,匹配相應的資料列表並展示
3.3、當用戶點選了資料列表某一項時,即當用戶選擇了
3.4、當用戶在指定的列表項按下enter鍵時,即當用戶選擇了
3.5、當用戶滑鼠移動在資料下拉列表上時,可以通過鍵盤up,down上下鍵來選擇
3.6、當用戶選擇了列表項後,再次點選(或聚焦)搜尋框,需要展示所有資料列表,並高亮顯示所選擇的資料項
3.7、當用戶在搜尋框中用滑鼠貼上了關鍵字後,需要顯示匹配的資料列表並展示(此項較複雜,併兼容了ie7,8)

注:jQuery在處理paste事件時,event引數並沒有處理event.clipboardData,即為undefined,因此需要自己處理事件繫結(相容ie)

4、示例

<!DOCTYPE html>
<html>
<head>
    <script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.min.js"></script>
    <meta charset="utf-8">
    <title>custom select</
title> <style> * {margin: 0; padding: 0;} /*customSelect*/ .custom-select-container { width: 150px; position: relative; display: inline-block; vertical-align: top; margin-right: 5px; /*相容IE6, 7*/ *display: inline; *zoom: 1; margin: 100px 0 0 100px; } .custom-select-input { width: 120px; padding-right: 28px; height: 30px; line-height: 30px; font-size: 14px; text-indent: 5px; *margin-left: -5px; border: none 0; outline: none; } .custom-select-input-wrap { position: relative; width: 148px; height: 30px; overflow: hidden; border: 1px solid #aaa; } .list-toggle-trigger { position: absolute; right: 0; top: 0; padding: 10px; background-color: #fff; } .list-toggle-trigger i { display: block; width: 0; height: 0; border-width: 8px 5px 5px; border-style: solid; border-color: #aaa transparent transparent transparent; } .list-toggle-trigger.active { padding-top: 4px; } .list-toggle-trigger.active i { border-width: 5px 5px 8px; border-color: transparent transparent #aaa transparent; } .custom-select-list { min-width: 120px; max-height: 400px; overflow-y: auto; border: 1px solid #006ed5; position: absolute; left: 0; top: 32px; z-index: 100; background-color: #FFF; display: none; } .custom-select-list span { display: block; height: 24px; line-height: 24px; color: #000; text-indent: 5px; white-space: nowrap; /*padding-right: 25px;*/ } .custom-select-list span.hover { color: #FFF; background-color: #006ed5; cursor: default; } </style> </head> <body> <div class="custom-select-container" data-name="oilBrand" data-default-value="品牌系列" data-placeholder="品牌系列"> <textarea style="display: none;"> [{"id": "1", "name": "1嘉實多級護全合成油SN級5W-30"}, {"id": "11", "name": "111嘉實多級護全合成油SN級5W-30"}, {"id": "2", "name": "2實多級護全合成油SN級5W-30"}, {"id": "3", "name": "3實多級護全合成油SN級5W-30"}, {"id": "4", "name": "4實多級護全合成油SN級5W-30"}, {"id": "5", "name": "5實多級護全合成油SN級5W-30"}, {"id": "6", "name": "6實多級護全合成油SN級5W-30"}] </textarea> </div> <script> (function($){ var jsonParse = window.JSON && JSON.parse ? JSON.parse : eval; var addEvent; if (document.body.addEventListener) { addEvent = function(elem, type, eventHandler) { elem.addEventListener(type, eventHandler); }; } else if (document.body.attachEvent) { addEvent = function(elem, type, eventHandler) { elem.attachEvent('on' + type, eventHandler); }; } else { addEvent = function(elem, type, eventHandler) { elem['on' + type] = eventHandler; }; } /** * author: yangjunhua * email: [email protected] * constructor: * CustomSelect * params: * options = { * container: selector, // init container * change: function(value) {} // it means select change handler * } * example: * html: * <div class="custom-select-container" data-name="carBrand" data-default-value="品牌系列" data-placeholder="品牌系列"> * <textarea style="display: none;">[{"id": "1", "name": "寶馬"}, {"id": "2", "name": "奧迪"}]</textarea> * </div> * <div class="custom-select-container" data-name="carPrice" data-default-value="價格區間" data-placeholder="價格區間"> * <textarea style="display: none;">[{"id": "1", "name": "30-100萬"}, {"id": "2", "name": "100-300萬"}]</textarea> * </div> * js: * $('.custom-select-container').each(function() { * new CustomSelect({ * container: this, * change: function(value) { * // value it means id * // query data ... * } * }); * }); * */ function CustomSelect(options) { this.options = $.extend({}, options || {}); this.init(); } // 原型 CustomSelect.prototype = { constructor: CustomSelect, keywords: '', init: function() { if (!this.options || !this.options.container) return; this.initContainer(); this.listenFocus(); this.listenBlur(); this.listenSearch(); this.listenTrigger(); this.listenSelect(); this.listenMouseenter(); this.listenBodyClick(); this.listenPaste(); }, initContainer: function() { this.$container = $(this.options.container).addClass('custom-select-container'); var tpl = '<div class="custom-select-input-wrap">' + '<input type="text" class="custom-select-input" value="' + (this.$container.data('default-value')) + '" placeholder="' + (this.$container.data('placeholder')) + '">' + '<div class="list-toggle-trigger"><i></i></div>' + '</div>' + '<div class="custom-select-list"></div>'; this.dataList = jsonParse(this.$container.find('textarea')[0].value); this.$container.html(tpl); this.$input = this.$container.find('.custom-select-input'); this.$list = this.$container.find('.custom-select-list'); this.$filterList = $(); this.$trigger = this.$container.find('.list-toggle-trigger'); this.defaltValue = this.$container.data('default-value'); this.$container.data({ 'customSelect': this, 'value': '' }); }, _isRended: false, _isResetSize: false, _highlightIndex: -1, _seletedIndex: -1, highlight: function(idx) { idx = idx !== undefined && idx > -1 ? idx : this._highlightIndex; idx >= 0 && this.$filterList.children().removeClass('hover').eq(idx).addClass('hover'); }, renderList: function(list) { var listTpl = '', len = list.length; if (len > 0) { for (var i = 0; i < len; i++) { listTpl += '<span data-value="' + list[i].id + '">' + list[i].name + '</span>'; } this.$list.html(listTpl).slideDown('fast'); } else { this.$list.html(listTpl).hide(); } this.filterDataList = list; this._isRended = true; if (!this._isResetSize) { this._isResetSize = true; this.$list.css({ width: this.$list[0].scrollWidth + 25 + 'px' }); } }, search: function() { if (this.keywords === '' || this.keywords === this.defaltValue) { this.$input.val(''); this.renderList(this.dataList); this.$filterList = this.$list; return; } var searchList = []; var len = this.dataList.length; var reg = new RegExp(this.keywords, 'i'); for (var i = 0; i < len; i++) { var dataItem = this.dataList[i]; dataItem.name.match(reg) && (searchList.push(dataItem)); this.$filterList = this.$filterList.add(this.$list.eq(i)); } this.renderList(searchList); }, listenFocus: function() { var self = this; this.$input.on('focus', function() { if (self._isRended && self.filterDataList.length > 0) { self.highlight(self._seletedIndex); self.$list.slideDown('fast'); self.keywords === '' && self.$input.val(''); return; } self.search(); }); }, listenBlur: function() { var self = this; this.$input.on('blur', function() { if (self.filterDataList.length === 0) { self.$input.val(self.defaltValue); self.keywords = ''