如何在自动完成回调之外访问 jQuery UI 自动完成建议?

How to access jQuery UI autocomplete suggestions outside of the autocomplete callback?

我想结合 jQueryUI autocmoplete 的功能和标签输入库(不幸的是我不记得我从哪里得到这个库,但源代码在代码片段中)。


关于标签输入库的一点文档:

标签输入库基本隐藏了原来的<input/>,在new Tags('#testTags')初始化的时候增加了一个新的<input class="tag-input" />。可以使用 addTags("someTag");

添加新标签

目前有效:

当我从自动完成中收到一些值并且用户选择该值时,我调用 addTags(ui.item.value); - 这完全没问题。


我遇到的问题:

许多用户不从自动完成器中选择值,他们只是输入文本并单击 'search'。我想确保只插入来自自动完成器的值。 我知道,仅点击自动完成器值并不是用户体验的最佳选择,所以我希望如果用户输入文本并单击 'search',自动选择来自自动完成器的匹配值(如果存在) .

我尝试了这段代码:

$('#search').on('click',function(){
  if($( '.tag-input[placeholder="test1"]' ).val() != "") //user did not finish entry
  {
    testTags.addTags($( '.tag-input[placeholder="test1"]' ).val());
  }
});

如果输入包含尚未转换为标签的值,它会检查按钮单击。如果是这样,我将此值添加为标签。我的问题是,这个值应该在“转换”为标签之前与自动完成器值匹配。 如何访问 .autocomplete({}) 之外的那些自动完成建议?

一个相关的问题是: 不同之处在于,我更需要自动完成建议而不是所选值。


!function(){"use strict";function e(e,t){return"string"==typeof e?(t||document).querySelectorAll(e):[e]}function t(e,t){return"string"==typeof e?(t||document).querySelector(e):e}function n(e,t){var n=document.createElement(e);if(t)for(var i in t)void 0!==n[i]&&(n[i]=t[i]);return n}function i(){var e=document.documentElement,t={transition:"transitionend",WebkitTransition:"webkitTransitionEnd",MozTransition:"mozTransitionEnd",OTransition:"oTransitionEnd otransitionend"};for(var n in t)if(void 0!==e.style[n])return t[n];return!1}function r(e,t,n,i){i=i||!1,e.addEventListener(t,function t(r){n.call(this,r),e.removeEventListener(r.type,t,i)},i)}function a(e,t){return new RegExp("(^|\s+)"+e+"(\s+|$)").test(t.className)}function o(e,t){if(!a(e,t))return t.className+=""===t.className?e:" "+e}function s(e,t){t.className=t.className.replace(new RegExp("(^|\s+)"+e+"(\s+|$)"),"")}function u(u){function l(){y=n("div",{className:"tags-container"}),N=n("input",{type:"text",className:"tag-input",placeholder:h.placeholder||""}),y.appendChild(N),""!==h.value.trim()&&c(),h.type="hidden",h.parentNode.insertBefore(y,h.nextSibling),y.addEventListener("click",v,!1),y.addEventListener("keydown",d,!1),y.addEventListener("keyup",g,!1)}function c(){var e=h.value.trim().split(",");e.forEach(function(e){if(e=e.trim(),!~x.indexOf(e)){var t=f(e);x.push(e),y.insertBefore(t,N)}})}function f(e){var t=n("div",{className:"tag",innerHTML:'<span class="tag__name">'+e+'</span><button class="tag__remove">&times;</button>'});return t}function v(e){if(e.preventDefault(),"tag__remove"===e.target.className){var n=e.target.parentNode,i=t(".tag__name",n);y.removeChild(n),x.splice(x.indexOf(i.textContent),1),h.value=x.join(",")}N.focus()}function d(e){if("INPUT"===e.target.tagName&&"tag-input"===e.target.className){var t=e.target,n=e.which||e.keyCode;N.previousSibling&&n!==C.BACK&&s("tag--marked",N.previousSibling);var i=t.value.trim();n===C.ENTER?(t.blur(),m(i),E&&clearTimeout(E),E=setTimeout(function(){t.focus()},10)):n===C.BACK&&(""!==e.target.value||L||(L=!0,p()))}}function g(e){L=!1}function m(t){if(t=t.toString().replace(/,/g,"").trim(),""===t)return N.value="";if(~x.indexOf(t)){var n=e(".tag",y);return Array.prototype.forEach.call(n,function(e){e.firstChild.textContent===t&&(o("tag--exists",e),k?r(e,k,function(){s("tag--exists",e)}):s("tag--exists",e))}),N.value=""}var i=f(t);y.insertBefore(i,N),x.push(t),N.value="",h.value+=""===h.value?t:","+t}function p(){if(0!==x.length){var t=e(".tag",y),n=t[t.length-1];if(!a("tag--marked",n))return void o("tag--marked",n);x.pop(),y.removeChild(n),h.value=x.join(",")}}var h=t(u);if(!h.instance){h.instance=this;var E,y,N,T=h.type,k=i(),x=[],C={ENTER:13,BACK:8},L=!1;l(),this.getTags=function(){return x},this.clearTags=function(){h.instance&&(x.length=0,h.value="",y.innerHTML="",y.appendChild(N))},this.addTags=function(e){if(h.instance){if(Array.isArray(e))for(var t=0,n=e.length;t<n;t++)m(e[t]);else m(e);return x}},this.destroy=function(){h.instance&&(y.removeEventListener("click",v,!1),y.removeEventListener("keydown",d,!1),y.removeEventListener("keyup",d,!1),y.parentNode.removeChild(y),h.type=T,T=null,x=null,E=null,y=null,N=null,delete h.instance)}}}window.Tags=u}();




var testTags = new Tags('#testTags');

$( function() {
    var availableTags = [
      "ActionScript",
      "AppleScript",
      "Asp",
      "BASIC",
      "C",
      "C++",
      "Clojure",
      "COBOL",
      "ColdFusion",
      "Erlang",
      "Fortran",
      "Groovy",
      "Haskell",
      "Java",
      "JavaScript",
      "Lisp",
      "Perl",
      "PHP",
      "Python",
      "Ruby",
      "Scala",
      "Scheme"
    ];
    
    $( '.tag-input[placeholder="test1"]' ).autocomplete({
      source: availableTags,
      select: function(event, ui){
                event.preventDefault();
                event.stopPropagation();

                testTags.addTags(ui.item.value);

                return false;
            }
    });
    
    $('#search').on('click',function(){
      if($( '.tag-input[placeholder="test1"]' ).val() != "") //user did not finish entry
      {
        testTags.addTags($( '.tag-input[placeholder="test1"]' ).val());
      }
    });
    
 });
*,:after,:before{box-sizing:border-box}.tags-container{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-bottom:15px;width:100%;min-height:34px;padding:2px 5px;font-size:14px;line-height:1.6;background-color:transparent;border:1px solid #ccc;border-radius:1px;overflow:hidden;word-wrap:break-word;box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}input.tag-input{-ms-flex:3;flex:3;border:0;outline:0}.tag{position:relative;margin:2px 6px 2px 0;padding:1px 20px 1px 8px;font-size:inherit;font-weight:400;text-align:center;color:#fff;background-color:#317caf;border-radius:3px;transition:background-color .3s ease;cursor:default}.tag:first-child{margin-left:0}.tag--marked{background-color:#6fadd7}.tag--exists{background-color:#edb5a1;-webkit-animation:a 1s linear;animation:a 1s linear}.tag__name{margin-right:3px}.tag__remove{position:absolute;right:0;bottom:0;width:20px;height:100%;padding:0 5px;font-size:16px;font-weight:400;transition:opacity .3s ease;opacity:.5;cursor:pointer;border:0;background-color:transparent;color:#fff;line-height:1}.tag__remove:hover{opacity:1}@-webkit-keyframes a{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}}@keyframes a{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>

<input id="testTags" placeholder="test1">
<button id="search">search</button>

非常不清楚您要完成什么。我猜,如果用户输入“apple”,离开输入并单击搜索按钮,您想从 availableTags 中获取第一个可能有效的匹配项吗?

考虑以下因素。

  $('#search').on('click', function() {
    if ($('.tag-input[placeholder="test1"]').val() != "") {
      //user did not finish entry
      var partialTag = $('.tag-input[placeholder="test1"]').val();
      var tag = $.ui.autocomplete.filter(availableTags, partialTag)[0];
      testTags.addTags(tag);
    }
  });

$.ui.autocomplete.filter() 的使用可以帮助以与自动完成相同的方式过滤结果。它 returns 一个结果数组。如下所示:https://jqueryui.com/autocomplete/#multiple

文档不多,但您可以在这里找到更多信息:https://api.jqueryui.com/autocomplete/

这是一个完整的例子。

! function() {
  "use strict";

  function e(e, t) {
    return "string" == typeof e ? (t || document).querySelectorAll(e) : [e]
  }

  function t(e, t) {
    return "string" == typeof e ? (t || document).querySelector(e) : e
  }

  function n(e, t) {
    var n = document.createElement(e);
    if (t)
      for (var i in t) void 0 !== n[i] && (n[i] = t[i]);
    return n
  }

  function i() {
    var e = document.documentElement,
      t = {
        transition: "transitionend",
        WebkitTransition: "webkitTransitionEnd",
        MozTransition: "mozTransitionEnd",
        OTransition: "oTransitionEnd otransitionend"
      };
    for (var n in t)
      if (void 0 !== e.style[n]) return t[n];
    return !1
  }

  function r(e, t, n, i) {
    i = i || !1, e.addEventListener(t, function t(r) {
      n.call(this, r), e.removeEventListener(r.type, t, i)
    }, i)
  }

  function a(e, t) {
    return new RegExp("(^|\s+)" + e + "(\s+|$)").test(t.className)
  }

  function o(e, t) {
    if (!a(e, t)) return t.className += "" === t.className ? e : " " + e
  }

  function s(e, t) {
    t.className = t.className.replace(new RegExp("(^|\s+)" + e + "(\s+|$)"), "")
  }

  function u(u) {
    function l() {
      y = n("div", {
        className: "tags-container"
      }), N = n("input", {
        type: "text",
        className: "tag-input",
        placeholder: h.placeholder || ""
      }), y.appendChild(N), "" !== h.value.trim() && c(), h.type = "hidden", h.parentNode.insertBefore(y, h.nextSibling), y.addEventListener("click", v, !1), y.addEventListener("keydown", d, !1), y.addEventListener("keyup", g, !1)
    }

    function c() {
      var e = h.value.trim().split(",");
      e.forEach(function(e) {
        if (e = e.trim(), !~x.indexOf(e)) {
          var t = f(e);
          x.push(e), y.insertBefore(t, N)
        }
      })
    }

    function f(e) {
      var t = n("div", {
        className: "tag",
        innerHTML: '<span class="tag__name">' + e + '</span><button class="tag__remove">&times;</button>'
      });
      return t
    }

    function v(e) {
      if (e.preventDefault(), "tag__remove" === e.target.className) {
        var n = e.target.parentNode,
          i = t(".tag__name", n);
        y.removeChild(n), x.splice(x.indexOf(i.textContent), 1), h.value = x.join(",")
      }
      N.focus()
    }

    function d(e) {
      if ("INPUT" === e.target.tagName && "tag-input" === e.target.className) {
        var t = e.target,
          n = e.which || e.keyCode;
        N.previousSibling && n !== C.BACK && s("tag--marked", N.previousSibling);
        var i = t.value.trim();
        n === C.ENTER ? (t.blur(), m(i), E && clearTimeout(E), E = setTimeout(function() {
          t.focus()
        }, 10)) : n === C.BACK && ("" !== e.target.value || L || (L = !0, p()))
      }
    }

    function g(e) {
      L = !1
    }

    function m(t) {
      if (t = t.toString().replace(/,/g, "").trim(), "" === t) return N.value = "";
      if (~x.indexOf(t)) {
        var n = e(".tag", y);
        return Array.prototype.forEach.call(n, function(e) {
          e.firstChild.textContent === t && (o("tag--exists", e), k ? r(e, k, function() {
            s("tag--exists", e)
          }) : s("tag--exists", e))
        }), N.value = ""
      }
      var i = f(t);
      y.insertBefore(i, N), x.push(t), N.value = "", h.value += "" === h.value ? t : "," + t
    }

    function p() {
      if (0 !== x.length) {
        var t = e(".tag", y),
          n = t[t.length - 1];
        if (!a("tag--marked", n)) return void o("tag--marked", n);
        x.pop(), y.removeChild(n), h.value = x.join(",")
      }
    }
    var h = t(u);
    if (!h.instance) {
      h.instance = this;
      var E, y, N, T = h.type,
        k = i(),
        x = [],
        C = {
          ENTER: 13,
          BACK: 8
        },
        L = !1;
      l(), this.getTags = function() {
        return x
      }, this.clearTags = function() {
        h.instance && (x.length = 0, h.value = "", y.innerHTML = "", y.appendChild(N))
      }, this.addTags = function(e) {
        if (h.instance) {
          if (Array.isArray(e))
            for (var t = 0, n = e.length; t < n; t++) m(e[t]);
          else m(e);
          return x
        }
      }, this.destroy = function() {
        h.instance && (y.removeEventListener("click", v, !1), y.removeEventListener("keydown", d, !1), y.removeEventListener("keyup", d, !1), y.parentNode.removeChild(y), h.type = T, T = null, x = null, E = null, y = null, N = null, delete h.instance)
      }
    }
  }
  window.Tags = u
}();




var testTags = new Tags('#testTags');

$(function() {
  var availableTags = [
    "ActionScript",
    "AppleScript",
    "Asp",
    "BASIC",
    "C",
    "C++",
    "Clojure",
    "COBOL",
    "ColdFusion",
    "Erlang",
    "Fortran",
    "Groovy",
    "Haskell",
    "Java",
    "JavaScript",
    "Lisp",
    "Perl",
    "PHP",
    "Python",
    "Ruby",
    "Scala",
    "Scheme"
  ];

  $('.tag-input[placeholder="test1"]').autocomplete({
    source: availableTags,
    select: function(event, ui) {
      event.preventDefault();
      event.stopPropagation();
      testTags.addTags(ui.item.value);
      return false;
    }
  });

  $('#search').on('click', function() {
    if ($('.tag-input[placeholder="test1"]').val() != "") {
      //user did not finish entry
      var partialTag = $('.tag-input[placeholder="test1"]').val();
      var tag = $.ui.autocomplete.filter(availableTags, partialTag)[0];
      testTags.addTags(tag);
    }
  });

});
*,
:after,
:before {
  box-sizing: border-box
}

.tags-container {
  display: -ms-flexbox;
  display: flex;
  -ms-flex-flow: row wrap;
  flex-flow: row wrap;
  margin-bottom: 15px;
  width: 100%;
  min-height: 34px;
  padding: 2px 5px;
  font-size: 14px;
  line-height: 1.6;
  background-color: transparent;
  border: 1px solid #ccc;
  border-radius: 1px;
  overflow: hidden;
  word-wrap: break-word;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1)
}

input.tag-input {
  -ms-flex: 3;
  flex: 3;
  border: 0;
  outline: 0
}

.tag {
  position: relative;
  margin: 2px 6px 2px 0;
  padding: 1px 20px 1px 8px;
  font-size: inherit;
  font-weight: 400;
  text-align: center;
  color: #fff;
  background-color: #317caf;
  border-radius: 3px;
  transition: background-color .3s ease;
  cursor: default
}

.tag:first-child {
  margin-left: 0
}

.tag--marked {
  background-color: #6fadd7
}

.tag--exists {
  background-color: #edb5a1;
  -webkit-animation: a 1s linear;
  animation: a 1s linear
}

.tag__name {
  margin-right: 3px
}

.tag__remove {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 20px;
  height: 100%;
  padding: 0 5px;
  font-size: 16px;
  font-weight: 400;
  transition: opacity .3s ease;
  opacity: .5;
  cursor: pointer;
  border: 0;
  background-color: transparent;
  color: #fff;
  line-height: 1
}

.tag__remove:hover {
  opacity: 1
}

@-webkit-keyframes a {
  0%,
  to {
    -webkit-transform: translateZ(0);
    transform: translateZ(0)
  }
  10%,
  30%,
  50%,
  70%,
  90% {
    -webkit-transform: translate3d(-5px, 0, 0);
    transform: translate3d(-5px, 0, 0)
  }
  20%,
  40%,
  60%,
  80% {
    -webkit-transform: translate3d(5px, 0, 0);
    transform: translate3d(5px, 0, 0)
  }
}

@keyframes a {
  0%,
  to {
    -webkit-transform: translateZ(0);
    transform: translateZ(0)
  }
  10%,
  30%,
  50%,
  70%,
  90% {
    -webkit-transform: translate3d(-5px, 0, 0);
    transform: translate3d(-5px, 0, 0)
  }
  20%,
  40%,
  60%,
  80% {
    -webkit-transform: translate3d(5px, 0, 0);
    transform: translate3d(5px, 0, 0)
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>

<input id="testTags" placeholder="test1">
<button id="search">search</button>