【问题标题】:How to use materialize autocomplete plugin with ajax?如何将物化自动完成插件与ajax一起使用?
【发布时间】:2016-12-06 00:42:54
【问题描述】:

我正在尝试将 materializecss autocomplete plugin 与我的 ajax 调用一起工作,以便根据输入字段上键入的内容动态加载数据。

我的 ajax 请求在 keydown 事件中被调用。所有获取的数据都会自动推送到键/值对象数组中。

然后,我把自动完成功能放在ajax的成功函数中,键“data”的值就是之前构建的对象数组。

看来我的方法不错,但是当我在浏览器中进行测试时,每次输入内容时,建议下拉列表都会按预期显示结果,但不是在每次按键后更新,而是另一个下拉列表与上一个等等...

所以这是我的问题:如何避免下拉建议列表重叠,而是让它在我每次按键时更新?

感谢您的帮助。

var dat = {};
$("input").on("keydown", function(e) {
    var d = {
        query: {
            prefix: {
                body: e.target.value
            }
        }
    };

    $.ajax({
        url: "https://xxxxxxxxxxxxxxx.eu-west-1.es.amazonaws.com/xxxxxxxxxxxxx",
        type: "POST",
        contentType: "application/json",
        crossDomain: true,
        data: JSON.stringify(d),
        dataType: "JSON",
        async: true,
        success: function(da) {
            var c = da.hits.hits.length;
            for (var i = 0; i < c; i++) {
                dat[da.hits.hits[i]._source.body] = null;
            }

            $("input").autocomplete({
                data: dat
            });
        },
        error: function(jqXHR, errorStatus, errorThrown) {
            console.log(jqXHR);
            console.log(errorStatus);
            console.log(errorThrown);
        }
    });
});

【问题讨论】:

  • 你让我们都处于劣势。您可以看到您的代码,而我们不能。
  • @BobRodes 对不起,我认为在这种情况下解释更准确。现在你可以检查代码了

标签: jquery ajax autocomplete materialize


【解决方案1】:

您的autocomplete() 函数缺少选项列表对象的右括号、函数本身的右括号以及后面需要的分号。尝试修复这些,看看你会得到什么:

    success: function(da){          
        var c = da.hits.hits.length;
        for(var i = 0; i < c; i++){
            dat[da.hits.hits[i]._source.body] = null;
        }
        $("input").autocomplete({
            data : dat
        });  <----HERE
    },

好的。您已经修复了括号,但仍然存在问题。首先,我不清楚为什么每次用户按下一个键时你都试图用新数据重新初始化自动完成小部件。我会在打开页面时初始化它,然后如果数据发生更改,我会重新初始化它。可用的选择不会因为用户按键而改变。

但除此之外:看看文档,它非常粗略,而且你已经完成了它告诉你做的事情。它没有解决如何使用新数据重新初始化现有小部件。在重新初始化之前,您可能必须找到一种方法来删除以前的列表。以下是他们对Select 小部件的评价:

如果你想更新选择中的项目,只需在编辑原始选择后重新运行上面的初始化代码。或者您可以使用下面的这个函数来破坏材质选择,然后完全创建一个新的选择。

也许这也适用于autocomplete 小部件,但他们只是未能记录它。所以,我先试试这个:

        $("input").autocomplete("destroy")
        .autocomplete({
            data : dat
        });

销毁现有的自动完成功能,然后使用新数据重新初始化它。如果这不起作用(他们没有说他们支持自动完成的destroy() 方法,但在你尝试之前你永远不会知道)那么你需要深入研究 DOM,找到包含数据,并在调用 autocomplete() 函数之前编写代码将其删除。如果失败了,那么您可能会考虑使用 jquery-ui 自动完成功能,因为使用它的人比您正在使用的人多得多,而且您可以获得更好的帮助。

【讨论】:

  • 谢谢。但是总是同样的问题。每次我按下一个键时,下拉列表都会重复,并且有几个下拉列表重叠
  • 那么,你修好了括号还是有问题?
  • 是的,我修复了括号和分号。还是一样的问题
  • 那么,你为什么要把这一切都放在 keydown 事件中呢?我没有马上注意到。
  • 因为我希望 ajax 在我开始输入后立即向我的 url 发出请求。例如:如果我正在搜索单词 Hello,我希望我的 ajax 在我键入 H 时请求服务器。你知道这是我找到的唯一一种近乎实时获取数据的解决方案。但显然,自动完成功能也在使用 keydown 事件,所以我认为这就是下拉列表重复的原因。
【解决方案2】:

给你,一个更简洁的例子。

  • 基于 Materialized.js 原函数
  • 在您键入时取消现有的 ajax 请求,这样您就不会得到双重结果
  • 如果您删除“超时”注释行,这只会在按键后经过“x”时间后调用 ajax 调用。当您快速键入以避免每次按键时调用 ajax(即使它们被取消)时可能很有用。

见下文:

initAutoComplete({inputId:'autocomplete-input',ajaxUrl:'/search/my-auto-complete-results'})


function initAutoComplete(options)
{  
  var defaults = {
    inputId:null,
    ajaxUrl:false,    
    data: {}
  };

  options = $.extend(defaults, options);
  var $input = $("#"+options.inputId);

  if (options.ajaxUrl !== false)
  {
    var $autocomplete = $('<ul id="myId" class="autocomplete-content dropdown-content"></ul>'),   
        $inputDiv = $input.closest('.input-field'),
        //timeout,
        runningRequest = false,
        request;

    if ($inputDiv.length) {
      $inputDiv.append($autocomplete); // Set ul in body
    } else {      
      $input.after($autocomplete);
    }

    var highlight = function(string, $el) {
      var img = $el.find('img');
      var matchStart = $el.text().toLowerCase().indexOf("" + string.toLowerCase() + ""),
          matchEnd = matchStart + string.length - 1,
          beforeMatch = $el.text().slice(0, matchStart),
          matchText = $el.text().slice(matchStart, matchEnd + 1),
          afterMatch = $el.text().slice(matchEnd + 1);
      $el.html("<span>" + beforeMatch + "<span class='highlight'>" + matchText + "</span>" + afterMatch + "</span>");
      if (img.length) {
        $el.prepend(img);
      }
    };

    $autocomplete.on('click', 'li', function () {
      $input.val($(this).text().trim());
      $autocomplete.empty();
    });

    $input.on('keyup', function (e) {

      //if(timeout){ clearTimeout(timeout);}
      if(runningRequest) request.abort();      

      if (e.which === 13) {
        $autocomplete.find('li').first().click();
        return;
      }

      var val = $input.val().toLowerCase();
      $autocomplete.empty();

      //timeout = setTimeout(function() {

        runningRequest=true;

        request = $.ajax({
          type: 'GET', // your request type
          url: options.ajaxUrl,        
          success: function (data) {
            if (!$.isEmptyObject(data)) {
              // Check if the input isn't empty
              if (val !== '') {
                for(var key in data) {
                  if (data.hasOwnProperty(key) &&
                      key.toLowerCase().indexOf(val) !== -1 &&
                      key.toLowerCase() !== val) {
                    var autocompleteOption = $('<li></li>');
                    if(!!data[key]) {
                      autocompleteOption.append('<img src="'+ data[key] +'" class="right circle"><span>'+ key +'</span>');
                    } else {
                      autocompleteOption.append('<span>'+ key +'</span>');
                    }
                    $autocomplete.append(autocompleteOption);

                    highlight(val, autocompleteOption);
                  }
                }
              }
            }                    
          },
          complete:function(){
            runningRequest = false;
          }        
        });
      //},250);
    });
  }
  else 
  {
    $input.autocomplete({
      data: options.data
    });
  }
}

【讨论】:

  • 使用过它,在适应我的用例后,工作就像一个魅力,不错的方法 tho
【解决方案3】:

我解决了这个问题。

<script src="jquery/2.1.4/jquery.js" type="application/javascript"></script>
<script src="/js/default.js" type="application/javascript"></script>
<script src="materializecss/0.97.7/js/materialize.js" type="application/javascript"></script>

注意库中间的文件“default.js”。

然后在“default.js”中

$.fn.alterAutocomplete = $.fn.autocomplete;

在我需要使用自动完成插件的任何地方我都这样做了。

 $('#autocomplete-input').alterAutocomplete ({
    source: function (request, response) {

    },
    response: function (event, ui) {
        if (ui.content.length <= 0) {
            ui.content.push({label: "No results"});
        }
    },
    select: function (event, ui) {
    }
});

【讨论】:

    【解决方案4】:

    参加聚会有点晚了,但认为这可能会帮助一些在同一问题上苦苦挣扎的人。

    我发现的一种方法是制作 autocomplete() 返回的对象的副本,然后在副本上使用内置的 data() 函数遍历结果并将它们添加进去。需要一个新副本,否则它只会添加对象中的额外值(我确信有一些方法可以清理对象,但所有常用方法对我来说都失败了)。

    var data = [{
      key : 'One'
    }, {
      key : 'Two'
    }, {
      key : 'Three'
    }, {
      key: 'Twenty One'
    }, {
      key: 'Thirty Five'
    }, {
      key: 'Three Thousand'
    }]; // Dummy data to emulate data from ajax request
    
    function autocompleteData(field) {
      window.acSearch = $.ajax({
          url: 'somescript.php',
          type: 'GET',
          data: {
            key: function() {
              return $(field).val().trim();
            }
          },
          success: function(data) {
            $('.autocomplete-content').remove(); // Clear the old elements
            var newData = $.extend({}, $(field).autocomplete()); // Create copy of autocomplete object
            for (var i = 0; i < 20 && i < data.length; i++) {
              newData.data((data[i]["key"]), null); // Iterate through results and add to the copied autocomplete object (I set the limit to 20 as this is the limit I set below for the autocomplete)
          	}
    
            $(field).autocomplete({
            data: newData.data(),
            limit: 20, // Limit the number of results
          });
          $(field).keyup(); // This is just to get it to show the updated autocomplete results
        },
        error: function(){ // Ajax request will error as the URL is invalid so we will use the dummy data var created earlier and process the same function on error as we would on success - THIS IS NOT NEEDED (it's just for demonstrative purposed)
          $('.autocomplete-content').remove();
            var newData = $.extend({}, $(field).autocomplete());
            for (var i = 0; i < 20 && i < data.length; i++) {
              newData.data(data[i]["key"], null);
          	}
    
            $(field).autocomplete({
            data: newData.data(),
            limit: 20,
          });
          $(field).keyup();
        },
        complete: function(data) {
          setTimeout(function() {
            $(field).keyup()
          }, 250);
    
        }
      });
    }
    
    // Event handler on input field to trigger our function above and to clear any pending ajax requests
    $('#autocompleteInput').on('input', function(e) {
      if (typeof acSearch != 'undefined') {
        acSearch.abort();
      }
      autocompleteData(this);
    });
    <head>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css" rel="stylesheet"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/js/materialize.min.js"></script>
    <head>
    <body>
    <div class="container">
      <label for="autocompleteInput">Example Autocomplete</label>
      <div class="input-field">
        <input id="autocompleteInput" class="autocomplete">
      </div>
    </div>
    </body>

    【讨论】:

      【解决方案5】:

      在@friek108 的出色回答的基础上,让我们添加以下功能。

      • 单击外部时关闭自动完成小部件。
      • 允许使用箭头键滚动结果并使用 Enter 键进行选择。
      • 仅在输入预定义的最少字符数后进行 AJAX 调用。
      • 阻止某些键触发 AJAX 调用。

      这采用了@friek108 回答中的超时和ajax 呼叫取消功能。你可能想先检查一下。

      ajaxAutoComplete({inputId:'autocomplete-input',ajaxUrl:'/search/my-auto-complete-results'})
      
      function ajaxAutoComplete(options)
      {
      
          var defaults = {
              inputId:null,
              ajaxUrl:false,    
              data: {},
              minLength: 3
          };
      
          options = $.extend(defaults, options);
          var $input = $("#" + options.inputId);
      
      
          if (options.ajaxUrl){
      
      
              var $autocomplete = $('<ul id="ac" class="autocomplete-content dropdown-content"'
                  + 'style="position:absolute"></ul>'),
              $inputDiv = $input.closest('.input-field'),
              request,
              runningRequest = false,
              timeout,
              liSelected;
      
              if ($inputDiv.length) {
                  $inputDiv.append($autocomplete); // Set ul in body
              } else {
                  $input.after($autocomplete);
              }
      
              var highlight = function (string, match) {
                  var matchStart = string.toLowerCase().indexOf("" + match.toLowerCase() + ""),
                  matchEnd = matchStart + match.length - 1,
                  beforeMatch = string.slice(0, matchStart),
                  matchText = string.slice(matchStart, matchEnd + 1),
                  afterMatch = string.slice(matchEnd + 1);
                  string = "<span>" + beforeMatch + "<span class='highlight'>" + 
                  matchText + "</span>" + afterMatch + "</span>";
                  return string;
      
              };
      
              $autocomplete.on('click', 'li', function () {
                  $input.val($(this).text().trim());
                  $autocomplete.empty();
              });
      
              $input.on('keyup', function (e) {
      
                  if (timeout) { // comment to remove timeout
                      clearTimeout(timeout);
                  }
      
                  if (runningRequest) {
                      request.abort();
                  }
      
                  if (e.which === 13) { // select element with enter key
                      liSelected[0].click();
                      return;
                  }
      
                  // scroll ul with arrow keys
                  if (e.which === 40) {   // down arrow
                      if (liSelected) {
                          liSelected.removeClass('selected');
                          next = liSelected.next();
                          if (next.length > 0) {
                              liSelected = next.addClass('selected');
                          } else {
                              liSelected = $autocomplete.find('li').eq(0).addClass('selected');
                          }
                      } else {
                          liSelected = $autocomplete.find('li').eq(0).addClass('selected');
                      }
                      return; // stop new AJAX call
                  } else if (e.which === 38) { // up arrow
                      if (liSelected) {
                          liSelected.removeClass('selected');
                          next = liSelected.prev();
                          if (next.length > 0) {
                              liSelected = next.addClass('selected');
                          } else {
                              liSelected = $autocomplete.find('li').last().addClass('selected');
                          }
                      } else {
                          liSelected = $autocomplete.find('li').last().addClass('selected');
                      }
                      return;
                  } 
      
                  // escape these keys
                  if (e.which === 9 ||        // tab
                      e.which === 16 ||       // shift
                      e.which === 17 ||       // ctrl
                      e.which === 18 ||       // alt
                      e.which === 20 ||       // caps lock
                      e.which === 35 ||       // end
                      e.which === 36 ||       // home
                      e.which === 37 ||       // left arrow
                      e.which === 39) {       // right arrow
                      return;
                  } else if (e.which === 27) { // Esc. Close ul
                      $autocomplete.empty();
                      return;
                  }
      
                  var val = $input.val().toLowerCase();
                  $autocomplete.empty();
      
                  if (val.length > options.minLength) {
      
                      timeout = setTimeout(function () { // comment this line to remove timeout
                          runningRequest = true;
      
                          request = $.ajax({
                              type: 'GET',
                              url: options.ajaxUrl + val,
                              success: function (data) {
                                  if (!$.isEmptyObject(data)) { // (or other) check for empty result
                                      var appendList = '';
                                      for (var key in data) {
                                          if (data.hasOwnProperty(key)) {
                                              var li = '';
                                              if (!!data[key]) { // if image exists as in official docs
                                                  li += '<li><img src="' + data[key] + '" class="left">';
                                                  li += "<span>" + highlight(key, val) + "</span></li>";
                                              } else {
                                                  li += '<li><span>' + highlight(key, val) + '</span></li>';
                                              }
                                              appendList += li;
                                          }
                                      }
                                      $autocomplete.append(appendList);
                                  }else{
                                      $autocomplete.append($('<li>No matches</li>'));
                                  }
                              },
                              complete: function () {
                                  runningRequest = false;
                              }
                          });
                      }, 250);        // comment this line to remove timeout
                  }
              });
      
              $(document).click(function () { // close ul if clicked outside
                  if (!$(event.target).closest($autocomplete).length) {
                      $autocomplete.empty();
                  }
              });
          }
      }
      

      我没有将结果一个一个地附加到自动完成小部件,而是将它们全部附加到一个单独的长字符串中,以加快处理速度。 (阅读精彩解析jQuery .append() 方法here)。

      【讨论】:

        【解决方案6】:

        我能够让https://github.com/devbridge/jQuery-Autocomplete 使用 Materialise CSS。它似乎得到了积极的维护,并且具有一些不错的功能,例如客户端的搜索缓存,而且速度非常快。

        实现比我尝试的其他库更简单、更流畅。

        HTML 元素:

        <div class="row">
          <div class="input-field col s12">
            <i class="material-icons prefix">textsms</i>
            <input id="autocomplete-input" type="text" class="validate">
            <label for="autocomplete-input">Autocomplete</label>
          </div>
        </div>
        

        在 HTML 文件底部的 &lt;script&gt;&lt;/script&gt; 元素中:

        $("#autocomplete-input").devbridgeAutocomplete({
          serviceUrl:"/api/url",
          // Called when an item is selected from the list of results
          onSelect: function (selected) {
            console.log('You selected: ' + selected.value + ', ' + selected.data);
          },
          showNoSuggestionNotice: true,
          noSuggestionNotice: 'Sorry, no matching results',
        });
        

        默认情况下,请求为GET,查询字符串为query=&lt;value of input field&gt;,您的API 应返回JSON 对象中的数据数组作为名为@9​​87654328@ 的元素的值:

        {
          "suggestions": ["Java", "Javascript"]
        }
        

        完整文档:https://www.devbridge.com/sourcery/components/jquery-autocomplete/

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-15
          • 2011-09-17
          • 1970-01-01
          • 2020-04-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多