如何使用带有ajax的物品自动完成插件?

时间:2016-08-01 02:41:05

标签: jquery ajax autocomplete materialize

我正在努力使materializecss autocomplete plugin与我的ajax调用一起工作,以便根据输入字段中输入的内容动态加载数据。

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

然后,我将自动完成功能放在ajax的成功函数和键的值#34;数据"是之前构建的对象数组。

似乎我的方式很好但是当我在浏览器中进行测试时,每次输入内容时,建议下拉列表会显示预期的结果,而不是在每次keydown后更新,另一个下拉列表与前一个重叠,因此只有一个......

所以这就是我的问题:如何避免下拉建议列表重叠,而不是每次按一次键都要更新?

感谢您的帮助。



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);
        }

       }) 
		
&#13;
&#13;
&#13;

6 个答案:

答案 0 :(得分:3)

在这里,一个更清洁的例子。

  • 它基于Materialized.js原始功能
  • 在您输入时取消现有的ajax请求,这样您就不会获得双重结果
  • 如果您删除超时&#39;注释行,这只会在&#39; x&#39;之后调用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
    });
  }
}

答案 1 :(得分:3)

在@ friek108的优秀答案之上,让我们添加以下功能。

  • 点击外部时关闭自动填充小部件。
  • 使用箭头键滚动结果并使用回车键选择。
  • 仅在输入预定义的最小字符数后才进行AJAX调用。
  • 停止某些键触发AJAX调用。

这采用了超时&amp; ajax从@ friek108的答案中调用取消功能。您可能想先检查一下。

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)的精彩分析。

答案 2 :(得分:1)

派对有点晚,但认为这可能会帮助一些人在同样的问题上挣扎。

我找到的一种方法是制作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>

答案 3 :(得分:1)

我能够https://github.com/devbridge/jQuery-Autocomplete使用Materialize 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文件底部的<script></script>元素中:

$("#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=<value of input field>,并且您的API应该在JSON对象中返回数据数组作为名为suggestions的元素的值:

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

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

答案 4 :(得分:0)

您的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自动完成功能,因为使用它的人数比您使用的更多,您可以获得更好的帮助。

答案 5 :(得分:0)

我解决了这个问题。

<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>

注意文件&#34; default.js&#34;在图书馆的中间。

然后在&#34; default.js&#34;

$.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) {
    }
});