【问题标题】:Select2 performance for large set of items大量项目的 Select2 性能
【发布时间】:2013-02-09 01:36:44
【问题描述】:

我正在使用带有 twitter 引导程序的 select2 jquery 插件。它适用于较少数量的项目。但是当列表很大(超过 1500 项)时,它真的会变慢。它在 IE 中最慢。

Normal Dropdownlist 可以非常快速地处理超过 1500 个项目。这种情况有什么解决办法吗?

【问题讨论】:

  • 使用 AJAX :) 而不是加载整个数据
  • 顺便说一句:我觉得这被格式化为一首诗。请使用 Shift 键?

标签: jquery jquery-select2


【解决方案1】:

即使在 IE8 中,您也可以通过对建议进行分页来使这项工作变得更好,

代码:

// Function to shuffle the demo data
function shuffle(str) {
  return str
    .split('')
    .sort(function() {
      return 0.5 - Math.random();
  })
.join('');
}

// For demonstration purposes we first make
// a huge array of demo data (20 000 items)
// HEADS UP; for the _.map function i use underscore (actually lo-dash) here
function mockData() {
  return _.map(_.range(1, 20000), function(i) {
    return {
      id: i,
      text: shuffle('te ststr ing to shuffle') + ' ' + i,
    };
  });
}
(function() {
  // init select 2
  $('#test').select2({
    data: mockData(),
    placeholder: 'search',
    multiple: true,
    // query with pagination
    query: function(q) {
      var pageSize,
        results,
        that = this;
      pageSize = 20; // or whatever pagesize
      results = [];
      if (q.term && q.term !== '') {
        // HEADS UP; for the _.filter function i use underscore (actually lo-dash) here
        results = _.filter(that.data, function(e) {
          return e.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0;
        });
      } else if (q.term === '') {
        results = that.data;
      }
      q.callback({
        results: results.slice((q.page - 1) * pageSize, q.page * pageSize),
        more: results.length >= q.page * pageSize,
      });
    },
  });
})();

这里有 20000 个项目的工作示例: http://embed.plnkr.co/db8SXs/preview

plnkr embed 不支持 IE8,因此请使用以下链接在 IE8 上试用: http://run.plnkr.co/plunks/db8SXs/

【讨论】:

  • 这是一个很好的解决方案。有一个问题,即屏幕上可能同时出现 200 个下拉菜单,并且下拉菜单中有超过 x 个项目会导致 lonnnnnnggg 页面加载。这能够将其缩短到几秒钟而不是 2 分钟。
  • 感谢您分享您的代码。如果您还在,我如何选择默认值?不幸的是,与以前不同,在您的实例化代码之后运行$('#e24').select2('val', ["150"]); 不起作用。
  • 如果在初始化 select2 之前设置值是可能的,例如$('#e24').val([firstSelectedValue, secondSelectedValue) 并根据 select2 文档在 select2 init 中添加 initSelection : function (element, callback) { var data = []; $(element.val().split(",")).each(function () { data.push({id: this, text: this}); }); callback(data); },。我无法让它与例如val : ["1", "2"] 虽然在初始化程序中。更新工作 plnkr:http://embed.plnkr.co/db8SXs/preview
  • 查看我的答案以更新此代码,它甚至在optgroup中搜索
  • plnkr 示例现已更新并正在运行,注意:仅在 Select2 v3 中
【解决方案2】:

我知道这是一个老问题,但我想分享对我有用的方法。如果您必须预加载大列表(取决于您是从头开始还是在其他人的代码上构建,这可能会更容易),请使用minimumInputLength,如here 中所述文档。在用户输入几个字符之前,不会显示巨大的选项列表。当实际选择 Select2 下拉菜单时,这大大降低了渲染它们时的性能损失。希望对您有所帮助!

【讨论】:

  • 唯一的缺点是我的一些用户喜欢向下滚动列表,然后选择一些彼此相邻的项目。您可以通过设置closeOnSelect:false 来允许此操作。如果你使用minimumInputLength,用户不能一次选择多个项目,一次只能选择一个。
  • 想补充一点,如果页面有很多带有大列表的下拉菜单,页面加载仍然会很慢
【解决方案3】:

这是 Select2 v4 的工作版本

基于答案 here: 并对其进行修改以使搜索与 lo-dash 一起使用

$(function () {
    items = []
    for (var i = 0; i < 1000; i++) {
        items.push({ id: i, text : "item " + i})
    }

    pageSize = 50

    jQuery.fn.select2.amd.require(["select2/data/array", "select2/utils"],

    function (ArrayData, Utils) {
        function CustomData($element, options) {
            CustomData.__super__.constructor.call(this, $element, options);
        }
        Utils.Extend(CustomData, ArrayData);

        CustomData.prototype.query = function (params, callback) {

            var results = [];
            if (params.term && params.term !== '') {
              results = _.filter(items, function(e) {
                return e.text.toUpperCase().indexOf(params.term.toUpperCase()) >= 0;
              });
            } else {
              results = items;
            }

            if (!("page" in params)) {
                params.page = 1;
            }
            var data = {};
            data.results = results.slice((params.page - 1) * pageSize, params.page * pageSize);
            data.pagination = {};
            data.pagination.more = params.page * pageSize < results.length;
            callback(data);
        };

        $(document).ready(function () {
            $("select").select2({
                ajax: {},
                dataAdapter: CustomData
            });
        });
    })
});

JsFiddlehttp://jsfiddle.net/nea053tw/

编辑:小提琴改变了。

【讨论】:

  • 标签在实现自定义数据适配器后不适用于此答案。
  • 要添加标签,您需要引入标签模块并在选项中装饰适配器。 dataAdapter: utils.Decorate(CustomData, tags)
  • 感谢您的回答。我不知道这是库还是脚本的错误,但是使用您的示例,使用
【解决方案4】:

因此请记住,您正在以 &lt;option&gt;s 的形式将 >1500 个实际元素加载到页面上,这最终也会损害页面性能。正如评论中的用户建议的那样,您可以通过对将返回您的值的后端服务进行 AJAX 调用来解决性能问题。

Select2 Ajax how-to

【讨论】:

  • 我使用 ajax 调用了我的数据。数据显示良好,但问题是我无法选择任何选项。
【解决方案5】:

对我来说最简单最短的作品是:

$(".client_id").select2({
   minimumInputLength: 2
 });

您可以随意更改 minimumInputLength 的值。

这样,select2 将不必显示整个列表,而是仅在输入固定数量的字符后才会显示结果。尽管您仍然在前端代码中拥有大量列表。

另外,如果您使用的是 allowClear,那么您必须像这样声明占位符:

$(".client_id").select2({
    minimumInputLength: 2,
    allowClear: true,
    placeholder: '--Select Client--'
 });

在此处查看文档http://select2.github.io/select2

如果您的数据仍然太大并且仍然存在性能问题,请使用 Ajax 方法。 select最好不要加载太大的数据,不如使用Ajax for Select2 https://select2.org/data-sources/ajax

【讨论】:

    【解决方案6】:

    这是一个非常古老的问题和答案,即使我们有更新版本的 select2。但如果有人也试图在 optgroup 中搜索。可以试试这个解决方案。

    http://jsfiddle.net/na1zLkz3/4/

        // Function to shuffle the demo data 
    var shuffle = function (str) {
        return str.split('').sort(function () {
          return 0.5 - Math.random();
        }).join('');
      };
    
    // For demonstration purposes we first make
    // a huge array of demo data (20 000 items)
    // HEADS UP; for the _.map function i use underscore (actually lo-dash) here
    var mockData = function () {
        var array = _.map(_.range(1, 10), function (i) {
            return {
              id  : i,
              text: shuffle('te ststr ing to shuffle') + ' ' + i
            };
          });
        return array;
      };
      var mockData1 = function () {
        var array = _.map(_.range(10, 15), function (i) {
            return {
              id  : i,
              text: shuffle('te ststr ing to shuffle') + ' ' + i
            };
          });
        return array;
      };
      var mockData2 = function () {
        var array = _.map(_.range(15, 20), function (i) {
            return {
              id  : i,
              text: shuffle('te ststr ing to shuffle') + ' ' + i
            };
          });
        return array;
      };
      // create demo data
      var dummyData = mockData();
      var dummyData1 = mockData1();
      var dummyData2 = mockData2();
      dummyData.push({
      text: 'canada',
      children: dummyData1
      });
      dummyData.push({
      text: 'USA',
      children: dummyData2
      });
    
      // init select 2
      $('#ddlCar').select2({
        data             : dummyData,
        // init selected from elements value
        initSelection    : function (element, callback) {
          var initialData = [];
          $(element.val().split(",")).each(function () {
            initialData.push({
              id  : this,
              text: this
            });
          });
          callback(initialData);
        },
    
        // NOT NEEDED: These are just css for the demo data
        dropdownCssClass : 'capitalize',
        containerCssClass: 'capitalize',
    
        // NOT NEEDED: text for loading more results
        formatLoadMore   : function() {return 'Loading more...'},
    
        // query with pagination
        query            : function (q) {
          var pageSize,
            results;
          pageSize = 20; // or whatever pagesize
          var results  = [];
          if (q.term && q.term !== "") {
            // HEADS UP; for the _.filter function i use underscore (actually lo-dash) here
            var results = this.data;
            var results = _.filter(results, function (e) {
                if(typeof e.children != 'undefined')
              {
                subresults = _.filter(e.children, function (f) {
                    return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                });
                if(subresults.length > 0)
                    return true;
                return false;
              }
              return (e.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
            });
            newresults = [];
            for (var i = 0, len = results.length; i < len; i++) {
            newresults[i] = {};
            if(typeof results[i].text != 'undefined')
                newresults[i].text = results[i].text;
            if(typeof results[i].id != 'undefined')
                newresults[i].id = results[i].id;
            if(typeof results[i].children != 'undefined')
            {
                newresults[i].children = results[i].children;
                newresults[i].children = _.filter(newresults[i].children, function (f)                          {
                    return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                });
            }
          }
          results = newresults;
          } else if (q.term === "") {
            results = this.data;
    
          }
    
          q.callback({
            results: results.slice((q.page - 1) * pageSize, q.page * pageSize),
            more   : results.length >= q.page * pageSize
          });
        }
      });
    

    【讨论】:

    • 我们需要的英雄?
    【解决方案7】:

    作为另一个更新,我想分享对我有用的东西,因为为这么旧的东西寻找支持变得越来越困难。我用 npm 安装了 select 2,发现包含两个版本。默认版本require('select2') 不支持查询参数。需要完整版本require('select2/dist/js/select2.full'),然后使用此处显示的代码对我使用 select 2 v4,data() 返回 16k 城市的列表

           $(".select_2_cities").select2({
                theme: "bootstrap",
                data: data(),
                multiple: true,
                query            : function (q) {
                  var pageSize,
                    results;
                  pageSize = 20; // or whatever pagesize
                  var results  = [];
                  if (q.term && q.term !== "") {
                    // HEADS UP; for the _.filter function i use underscore (actually lo-dash) here
                    var results = this.data;
                    var results = _.filter(results, function (e) {
                        if(typeof e.children != 'undefined')
                      {
                        subresults = _.filter(e.children, function (f) {
                            return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                        });
                        if(subresults.length > 0)
                            return true;
                        return false;
                      }
                      return (e.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                    });
                    newresults = [];
                    for (var i = 0, len = results.length; i < len; i++) {
                    newresults[i] = {};
                    if(typeof results[i].text != 'undefined')
                        newresults[i].text = results[i].text;
                    if(typeof results[i].id != 'undefined')
                        newresults[i].id = results[i].id;
                    if(typeof results[i].children != 'undefined')
                    {
                        newresults[i].children = results[i].children;
                        newresults[i].children = _.filter(newresults[i].children, function (f)                          {
                            return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                        });
                    }
                  }
                  results = newresults;
                  } else if (q.term === "") {
                    results = this.data;
                  }
                  q.callback({
                    results: results.slice((q.page - 1) * pageSize, q.page * pageSize),
                    more   : results.length >= q.page * pageSize
                  });
                }
            });

    【讨论】:

      猜你喜欢
      • 2013-03-18
      • 2019-11-10
      • 2019-05-11
      • 2023-03-03
      • 2016-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多