一个简单的输入关键字添加标签效果

效果

访问: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;

  }
});
原文地址:https://www.cnblogs.com/robinunix/p/8494596.html