思路:
控件封装首先应该考虑到兼容任何web框架,这样可以达到重复使用的效果。这里使用jquery方式来实现控件主体部分。
全局事件管理:
(function (global, $, factory) { global = factory.call(global, $); })(window, $, function ($) { var DUI = {}; window.DUI = window.$$ = DUI; window.$$.Event = function (props) { if (!(this instanceof $$.Event)) return new $$.Event(props); this.oldValue = null; this.newValue = null; this.element = null; this.params = {}; $.extend(this, props); }; window.$$.trigger = function (type, el, args) { var event = $.Event(type, args); el.trigger(event, args); }; })
jquery主体实现:
/* combobox UI */ var uuid = -1; var __Combobox__ = function (ops) { this.__eventNameSpave__ = ".combobox-event"; this.__ComboboxC__ = { root: $('body'), inputId: 'ui-combobox-input-', dorpdownId: 'ui-combobox-dropdown-', popupId: 'ui-combobox-popup-', placeHolderId: 'ui-combobox-placeholder-', listboxId: 'ui-combobox-listboxId-', items: [], popupOpend: false, oldValue: {}, newValue: {}, selectedIndex: -1, selectedItem: {} }; this.__delay__ = 200; this.__$Dom__ = {}; this.__filterSelectionResultIndex__ = []; this.__extends__(ops, this.__ComboboxC__); this.__element__ = this.__ComboboxC__.root; this.__initId__().__init__().__load__(); } __Combobox__.prototype = { __initId__: function () { this.__uuId = ++uuid; this.__ComboboxC__.inputId += uuid; this.__ComboboxC__.dorpdownId += uuid; this.__ComboboxC__.popupId += uuid; this.__ComboboxC__.placeHolderId += uuid; this.__ComboboxC__.listboxId += uuid; return this; }, __init__: function () { this.__element__.css('width', this.__ComboboxC__.width + 'px') .css('height', this.__ComboboxC__.height + 'px') .addClass('ui-combobox'); this.__createCombobox__() .__createDropDown__() .__createPopup__() .__initDom__() .__bindEvent__(); return this; }, __createCombobox__: function () { var h = -1, fragement = []; fragement[++h] = "<input id='" + this.__ComboboxC__.inputId + "' class="ui-combobox-input" />"; fragement[++h] = "<div class="ui-combobox-placeholder">"; fragement[++h] = "<div id='>" + this.__ComboboxC__.placeHolderId + "<' /div>"; fragement[++h] = "</div>"; this.__element__.append(fragement.join('')); return this; }, __createDropDown__: function () { var h = -1, fragement = []; fragement[++h] = "<div id=" + this.__ComboboxC__.dorpdownId + " class="ui-combobox-dropdown-container">"; fragement[++h] = "<div class="ui-dorpdown-icon"></div>"; fragement[++h] = "</div>"; this.__element__.append(fragement.join('')); return this; }, __createPopup__: function () { var h = -1, fragement = []; fragement[++h] = "<div class="ui-combobox-popup" id=" + this.__ComboboxC__.popupId + ">"; fragement[++h] = "<div class="ui-combobox-listbox">"; fragement[++h] = "<div class="ui-combobox-listbox-container" id=" + this.__ComboboxC__.listboxId + ">"; fragement[++h] = "</div>"; fragement[++h] = "</div>"; fragement[++h] = "</div>"; $('body').append(fragement.join('')); return this; }, __initDom__: function () { this.__$Dom__.$listbox = $('#' + this.__ComboboxC__.listboxId); this.__$Dom__.$input = $('#' + this.__ComboboxC__.inputId); this.__$Dom__.$popup = $('#' + this.__ComboboxC__.popupId); return this; }, __load__: function () { this.__seSelectionItems__().__setSelectionEvent__(); this.__$Dom__.$popup.hide(); }, setOptions: function (key, ops) { var self = this, clr = { setStyle: self.__setSytle__.bind(this), setItems: self.__setItems__.bind(this), setSelectedItem: self.__setSelectedItem__.bind(this) }; clr[key](ops); }, __setSytle__: function (ops) { this.__$Dom__.$popup.css('width', ops.popupWidth) .css('height', ops.popupHeight) .css('max-width', ops.popupMaxWidth) .css('max-height', ops.popupMaxHeight) return this; }, __setItems__: function (ops) { this.__ComboboxC__.items = ops.items; this.__seSelectionItems__() .__setSelectionEvent__() .__setSelectedItem__(ops.selectedItem); }, __seSelectionItems__: function () { var tempItems = this.__ComboboxC__.items; var i = 0, len = tempItems.length, fragement = []; this.__$Dom__.$listbox.empty(); for (; i < len; i++) { fragement.push("<div class="ui-combobox-selection-container">"); fragement.push("<div role='option' class="ui-combobox-selection" id=ui-combobox-selection-select-option-" + i + ">"); fragement.push(tempItems[i].name); fragement.push("</div>"); fragement.push("</div>"); } this.__$Dom__.$listbox.append(fragement.join('')); return this; }, __setSelectedItem__: function (selectedItem) { var self = this, clr = { object: function () { return selectedItem.name; }, string: function () { return selectedItem; }, undefined: function () { return self.__ComboboxC__.items[0]; } }, selectedText = ""; selectedText = clr[typeof selectedItem](); for (var i = 0; i < this.__ComboboxC__.items.length; i++) { if (this.__ComboboxC__.items[i].name == selectedText) { this.__ComboboxC__.selectedIndex = i; } } this.__$Dom__.$selections.removeClass('ui-combobox-selection-selected'); if (this.__ComboboxC__.selectedIndex != -1) { var inputValue = this.__ComboboxC__.items[this.__ComboboxC__.selectedIndex].name; this.__$Dom__.$input[0].value = inputValue; this.__$Dom__.$selections[this.__ComboboxC__.selectedIndex].classList.add('ui-combobox-selection-selected'); this.__ComboboxC__.newValue = this.__ComboboxC__.items[this.__ComboboxC__.selectedIndex]; } else { this.__$Dom__.$input[0].value = ''; this.__$Dom__.$selections[0].classList.add('ui-combobox-selection-selected'); this.__ComboboxC__.newValue = null; } this.__hide__(); return this; }, __bindEvent__: function () { var self = this; this.__$Dom__.$input.on('mousedown' + this.__eventNameSpave__, function (e) { self.__hideShow__(); }).on('focus' + this.__eventNameSpave__, function (e) { self.__element__.addClass('ui-combobox-focused'); }).on('blur' + this.__eventNameSpave__, function (e) { self.__element__.removeClass('ui-combobox-focused'); self.__hide__(); }).on('mouseover' + this.__eventNameSpave__, function () { self.__element__.addClass('ui-combobox-mouseover'); }).on('mouseleave' + this.__eventNameSpave__, function () { self.__element__.removeClass('ui-combobox-mouseover'); }).on('keyup', + this.__eventNameSpave__, this.__onInputkeyDown__.bind(this)); this.__debounceHandler__ = this.__debounce__(this.__filterSelection__.bind(this)); this.__$Dom__.$listbox.on('mousedown' + this.__eventNameSpave__, function (e) { e.stopPropagation(); e.preventDefault(); }); $('#' + this.__ComboboxC__.dorpdownId).on('click' + this.__eventNameSpave__, function (e) { self.__hideShow__(); }); }, __setSelectionEvent__: function () { var self = this; this.__$Dom__.$selections = $('#' + this.__ComboboxC__.listboxId + ' .ui-combobox-selection'); this.__$Dom__.$selections.on('click' + this.__eventNameSpave__, function (e) { var text = e.currentTarget.textContent; self.__setSelectedItem__(text); $$.trigger("selectionChanged", self.__element__, $$.Event({ element: self.__element__, oldValue: self.__ComboboxC__.oldValue, newValue: self.__ComboboxC__.newValue })) self.__ComboboxC__.oldValue = self.__ComboboxC__.newValue; }); return this; }, __onInputkeyDown__: function (e) { var self = e.data, keyCode = e.which; this.__debounceHandler__(e.target.value); }, __debounce__: function (fn) { var timer, self = this; return function (e) { clearTimeout(timer); timer = setTimeout(function () { fn.apply(self, [e]) }, self.__delay__); } }, __filterSelection__: function (e) { console.log(e); var condition = this.__getCondition__(); var i = 0, len = this.__ComboboxC__.items.length, items = this.__ComboboxC__.items; for (var i = 0; i < len; i++) { var name = this.__getInputValue__(items[i]); if (name.indexOf(condition) > -1) { this.__filterSelectionResultIndex__.push(i); } }; this.__updateFilteredSelections__(); this.__filterSelectionResultIndex__.length = 0; }, __getCondition__: function () { var value = ''; value = this.__$Dom__.$input.val().toLowerCase(); return value; }, __getInputValue__: function (item) { return item['name']; }, __updateFilteredSelections__: function () { var items = this.__ComboboxC__.items, i = 0, len = items.length; for (; i < len; i++) { if (this.__filterSelectionResultIndex__.includes(i)) { $(this.__$Dom__.$listbox.children()[i]).show(); } else { $(this.__$Dom__.$listbox.children()[i]).hide(); } } this.__show__(); }, __hideShow__: function () { this.__ComboboxC__.popupOpend = !this.__ComboboxC__.popupOpend; if (this.__ComboboxC__.popupOpend) this.__show__(); else this.__hide__(); }, __hide__: function () { this.__ComboboxC__.popupOpend = false; this.__$Dom__.$popup.hide(); }, __show__: function () { this.__ComboboxC__.popupOpend = true; this.__$Dom__.$popup.show(); this.__setPopupPosition__(); this.__$Dom__.$selections.removeClass('ui-combobox-selection-selected'); if (this.__filterSelectionResultIndex__.length == 0 && this.__$Dom__.$selections.length == this.__ComboboxC__.items.length) { if (this.__ComboboxC__.selectedIndex == -1) $(this.__$Dom__.$selections[0]).addClass('ui-combobox-selection-selected'); else $(this.__$Dom__.$selections[this.__ComboboxC__.selectedIndex]).addClass('ui-combobox-selection-selected'); return; } if (this.__filterSelectionResultIndex__.includes(this.__ComboboxC__.selectedIndex)) { $(this.__$Dom__.$selections[this.__ComboboxC__.selectedIndex]).addClass('ui-combobox-selection-selected'); return; } else { $(this.__$Dom__.$selections[this.__filterSelectionResultIndex__[0]]).addClass('ui-combobox-selection-selected'); return; } console.log('no select items.'); }, __setPopupPosition__: function () { var self = this; this.__$Dom__.$popup .css({ top: 0 }) .position({ my: "left top", at: "left bottom", of: self.__element__, collision: "flip" }) }, __extends__: function (ops, target) { for (var i in ops) { if (typeof ops[i] !== undefined) target[i] = ops[i]; } } }
react外层包装:
import ReactWidget from './react-widget'; class Combobox extends ReactWidget { constructor(props) { super(props); this.element = {}; this.state = { items: [] }; } componentWillReceiveProps(newPorps) { if (newPorps.items.length !== 0) this.element.setOptions('setItems', { items: newPorps.items }) if (newPorps.selectedItem != null) this.element.setOptions('setSelectedItem', newPorps.selectedItem) } componentDidMount() { this.element = new aui.Combobox({ root: $(ReactDOM.findDOMNode(this)), items: this.props.items }); $(ReactDOM.findDOMNode(this)).on('selectionChanged', this.selectionChangedHandler.bind(this)); } selectionChangedHandler(e, args) { this.props.selectionChanged(e, args); } render() { return <div></div> } } window.$$.Combobox = Combobox;
样式:
section { border-bottom: 1px solid #FF9900; padding: 5px; } /* icon */ .ui-dorpdown-icon { height: auto; width: 20px; &:after { content: '25BC' } } /* dialog */ button { border: 1px solid #9D9D9D; background-color: #fff; height: auto; padding: 5px 20px; border-radius: 5px; color: #9D9D9D; font-weight: inherit; font-style: inherit; font-family: inherit; cursor: pointer; &:hover { background: #9D9D9D; color: #fff; } } .ui-backdrop { POSITION: fixed; left: 0; top: 0; right: 0; bottom: 0; background-color: rgba(34, 34, 34, 0.5); z-index: 99; } .dialog { position: fixed; z-index: 100; top: 50%; left: 50%; right: 0; bottom: 0; border: 1px solid #ff9900; border-radius: 5px; text-align: center; background: #ffffff; .dialog-title { width: auto; height: 40px; line-height: 40px; background-color: #ff9900; font-weight: bold; color: #ffffff; } .dialog-content { padding: 10px; } .dialog-footer { position: absolute; bottom: 0px; background-color: #ff9900; width: 100%; height: auto; padding: 2px 0; } } /* banner */ .banner { position: relative; width: 100%; height: 400px; .banner-indicators { position: absolute; bottom: 20px; z-index: 10; width: 60%; padding-left: 0; list-style: none; text-align: center; width: 25%; left: 42%; .banner-indicators-item { list-style: none; float: left; width: 50px; padding: 5px 0; margin: 0 2px; opacity: 0.5; border: 0; background-color: #fff; height: auto; border-radius: 5px; margin-right: 50px; cursor: pointer; } .active { background-image: -webkit-linear-gradient(left, #9D9D9D, #000000 25%, #4F4F4F 50%, #000000 75%, #9D9D9D 100%); -webkit-text-fill-color: transparent; -webkit-animation: indicators-animation 2s infinite linear; } @-webkit-keyframes indicators-animation { 0% { background: #9D9D9D; border: 3px solid #ffffff; } 50% { background: #000000; border: 3px solid #9D9D9D; } 100% { background: #9D9D9D; border: 3px solid #ffffff; } } } .banner-inner { width: 100%; height: 100%; overflow: hidden; .banner-content { width: 300%; height: 100%; transform: translate3d(-66.66666666%, 0, 0); transition: transform 0.6s ease-in-out; -webkit-transition: -webkit-transform 0.6s ease-in-out; .banner-item { display: inline-block; position: relative; width: 33.333333333%; height: 100%; background-repeat: no-repeat; background-size: cover; &:nth-child(1) { background-image: url('./Image/banner0.jpg'); } &:nth-child(2) { background-image: url('./Image/banner1.jpg'); } &:nth-child(3) { background-image: url('./Image/banner2.jpg'); } .banner-item-inner { .title { visibility: hidden; transform: translate3d(-15%, 350%, 0); transition: transform 0.3s ease-in-out; -webkit-transition: -webkit-transform 0.3s ease-in-out; } .description { visibility: hidden; transform: translate3d(100%, 300%, 0); transition: transform 0.3s ease-in-out; -webkit-transition: -webkit-transform 0.3s ease-in-out; } } .active { .title { visibility: visible; transform: translate3d(30%, 350%, 0); } .description { visibility: visible; transform: translate3d(60%, 300%, 0); } } .banner-item-inner { p { font-size: 40px; font-weight: bold; color: #fff; font-style: italic; } } } } } } .ui-combobox { position: relative; width: 200px; min-width: 50px; height: 30px; line-height: 30px; background-color: #fff; border: 1px solid #bbb; outline: 0; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; .ui-combobox-input { position: absolute; width: 100%; height: 100%; margin: 0 auto; outline: 0; overflow: hidden; resize: none; border: none; background: #fff; cursor: default; padding: 1px 30px 1px 5px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } .ui-combobox-dropdown-container { position: absolute; height: 100%; right: 0; top: 0; border-left: 1px solid transparent; cursor: default; } .ui-combobox-placeholder { position: absolute; display: table; table-layout: fixed; height: 100%; top: 0; left: 5px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #aaa; } } .ui-combobox-focused { border-color: #49d; } .ui-combobox-mouseover { border-color: #49d; } .ui-combobox-popup-shown { border-color: #49d; } .ui-combobox-popup { position: absolute; width: 198px; height: auto; background-color: #ffffff; border: 1px solid #bbb; box-sizing: content-box; .ui-combobox-listbox { height: auto; max-height: 300px; min-height: 22px; list-style: none; overflow: auto; background-color: #ffffff; box-sizing: border-box; .ui-combobox-listbox-container { display: table; width: 100%; .ui-combobox-selection-container { display: table-row; &:hover { background-color: #F0F0F0; } .ui-combobox-selection { display: table-cell; border: 1px solid #ffffff; outline: 0; white-space: nowrap; padding: 13px 0 13px 5px; line-height: 1px; cursor: default; } .ui-combobox-selection-selected { background-color: #bbb; } } } } } /* loading */ .ui-loading { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 99999; display: block; text-align: center; &::before { content: ''; height: 100%; width: 0; vertical-align: middle; display: inline-block; } .ui-loading-dialog { position: relative; vertical-align: middle; display: inline-block; .ui-loading-circle { position: relative; height: 60px; width: 60px; border-radius: 50%; background-color: #9D9D9D; &::before { height: 4px; width: 100%; left: 0; top: 28px; z-index: 1; position: absolute; content: ""; background: #fff; } &::after { width: 4px; height: 100%; top: 0; right: 28px; position: absolute; content: ""; background: #ffffff; } .left { position: absolute; height: 100%; width: 50%; overflow: hidden; left: 0; } .right { position: absolute; overflow: hidden; height: 100%; width: 50%; left: 50%; } .center { position: absolute; top: 6px; left: 6px; height: 48px; width: 48px; background: #fff; border-radius: 50%; } .anim { position: absolute; left: 100%; top: 0; height: 100%; width: 100%; border-radius: 999px; background-color: #ff9900; -webkit-animation: aui-circle-loading-left 2s infinite; animation: aui-circle-loading-left 2s infinite; -webkit-transform-origin: 0 50% 0; transform-origin: 0 50% 0; -webkit-animation-duration: 2s; -webkit-animation-timing-function: linear; animation-duration: 2s; animation-timing-function: linear; } .right .anim { -webkit-animation-name: aui-circle-loading-right; animation-name: aui-circle-loading-right; } .left .anim { border-bottom-left-radius: 0; border-top-left-radius: 0; } .right .anim { border-bottom-right-radius: 0; border-top-right-radius: 0; left: -100%; -webkit-transform-origin: 100% 50% 0; transform-origin: 100% 50% 0; } } } } @keyframes aui-circle-loading-left { 0% { transform: rotate(0deg) } 25% { transform: rotate(0deg) } 50% { transform: rotate(180deg) } 75% { transform: rotate(180deg) } 100% { transform: rotate(360deg) } } @keyframes aui-circle-loading-right { 0% { transform: rotate(0deg) } 25% { transform: rotate(180deg) } 50% { transform: rotate(180deg) } 75% { transform: rotate(360deg) } 100% { transform: rotate(360deg) } }
效果: