UNPKG

lu2

Version:

Simple and flexible UI component library based on native HTML and JavaScript

1,161 lines (985 loc) 40.7 kB
/** * @Datalist.js * @author zhangxinxu * @version * Created: 16-03-28 * @description 多功能下拉数据列表 **/ /* global module */ (function (global, factory) { if (typeof exports === 'object' && typeof module !== 'undefined') { global.Follow = require('./Follow'); module.exports = factory(); } else if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Datalist = factory(); } // eslint-disable-next-line }((typeof global !== 'undefined') ? global // eslint-disable-next-line : ((typeof window !== 'undefined') ? window : ((typeof self !== 'undefined') ? self : this)), function (require) { var Follow = (this || self).Follow; if (typeof require == 'function' && !Follow) { Follow = require('common/ui/Follow'); } else if (!Follow) { window.console.error('need Follow.js'); return {}; } /** * 数据下拉列表 * 类似于传统Autocomplete功能 * 仿HTML5原生datalist功能实现 * 支持静态呈现(基于现有HTML构建)和动态获取(本地存储或ajax请求) */ // 常量变量 var DATALIST = 'datalist'; var SELECTED = 'selected'; var DISABLED = 'disabled'; var ACTIVE = 'active'; var REVERSE = 'reverse'; // 样式类名统一处理 var CL = { add: function () { return ['ui', DATALIST].concat([].slice.call(arguments)).join('-'); }, toString: function () { return 'ui-' + DATALIST; } }; var objEventType = { start: 'mousedown', move: 'mousemove', end: 'mouseup' }; if ('ontouchstart' in document) { objEventType = { start: 'touchstart', move: 'touchmove', end: 'touchend' }; } /** * 过滤HTML标签的方法 * @param {String} str 需要过滤的HTML字符串 * @return {String} 返回过滤后的HTML字符串 */ var funStripHTML = function (str) { if (typeof str == 'string') { return str.replace(/<\/?[^>]*>/g, ''); } return ''; }; /** * 转义HTML标签的方法 * @param {String} str 需要转义的HTML字符串 * @return {String} 转义后的字符串 */ var funEncodeHTML = function (str) { if (typeof str == 'string') { return str.replace(/<|&|>/g, function (matches) { return ({ '<': '&lt;', '>': '&gt;', '&': '&amp;' })[matches]; }); } return ''; }; /** * 反转义HTML标签的方法 * @param {String} str 需要反转义的字符串 * @return {String} 反转义后的字符串 */ var funDecodeHTML = function (str) { if (typeof str == 'string') { return str.replace(/&lt;|&gt;|&amp;/g, function (matches) { return ({ '&lt;': '<', '&gt;': '>', '&amp;': '&' })[matches]; }); } return ''; }; /* * 支持jQuery API调用和模块化调用两种 * @example * new Datalist(element) */ /** * Datalist实例方法 * @param {Object|Element} element 输入框元素DOM对象或者选择器 * @param {Object} options 可选参数 */ var Datalist = function (element, options) { if (typeof element == 'string') { element = document.querySelector(element); } if (!element) { window.console.error('Datalist实例方法参数缺失'); return; } // el一般特指输入框 if (element.nodeType != 1) { window.console.error('element参数不合法'); return; } // 分3种情况 // 1. list属性静态获取 // 2. 根据name值本地获取 // 3. options的data参数获取 var defaults = { // 列表数据,'auto'表示取自本地存储,还支持数组和函数类型和对象 data: 'auto', // 最多出现的匹配条目数,如果数目不限,使用'auto'关键字 // 支持从element元素的results属性获取 max: 8, // 下拉列表的宽度,默认和输入框宽度一致,支持数值和function width: 'auto', // 显示容器 container: document.body, // 输入框点击时候的交互方式,是否变成placeholder状态 // 默认值是'auto',还支持true/和false placeholder: 'auto', // 对总数据的过滤方法,默认前序匹配 filter: function (data, value) { // this是当前实例对象 var arr = []; // 默认是从头到尾完全字符匹配 if (data.forEach) { data.forEach(function (obj) { if (value == '' || obj.value.indexOf(value) == 0) { arr.push(obj); } }); } return arr; }, // 选中某一项的回调 // 点击和回车,上下键不会触发此事件 onSelect: function () {}, // 显示和隐藏时候的回调 // this为实例对象,支持两个参数, // 分别是trigger(输入框)和target(生成列表)元素 onShow: function () {}, onHide: function () {} }; options = options || {}; // 元素 var eleInput = element; if (eleInput.data && eleInput.data.datalist) { return eleInput.data.datalis; } // max是否通过原生的results属性设置 var strResults = eleInput.getAttribute('results'); var numResults = Number(strResults); if (strResults) { options.max = numResults; } var objParams = Object.assign({}, defaults, options); var eleContainer = objParams.container; if (eleContainer == 'string') { eleContainer = document.querySelector(eleContainer); } // 暴露的数据 this.element = { input: eleInput, trigger: eleInput, form: eleInput.closest('form'), container: eleContainer }; this.callback = { filter: objParams.filter, select: objParams.onSelect, show: objParams.onShow, hide: objParams.onHide }; this.params = { width: objParams.width, max: objParams.max, data: objParams.data, index: -1 }; // 记住输入框默认的placeholder值 var strAttrPlaceholder = eleInput.getAttribute('placeholder'); if (strAttrPlaceholder) { this.params.placeholder = strAttrPlaceholder; } // 下拉列表是否显示的标志量 this.display = false; // 占位符交互标示量 if (objParams.placeholder == 'auto') { this.placeholder = true; } // 输入框的name属性值 var strAttrName = eleInput.name; // 从本地自动获得数据 if (objParams.data == 'auto') { var strAttrList = eleInput.getAttribute('list'); var strAttrAutocomplete = eleInput.getAttribute('autocomplete'); if (strAttrList) { // 走<datalist>元素获取数据 var eleDatalist = document.getElementById(strAttrList); if (!eleDatalist) { return; } // 暴露在外 this.element.datalist = eleDatalist; // 去掉浏览器原生的行为 eleInput.removeAttribute('list'); // 数据实时从<datalist>元素获取 this.callback.data = function () { return [].slice.call(eleDatalist.querySelectorAll('option')).map(function (eleOption) { var objAttr = {}; [].slice.call(eleOption.attributes).forEach(function (objNameValue) { objAttr[objNameValue.name] = objNameValue.value; }); // value和label是必须的 // 降低filter中出错概率 objAttr.value = objAttr.value || ''; objAttr.label = objAttr.label || ''; return objAttr; }); }; } else if (strAttrName && strAttrAutocomplete != 'off') { // 走本地存储获取数据,模拟浏览器autocomplete效果 // 跟随浏览器autocomplete行为规范实现 // 数据从本地localStorage实时获取 this.callback.data = function () { var data = []; // IE7忽略自动记忆功能 // 本地获取 var strList = localStorage[DATALIST + '-' + strAttrName]; if (strList) { strList.split(',').forEach(function (value) { // value必须 if (value) { data.push({ label: '', value: value }); } }); } return data; }; this.params.twice = true; // autocomplete交互占位符不参与 this.placeholder = false; // 表单记忆 if (this.element.form) { this.element.form.addEventListener('submit', function () { this.store(); }.bind(this)); } } else { this.callback.data = function () { return []; }; } } else if (objParams.data instanceof Array) { // 2. 如果直接data参数 this.callback.data = function () { return objParams.data; }; } else if (typeof objParams.data == 'function') { // 3. 如果是函数 this.callback.data = objParams.data; } else if (typeof objParams.data == 'object' && objParams.data.url) { // 4. 如果是ajax参数对象 // 更改filter内置过滤处理(如果用户并没有自定义的话) if (!options.filter) { this.callback.filter = function (data) { return data; }; } var timerAjaxDatalist = null; var objDatalist = this; this.callback.data = function () { // 清除延时,避免每次输入都请求 clearTimeout(timerAjaxDatalist); if (!strAttrName) { strAttrName = 'k'; } // 没有值的时候清空数据 // 不请求 var strValue = eleInput.value.trim(); if (strValue == '') { objDatalist.data = []; return []; } var objAjaxParams = new URLSearchParams(objParams.data.data); // 加入输入数据 objAjaxParams.append(strAttrName, strValue); // URL处理 var strUrlAjax = objParams.data.url.split('#')[0]; // URL拼接 if (strUrlAjax.split('?').length > 1) { strUrlAjax = strUrlAjax + '&' + objAjaxParams.toString(); } else { strUrlAjax = strUrlAjax + '?' + objAjaxParams.toString(); } // 有2个参数有内置,需要合并 // 1是搜索查询参数 // 2是成功后的回调 var funAjax = function () { var xhr = new XMLHttpRequest(); xhr.open('GET', strUrlAjax); xhr.onload = function () { var json = {}; try { json = JSON.parse(xhr.responseText); if (json && json.data) { objDatalist.refresh(objDatalist.callback.filter.call(objDatalist, json.data)); // 成功回调 if (objParams.data.success) { objParams.data.success(json); } } else if (objParams.data.error) { objParams.data.error(json); } } catch (event) { if (objParams.data.error) { objParams.data.error(event); } } }; xhr.onloadend = function () { // 清除输入框忙碌状态 eleInput.removeAttribute('aria-busy'); }; xhr.onerror = function (event) { if (objParams.data.error) { objParams.data.error(event); } }; xhr.send(); // 标记输入框忙碌状态 eleInput.setAttribute('aria-busy', 'true'); }; // 请求保护,200毫秒延迟判断 timerAjaxDatalist = setTimeout(funAjax, 200); }; // autocomplete交互占位符不参与 this.placeholder = false; } // 务必灭了浏览器内置的autocomplete if (eleInput.form) { eleInput.setAttribute('autocomplete', 'off'); } else { eleInput.setAttribute('autocomplete', 'new-password'); } // 上面的数据方法准备完毕,下面事件 this.events(); // 暴露 // 存储 if (!eleInput.data) { eleInput.data = {}; } eleInput.data.datalist = this; }; /** * 本地存储输入框的值 * @return {Object} 返回当前实例对象 */ Datalist.prototype.store = function () { // 元素 var eleInput = this.element.input; // 元素属性值 var strValue = this.value(); var strName = eleInput.name; // 只有有值的时候才本地记忆 if (strValue && strName) { // 本地获取 var arrList = (localStorage[DATALIST + '-' + strName] || '').split(','); // 如果当前数据并未保存过 var numIndexMatch = arrList.indexOf(strValue); if (numIndexMatch == -1) { // 新值,前面插入 arrList.unshift(strValue); } else if (numIndexMatch != 0) { // 如果当前匹配内容不是首位,顺序提前 // 把需要提前的数组弄出来 var arrSplice = arrList.splice(numIndexMatch, 1); // 重新连接 arrList = arrSplice.concat(arrList); } // 更改对应的本地存储值 localStorage[DATALIST + '-' + strName] = arrList.join(); } return this; }; /** * 清除本地存储的值 * @param {String} value value参数存在3种逻辑,具体参见方法内注释 * @return {Object} 返回当前实例对象 */ Datalist.prototype.removeStore = function (value) { // value参数存在下面3种逻辑 // 1. 字符串内容,表示移除本地该值数据(如果有) // 2. true,表示清空本地对应该存储 // 3. undefined, 表示使用输入框的value值作为移除对象 // 元素 var eleInput = this.element.input; // 元素属性值 var strName = eleInput.name; // 值 var strValue = value || this.value(); // 只有data为auto时候才本地记忆 if (strValue && strName) { if (strValue === true) { localStorage.removeItem(DATALIST + '-' + strName); } else if (typeof strValue == 'string') { // 本地获取 var arrList = (localStorage[DATALIST + '-' + strName] || '').split(','); // 当前数据位置 var numIndexMatch = arrList.indexOf(strValue); if (numIndexMatch != -1) { // 删除 arrList.splice(numIndexMatch, 1); // 更改对应的本地存储值 localStorage[DATALIST + '-' + strName] = arrList.join(); } } } return this; }; /** * 刷新列表 * @param {Array} data 刷新列表的数据,可缺省,缺省则调用API中的data()方法获取 * @return {Object} 返回当前实例对象 */ Datalist.prototype.refresh = function (data) { // 元素们 var eleTarget = this.element.target; var eleInput = this.element.input; if (!eleTarget) { this.create(); eleTarget = this.element.target; } // 此时输入框的值 var strValue = this.value(); // 显示的列表数据 var arrData = data; // 这个键盘操作时候需要 var numParamsIndex = this.params.index; // 列表的刷新 // 根据data和filter得到最终呈现的数据 if (typeof arrData == 'undefined') { if (typeof this.callback.data != 'function') { return this; } arrData = this.callback.filter.call(this, this.callback.data(), strValue); if (arrData instanceof Array == false) { return this; } } // 显示的最大个数 if (typeof this.params.max == 'number') { arrData = arrData.slice(0, this.params.max); } // 暴露最终使用的列表数据 this.data = arrData; // 列表HTML组装 var strHtmlList = ''; if (arrData && arrData.length) { // 占位符 var strAttrPlaceholder = eleInput.getAttribute('placeholder'); var strParamPlaceholder = this.params.placeholder; // 拼接列表字符内容 arrData.forEach(function (objData, numIndex) { // 过滤HTML标签 var strValueStrip = funStripHTML(objData.value || '').trim(); var strLabelStrip = funStripHTML(objData.label || '').trim(); var strClassList = ''; if ((strValue && strValueStrip == strValue) || (!strValue && strValueStrip == strAttrPlaceholder && strValueStrip != strParamPlaceholder)) { if (numParamsIndex == numIndex) { strClassList = ' ' + SELECTED; } } // 禁用态,禁用态和选中态不能同时存在 if (objData[DISABLED] || typeof objData[DISABLED] == 'string') { strClassList = ' ' + DISABLED; } if (objData.label) { strHtmlList = strHtmlList + // 虽然使用其他标签模拟<datalist> // 但是,一些属性值,还是遵循HTML规范 '<li class="' + CL.add('option') + strClassList + '" data-value="' + strValueStrip + '" label="' + strLabelStrip + '" data-index="' + numIndex + '">' + // label应该前置,可以通过相邻选择器控制后面内容的UI '<label class="' + CL.add('label') + '">' + objData.label + '</label><span class="' + CL.add('value') + '">' + objData.value + '</span>' + '</li>'; } else { strHtmlList = strHtmlList + // 但是,一些属性值,还是遵循HTML规范 '<li class="' + CL.add('option') + strClassList + '" data-value="' + strValueStrip + '" data-index="' + numIndex + '">' + '<span class="' + CL.add('value') + '">' + objData.value + '</span>' + '</li>'; } }); } if (strHtmlList != '') { strHtmlList = '<ul class="' + CL.add(DATALIST) + '">' + strHtmlList + '</ul>'; } eleTarget.innerHTML = strHtmlList; var eleSelected = eleTarget.querySelector('.' + SELECTED); if (this.display == true && eleSelected) { // 选中元素距离容器上边缘的位置 var numOffsetTop = eleSelected.offsetTop - (eleTarget.lastScrollTop || 0); // 如果不可见 if (numOffsetTop < 0 || numOffsetTop >= eleSelected.parentElement.clientHeight) { eleSelected.parentElement.scrollTop = eleSelected.offsetTop; eleTarget.lastScrollTop = eleSelected.offsetTop; } else { eleSelected.parentElement.scrollTop = eleTarget.lastScrollTop || 0; } } if (strHtmlList) { if (this.display == false) { this.show(); } } else if (this.display == true) { this.hide(); } }; /** * 创建下拉面板 * 方法私有 * @return {Object} 返回当前实例对象 */ Object.defineProperty(Datalist.prototype, 'create', { value: function () { // list属性值需要和创建的列表元素id对应获取 var eleTrigger = this.element.trigger; // 原生HTML5应该是对应datalist元素 // 但1. datalist无法自定义UI; 2. IE9-不支持;3. 一些bug存在 // 所以,我们使用普通的ul列表元素模拟 if (!this.element.target) { // 看看是否有list属性值 var strId = this.element.datalist && this.element.datalist.id; if (!strId) { // 如果没有关联id,创建之 strId = ('lulu_' + Math.random()).replace('0.', ''); // 设置关联id eleTrigger.setAttribute('data-target', strId); } var eleTarget = document.createElement('div'); eleTarget.classList.add(CL); eleTarget.addEventListener('click', function (event) { if (event.touches && event.touches.length) { event = event.touches[0]; } var eleClicked = event.target.closest && event.target.closest('li'); if (eleClicked && eleClicked.classList.contains(DISABLED) == false) { var strValue = eleClicked.getAttribute('data-value'); var strIndex = eleClicked.getAttribute('data-index'); this.params.index = Number(strIndex); // 赋值并关闭列表 this.value(strValue); this.hide(); // 选中触发 this.select(); } }.bind(this)); // 方便区分不同的数据列表 if (eleTrigger.id) { eleTarget.classList.add(CL.add(eleTrigger.id.replace(/[A-Z]/g, function (matches) { return '-' + matches.toLowerCase(); }).replace(/^-+|-+$/g, ''))); } // 载入页面 this.element.container.appendChild(eleTarget); // 元素暴露 this.element.target = eleTarget; // 默认display态 this.display = false; } return this; } }); /** * 输入框赋值或者取值 * @param {String} value 赋予输入框的值,如果缺省,则表示取值 * @return {Object} 返回当前实例对象 */ Datalist.prototype.value = function (value) { // 元素们 var eleInput = this.element.input; if (typeof value == 'undefined') { // 取值 return funEncodeHTML(eleInput.value.trim()); } var strValue = value.toString(); // 赋值 eleInput.value = funDecodeHTML(strValue.trim()); // 使用的数据对象 var objData = this.data[this.params.index]; // 事件 if (JSON.stringify(objData) != JSON.stringify(eleInput.oldValue) || this.params.index != eleInput.lastIndex) { // 赋值时候触发的回调事件们 eleInput.dispatchEvent(new CustomEvent('change', { 'bubbles': true, 'detail': objData })); // 由于输入框可输入,因此input事件是一定要触发的 eleInput.dispatchEvent(new CustomEvent('input', { 'bubbles': true, 'detail': objData })); } // 记住上一次的值和索引 eleInput.oldValue = objData; // 记住索引的原因在于,可能两条数据是一样的 eleInput.lastIndex = this.params.index; return this; }; /** * 一些事件 * @return {Object} 返回当前实例对象 */ Datalist.prototype.events = function () { // 事件 // 元素们 var eleTrigger = this.element.trigger; if (document.activeElement != eleTrigger) { eleTrigger.isFocus = false; } eleTrigger.addEventListener('blur', function () { eleTrigger.isFocus = false; }); eleTrigger.addEventListener('focus', function () { if (window.isKeyEvent) { eleTrigger.click(); } }); eleTrigger.addEventListener('click', function () { if (this.display == false) { // autocomplete模式不执行占位符交互 if (this.placeholder === true) { eleTrigger.focusValue = eleTrigger.value.trim(); if (eleTrigger.focusValue) { eleTrigger.setAttribute('placeholder', eleTrigger.focusValue); } eleTrigger.value = ''; } if (this.params.twice == true && eleTrigger.isFocus == true) { this.refresh(); } else if (!this.params.twice) { this.refresh(); } } eleTrigger.isFocus = true; }.bind(this)); eleTrigger.addEventListener('input', function (event) { if (event.isTrusted === false) { return; } // IE10+下有个bug // focus placeholder变化时候,会触发input事件 if (document.msHidden != undefined && eleTrigger.value.trim() == '' && !eleTrigger.lastValue && eleTrigger.getAttribute('placeholder')) { eleTrigger.lastValue = eleTrigger.value; return; } // 输入行为的时候,如果内容为空,内隐藏列表 if (this.placeholder == true || eleTrigger.value.trim()) { this.refresh(); } else { this.hide(); } }.bind(this)); eleTrigger.addEventListener('keydown', function (event) { // data为当前列表使用的数据 // index表示当前选中列表的索引值 var arrData = this.data; var numIndex = this.params.index; // 面板元素 var eleTarget = this.element.target; if (!eleTarget) { return; } var eleSelected = eleTarget.querySelector('.' + SELECTED); switch (event.keyCode) { case 27: case 13: { // ESC-27 ENTER-13 if (this.display == true) { // 列表隐藏 this.hide(); event.preventDefault(); if (eleSelected) { eleSelected.click(); // 当键盘选值的时候,阻止默认行为 // 例如ENTER表单提交,ESC输入框内容清空 event.preventDefault(); // 文本内容从选中态改为focus态 setTimeout(function () { var eleInput = eleTrigger; if (eleTrigger.setSelectionRange) { try { // 部分输入框类型,例如email, number不支持selection eleInput.setSelectionRange(eleInput.value.length, eleInput.value.length); } catch (e) { eleInput.value = eleInput.value; } } else { eleInput.value = eleInput.value; } // 触发Validate.js中的验证 eleInput.dispatchEvent(new CustomEvent('input', { 'bubbles': true, 'detail': arrData[numIndex] })); }, 17); } } break; } case 38: case 40: { // UP-38 if (this.display == true && arrData && arrData.length) { event.preventDefault(); // 过滤出可以用来选中的索引 var arrIndexMatchAble = []; arrData.forEach(function (objData, numIndexMatch) { if (!objData[DISABLED] && objData[DISABLED] !== '') { arrIndexMatchAble.push(numIndexMatch); } }); // 全部列表都禁用,忽略 if (arrIndexMatchAble.length == 0) { return; } // 然后索引往后挪一位 var numIndexFilterMatch = arrIndexMatchAble.indexOf(numIndex); if (event.keyCode == 38) { numIndexFilterMatch--; } else { numIndexFilterMatch++; } if (numIndexFilterMatch < 0) { numIndex = arrIndexMatchAble[arrIndexMatchAble.length - 1]; } else if (numIndexFilterMatch > arrIndexMatchAble.length - 1) { numIndex = arrIndexMatchAble[0]; } else { numIndex = arrIndexMatchAble[numIndexFilterMatch]; } this.params.index = numIndex; } // 上下键的时候,列表数据不动态获取和过滤 if (arrData[numIndex]) { this.value(funStripHTML(arrData[numIndex].value)); } eleTrigger.select(); this.refresh(arrData); break; } case 46: { // DELETE-46 if (this.display == true && this.params.twice == true && eleSelected) { var strValueSelected = eleSelected.getAttribute('data-value'); // 清除本地存储内容 this.removeStore(strValueSelected); // data中对应对象删除 arrData = arrData.filter(function (objData) { return objData.value != strValueSelected; }); // 阻止默认删除行为 event.preventDefault(); // 获取现在应该显示的值 var objDataLeave = arrData[numIndex] || arrData[numIndex - 1]; if (objDataLeave) { this.value(funStripHTML(objDataLeave.value)); // 列表刷新 this.refresh(arrData); } else { // 全部删除 this.value = ''; // 列表隐藏 this.hide(); } } break; } } }.bind(this)); // 点击空白处隐藏 document.addEventListener(objEventType.end, function (event) { if (event.touches && event.touches.length) { event = event.touches[0]; } var eleClicked = event.target; var eleTarget = this.element.target; if (eleTarget && eleClicked.nodeType == 1 && eleTarget.contains(eleClicked) == false) { this.hide(); } if (eleClicked != eleTrigger && eleTrigger.value.trim() == '') { if (eleTrigger.focusValue) { eleTrigger.value = eleTrigger.focusValue; } else if (this.params.placeholder) { eleTrigger.setAttribute('placeholder', this.params.placeholder); } } }.bind(this)); // 浏览器窗口改变重定位 window.addEventListener('resize', function () { if (this.display == true) { this.position(); } }.bind(this)); return this; }; /** * 下拉面板的定位 * @return {Object} 返回当前实例 */ Datalist.prototype.position = function () { // 元素们 var eleTrigger = this.element.trigger; var eleTarget = this.element.target; if (eleTrigger && eleTarget) { new Follow(eleTrigger, eleTarget, { // 边缘不自动调整,此处手动调整 edgeAdjust: this.params.edgeAdjust || false }); if (this.display == true) { eleTrigger.classList.add(ACTIVE); } } // 列表的定位 return this; }; /** * 列表选择 * @return {Object} 返回当前实例 */ Datalist.prototype.select = function () { // 选择回调 this.callback.select.call(this, this.data[this.params.index], this.element.trigger, this.element.target); }; /** * 下拉面板显示 * @return {Object} 返回当前实例 */ Datalist.prototype.show = function () { // 元素们 var eleTrigger = this.element.trigger; var eleTarget = this.element.target; if (!eleTarget) { this.create(); eleTarget = this.element.target; } // 当前的显示状态 var isDisplay = this.display; // 列表的宽度 var numWidthTarget = this.params.width; var numWidthTrigger = eleTrigger.getBoundingClientRect().width || eleTrigger.clientWidth; if (numWidthTarget == 'auto') { numWidthTarget = numWidthTrigger; } else if (typeof numWidthTarget == 'function') { numWidthTarget = numWidthTarget.call(this, eleTrigger, eleTarget); } if (numWidthTarget != 'auto' && typeof numWidthTarget != 'number') { numWidthTarget = numWidthTrigger; } eleTarget.style.display = 'block'; eleTarget.style.width = numWidthTarget + 'px'; if (typeof eleTarget.lastScrollTop == 'number' && eleTarget.querySelector('ul')) { eleTarget.querySelector('ul').scrollTop = eleTarget.lastScrollTop; } // 显示状态标记 this.display = true; // 定位 this.position(); // 显示回调,当之前隐藏时候才触发 if (isDisplay == false) { this.callback.show.call(this, eleTrigger, eleTarget); } }; /** * 下拉面板隐藏 * @return {Object} 返回当前实例 */ Datalist.prototype.hide = function () { // 元素们 var eleTrigger = this.element.trigger; var eleTarget = this.element.target; if (eleTarget && this.display == true) { // 记住滚动高度 if (eleTarget.querySelector('ul')) { eleTarget.lastScrollTop = eleTarget.querySelector('ul').scrollTop; } eleTarget.style.display = 'none'; eleTarget.classList.remove(REVERSE); // 隐藏回调 this.callback.hide.call(this, eleTrigger, eleTarget); } eleTrigger.classList.remove(ACTIVE); eleTrigger.classList.remove(REVERSE); // 隐藏状态标记 this.display = false; }; // is-datalist 属性实时watch var funAutoInitAndWatching = function () { // 目标选择器 var strSelector = 'input[is-datalist]'; var funSyncRefresh = function (nodes) { if (!nodes || !nodes.forEach) { return; } nodes.forEach(function (node) { if (node.matches && node.matches(strSelector)) { node.dispatchEvent(new CustomEvent('connected')); } }); }; // 遍历页面上匹配的元素 setTimeout(function () { funSyncRefresh(document.querySelectorAll(strSelector)); }, 1); // 如果没有开启观察,不监听DOM变化 if (window.watching === false) { return; } // DOM Insert自动初始化 // IE11+使用MutationObserver // IE9-IE10使用Mutation Events if (window.MutationObserver) { var observerSelect = new MutationObserver(function (mutationsList) { mutationsList.forEach(function (mutation) { mutation.addedNodes.forEach(function (eleAdd) { if (eleAdd.matches) { if (eleAdd.matches(strSelector)) { funSyncRefresh([eleAdd]); } else if (eleAdd.querySelector) { funSyncRefresh(eleAdd.querySelectorAll(strSelector)); } } }); }); }); observerSelect.observe(document.body, { childList: true, subtree: true }); } else { document.body.addEventListener('DOMNodeInserted', function (event) { // 插入节点 funSyncRefresh([event.target]); }); } }; // 监听-免初始绑定 if (document.readyState != 'loading') { funAutoInitAndWatching(); } else { window.addEventListener('DOMContentLoaded', funAutoInitAndWatching); } return Datalist; }));