效果
访问:https://bxcn.github.io/keyWord/
目录结构
参考
https://github.com/bxcn/keyWord
readme.md
一个简单的输入关键字添加标签效果 实现功能: 输入关键字加空格键添加tag标签 按Backspace键删除一个标签 输入关键字后,鼠标失去焦点添加tag标签 keyWord.init方法初始化方法 防止输入重复的关键字 限止最多输入几个关键字 <style> .block { display:flex; flex-direction:row; align-items:center; 500px; height:30px; border:1px solid #ddd; padding:10px; margin:100px auto 0; } #wordTags { display:flex; flex-wrap:nowrap; } input{ 100%; height:20px; border:none; } </style> <div class="block"> <div id="wordTags"></div> <input id="wordInput" type="text" name="" placeholder="请输入关键词以空格结尾"> <input id="wordHiddenInput" type="hidden" name=""> </div> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script> <script type="text/javascript" src="aspect.js"></script> <script type="text/javascript" src="keyWord.js"></script> $(function () { var keyWord = $("#wordInput").keyWord({ panel: '#wordTags', value: '#wordHiddenInput', max: 3, tips: '最多只能输入3项' }); keyWord.init('php,java,前端开发') }); 属性说明: panel:面板的id value:隐藏字段的id max:最多输入关键字个数 tips:提示语
index.html
<!DOCTYPE html> <html> <head> <title>keyWord--一个简单的输入关键字添加标签效果</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="index.css"> </head> <body> <style> .block { display:flex; flex-direction:row; align-items:center; 500px; height:30px; border:1px solid #ddd; padding:10px; margin:100px auto 0; } #wordTags { display:flex; flex-wrap:nowrap; } input{ 100%; height:20px; border:none; } </style> </body> <div class="block"> <div id="wordTags"></div> <input id="wordInput" type="text" name="" placeholder="请输入关键词以空格结尾"> <input id="wordHiddenInput" type="hidden" name=""> </div> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script> <script type="text/javascript" src="aspect.js"></script> <script type="text/javascript" src="keyWord.js"></script> <script type="text/javascript"> $(function () { var keyWord = $("#wordInput").keyWord({ panel: '#wordTags', value: '#wordHiddenInput', max: 3, tips: '最多只能输入3项' }); keyWord.init('php,php,java,前端开发') }); </script> </html>
index.css
@charset "UTF-8"; @font-face { font-family: 'iconfont'; src: url("../fonts/iconfont.eot"); /* IE9*/ src: url("../fonts/iconfont.eot?#iefix") format("embedded-opentype"), url("../fonts/iconfont.woff") format("woff"), url("../fonts/iconfont.ttf") format("truetype"), url("../fonts/iconfont.svg#iconfont") format("svg"); /* iOS 4.1- */ } html { position: relative; min-height: 100%; -webkit-overflow-scrolling: touch; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, body, div, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fieldset, legend, button, input, textarea, form, th, td { margin: 0; padding: 0; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary { display: block; } audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } body, button, input, select, textarea { outline: none; } h1, h2, h3, h4, h5, h6 { font-size: 100%; font-weight: normal; } address, cite, dfn, em, var, i { font-style: normal; } body { line-height: 120%; font-size: 14px; color: #4d4d4d; font-family: Arial, sans-serif;} small { font-size: 80%; } ul, ol { list-style: none outside none; } a { text-decoration: none; } a:hover { text-decoration: none; outline: 0; } a:active { text-decoration: none; outline: 0; } a:focus { text-decoration: none; outline: 0; } abbr[title], acronym[title] { border-bottom: 1px dotted; cursor: help; } q:before, q:after { content: ''; } mark { background-color: #ff0; color: #000; } pre { /** normal 默认。空白会被浏览器忽略。 pre 空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。 nowrap 文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。 pre-wrap 保留空白符序列,但是正常地进行换行。 pre-line 合并空白符序列,但是保留换行符。 inherit 规定应该从父元素继承 white-space 属性的值。 */ white-space: pre-wrap; word-wrap: break-word; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } legend { display: none; border: 0; padding: 0; white-space: normal; } fieldset, iframe { border: 0 none; } img { border: 0 none; vertical-align: middle; -ms-interpolation-mode: bicubic; } button, input, select, textarea { font-family: inherit; font-size: 100%; vertical-align: baseline; } button, input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 改正iOS设备中“input”类型表单样式不可用的问题 */ cursor: pointer; /* 增强光标样式在input表单和其他表单的可用性和一致性 */ } button[disabled], html input[disabled] { cursor: default; /* 为禁用表单重设定默认光标样式 */ } button::-moz-focus-inner, button::-moz-focus-outer, input::-moz-focus-inner, input::-moz-focus-outer { border: 0 none; padding: 0; margin: 0; } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 调整IE 8/9中尺寸属性设置为“内容框”的盒子模型 */ padding: 0; /* 去除IE 8/9中的多余的外边距留白部分 */ } input[type="search"] { -webkit-appearance: textfield; /* 兼容Safari 5 and Chrome上 “searchfield” 上设置 “appearance”属性 */ /* 兼容Safari 5 and Chrome上 “border-box” 上设置 “box-sizing”属性 */ box-sizing: content-box; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; /* 去除OS X系统上Safari 5和Chrome中容器内边距和搜索取消按钮属性 */ } input:-webkit-autofill, textarea:-webkit-autofill, select:-webkit-autofill { -webkit-box-shadow: 0 0 0 1000px white inset; } textarea { overflow: auto; vertical-align: top; resize: vertical; } table { /* 删除表格单元格之间的间距。 */ border-collapse: collapse; border-spacing: 0; } strong { font-weight: normal; } img { vertical-align: middle; } .clearfix { zoom: 1; clear: both; height: 0; overflow: hidden; 100%; display: block; } .clearfix:before, .clearfix:after { content: ""; display: block; height: 0; overflow: hidden; visibility: hidden; } .fl { float: left; } .fr { float: right; } .mauto { float: inherit !important; margin-left: auto; margin-right: auto; } fieldset { border: none; } input::-webkit-input-placeholder { color: #bfbfbf; } input:-ms-input-placeholder { color: #bfbfbf; } input:-moz-placeholder { color: #bfbfbf; } input::-moz-placeholder { color: #bfbfbf; } /* 删除选中标签 */ .tag-checked-name { display:inline-block; position:relative; height: 24px; border-radius: 1px; color:#4abee0; font-size:12px; line-height:24px; padding:0 20px 0 8px; background-color: #f7fdff; border: solid 1px #4abee0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right:5px; } .tag-checked-name:first-of-type { margin-left: 0px; } .tag-checked-name em { display:block; position:absolute; top:5px; right:5px; 13px; height: 13px; cursor:pointer; transform: rotate(45deg); } .tag-checked-name em:after { display:block; position:absolute; content:''; top:6px; 13px; height:1px; background:#4abee0; } .tag-checked-name em:before { display:block; position:absolute; content:''; top:0px; left:5px; 1px; height: 13px; background:#4abee0; }
aspect.js
/** * @file aspect * @fork https://github.com/ecomfe/saber-lang/blob/master/src/function/aspect.js */ (function () { /** * Aspect * * @inner * @type {Object} */ var Aspect = {}; /** * before AOP * * @private * @param {string} method 欲AOP的目标方法名 * @param {Function} fn AOP处理函数 * @param {*} context `fn`调用时的上下文 * @return {Object} 目标对象 */ Aspect.before = function (method, fn, context) { return aspectTo(this, 'before', method, fn, context); }; /** * after AOP * * @private * @param {string} method 欲AOP的目标方法名 * @param {Function} fn AOP处理函数 * @param {*} context `fn`调用时的上下文 * @return {Object} 目标对象 */ Aspect.after = function (method, fn, context) { return aspectTo(this, 'after', method, fn, context); }; /** * 对`目标对象`的`指定方法`进行`AOP`包装 * * @inner * @param {Object} target 目标对象 * @param {string} type AOP方式,可取值 `before` | `after` * @param {string} method 欲AOP的目标对象的方法名 * @param {Function} fn AOP处理函数 * @param {*} context `fn`调用时的上下文 * @return {Object} 目标对象 */ function aspectTo(target, type, method, fn, context) { var oriMethod = target[method]; if (oriMethod) { if (type === 'before') { target[method] = function () { // abort support if (fn.apply(context || fn, arguments) !== false) { oriMethod.apply(this, arguments); } }; } else if (type === 'after') { target[method] = function () { oriMethod.apply(this, arguments); fn.apply(context || fn, arguments); }; } } return target; } /** * Aspect * * @exports Aspect * @type {Object} */ var exports = {}; /** * 将 `Aspect` 混入到目标对象 * * @public * @param {Object} obj 目标对象 * @return {Object} 混入 `Aspect` 后的目标对象 */ exports.mixin = function (obj) { // 省略了 hasOwnProperty 校验 /* eslint-disable guard-for-in */ for (var method in Aspect) { obj[method] = Aspect[method]; } /* eslint-enable guard-for-in */ return obj; }; window.AOP = exports; })();
keyWord.js
/** * 定义一个列表数据结构 * 作用:添加元素、删除元素、清除所有元素,将数据中的元素组装成对象返回一个数据 * 只针对数据处理 * @constructor */ function List() { this.dataStore = new Array(); this.listSize = 0; this.pos = 0; } List.prototype = { constructor: List, append: function(name) { this.dataStore[this.listSize++] = name; }, cusPos: function() { return this.pos; }, front: function() { this.pos = 0; }, end: function() { this.pos = this.listSize - 1; }, length: function() { return this.listSize; }, prev: function() { if (this.pos > 0) { --this.pos; } }, next: function() { if (this.pos < this.listSize) { ++this.pos; } }, find: function(name) { var index = -1; this.dataStore.forEach(function(data, i, array) { if (data == name) { index = i; } }); return index; }, remove: function(name) { var index = this.find(name); if (index > -1) { this.dataStore.splice(index, 1); --this.listSize; return true; } return false; }, getElement: function() { return this.dataStore[this.pos]; }, clear: function() { delete this.dataStore; this.dataStore = []; this.pos = this.listSize = 0; } } /** * 定义一个用来对列表操作的对象 * 对列表的一个包装 * @param options */ var doKeyWord = function(options) { var settings = options; var list = new List(); return AOP.mixin({ init: function(arr) { var that = this; // 初始化 if (typeof arr == "string") { arr = arr == '' ? [] : arr.split(','); } if (typeof arr == 'undefined') { arr = []; } // 清空数据 list.clear(); // 便利添加数据中 arr.forEach(function(data, i, array) { that.add(data); }) }, render: function() { // 渲染效果 var valueArr = [], html = [], name; for (list.front(); list.cusPos() < list.length(); list.next()) { name = list.getElement(); valueArr.push(name); html.push('<div class="tag-checked-name">' + name.substr(0, 10) + '<em data-word-tag-close="' + name + '"></em></div>') } $(settings.panel).html(html.join('')); $(settings.value).val(valueArr.join(',')); }, add: function(name) { name = $.trim(name); if (name == '') { return false; } // 添加数据 if (list.find(name) > -1) { return false; } list.append(name); }, remove: function(name) { list.remove(name); }, clear: function() { list.clear(); }, front: function() { return list.front(); }, end: function() { return list.end() }, getElement: function() { return list.getElement(); }, length: function() { return list.length(); } }); } $(function() { $.fn.keyWord = function(options) { var keyWord = doKeyWord(options); // 对添加的数据进行检查 function doCheck() { if (options.max < keyWord.length() + 1) { alert(options.tips); return false; } return true; } var render = keyWord.render; // 添加前检查 keyWord.before('add', doCheck); // 初始化后渲染效果 keyWord.after('init', render); // 添加后渲染效果 keyWord.after('add', render); // 删除后渲染效果 keyWord.after('remove', render); var that = $(this); // 删除元素 $(document).on('click', '[data-word-tag-close]', function() { var name = $(this).data('word-tag-close'); // 过滤掉不删除的 keyWord.remove(name); }); /** * Backspace删除 对应的键盘编码 * e.keyCode == 8 :Backspace键 */ that.keydown(function(e) { var that = $(this); var val = $.trim(that.val()); if (val == "" && e.keyCode == 8) { keyWord.end(); keyWord.remove(keyWord.getElement()); } }); // 添加数据 function doAdd(name) { name = $.trim(that.val()); that.val(''); if (name == '') { return; } keyWord.add(name); } /** * 判断有输入空格吗 * e.keyCode == 32 空格键 */ that.keyup(function(e) { var that = $(this); var isSpaceKey = /s+$/gi.test(that.val()); // 是空格键输入了一个空格字符 if (e.keyCode == 32 && isSpaceKey) { doAdd(that.val()) } }); // 鼠标失去焦点 that.blur(function(e) { doAdd(that.val()) }); this.init = function(arr) { keyWord.init(arr); } return this; } });