Code Copied

使用bootstrap typeahead插件

1.介绍

bootstrap的typeahead插件提供了比较灵活的autocomplete功能。下面将逐步介绍typeahead插件的使用。

本文中的typeahead版本为v0.10.5,基于JQuery 1.x和bootstrap 3。

typeahead的官方主页:http://twitter.github.io/typeahead.js/

typeahead的官方Example:http://twitter.github.io/typeahead.js/examples/

本文源码和在线示例:

2. 基本示例

效果:

image

html代码:

<div class="example">
    <h2>基本示例</h2>
    <div id="basic-example">
        <input class="typeahead" type="text" placeholder="请输入省份">
    </div>
</div>

js代码:

<script type="text/javascript" src="/Assets/typeahead/typeahead.bundle.js"></script>
<script type="text/javascript">
    jQuery(function () {
        /*** 1.基本示例 ***/
        var provinces = ["广东省", "福建省", "山西省", "山东省","湖北省", "湖南省", "陕西省", "上海市", "北京市", "广西省"];

        var substringMatcher = function (strs) {
            return function findMatches(q, cb) {
                var matches, substrRegex;
                matches = [];
                substrRegex = new RegExp(q, 'i');
                $.each(strs, function (i, str) {
                    if (substrRegex.test(str)) {
                        matches.push({ value: str });
                    }
                });
                cb(matches);
            };
        };

        $('#basic-example .typeahead').typeahead({
            hint: true,
            highlight: true,
            minLength: 1
        },
        {
            name: 'provinces',
            displayKey: 'value',
            source: substringMatcher(provinces)
        });

    });

</script>

在上面的基本示例中,数据源(source属性)并没有直接使用JavaScript数组,而是通过一个function先将数据源的每个元素包装为JavaScript的object。
typeahead只接受JavaScript对象数据源。

3.Ajax示例

在当前版本的typeahead中,已经不再支持在source属性中直接调用ajax方法获取数据源了。

现在开始改用bloodhound作为typeahead的suggestion engine了,bloodhound提供了一些很优秀的功能,例如数据预读(prefetch)、智能缓存、快速查找、远程数据源(remote)。

bloodhound介绍地址:https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md

bloodhound也帮我们省去了诸如$.ajax, $.getJSON等方法,只需要指定数据源的地址和方式就可以了。

3.1 使用prefetch

在bloodhound中,数据预读是指在页面加载时typeahead的目标对象(autocomplete的文本框)所需的数据源就加载了,而不需要再文本框中输入字符时才请求。
数据预读一般适用于固定的、体积较小的数据源,例如中国的省市、电话区号。

第一次页面加载:

image

加载完成后缓存数据源

image

第2次页面加载:

image

html代码:

<div class="example">
    <h2>Ajax数据预读示例</h2>
    <div id="ajax-prefetch-example">
        <input class="typeahead" type="text" placeholder="请输入省份">
    </div>
</div>

js代码:

/*** 2.Ajax数据预读示例 ***/

// 远程数据源
var prefetch_provinces = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    // 预获取并缓存
    prefetch: '/data/provinces.json'
});

prefetch_provinces.initialize();

$('#ajax-prefetch-example .typeahead').typeahead(null, {
    name: 'provinces',
    displayKey: 'value',
    source: prefetch_provinces.ttAdapter()
});

3.2 使用remote

在使用remote时,需要添加查询参数,语法如下:

remote: 'http://example.com/animals?q=%QUERY'

由于需要根据输入的字符在服务端进行数据过滤,所以静态的json文件不能满足remote的实现需求了。下面通过构建一个简单的Ajax请求来模拟remote的实现。

添加一个AjaxController类,并提供一个GetCities方法:

public class AjaxController : Controller
{
    //
    // GET: /Ajax/
    public JsonResult GetCities()
    {
        string q = Request.Params["q"] ?? "";
        return Json(!string.IsNullOrEmpty(q) ? Cities.Where(c => c.CityName.Contains(q)).ToList() : Cities, JsonRequestBehavior.AllowGet);
    }

    public IList<City> Cities
    {
        get
        {
            IList<City> cities = new List<City>();
            cities.Add(new City {Id = 1, ProvinceName = "广东省", CityName = "广州市"});
            cities.Add(new City {Id = 2, ProvinceName = "山东省", CityName = "济南市"});
            cities.Add(new City {Id = 3, ProvinceName = "陕西省", CityName = "西安市"});
            cities.Add(new City {Id = 4, ProvinceName = "青海省", CityName = "西宁市"});
            cities.Add(new City {Id = 5, ProvinceName = "广西省", CityName = "南宁市"});
            cities.Add(new City {Id = 6, ProvinceName = "江苏省", CityName = "南京市"});
            return cities;
        }
    } 

    public class City
    {
        public int Id { get; set; }
        public string ProvinceName { get; set; }
        public string CityName { get; set; }
    }
}

请求链接:/ajax/GetCities,数据源如下:

image

html代码:

<div class="example">
    <h2>Ajax及时获取数据示例</h2>
    <div id="ajax-remote-example">
        <input class="typeahead" type="text" placeholder="请输入城市">
    </div>
</div>

js代码:

//远程数据源
var remote_cities = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('CityName'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    // 在文本框输入字符时才发起请求
    remote: '/ajax/GetCities?q=%QUERY'
});

remote_cities.initialize();

$('#ajax-remote-example .typeahead').typeahead(null, {
    name: 'cities',
    displayKey: 'CityName',
    source: remote_cities.ttAdapter()
});

运行效果:

image

通过输入法输入汉字时,文本框首先先出现的字符是汉字的拼音,然后才出现汉字,这会向服务端发起2次请求(这是一个问题)。
但是当我们再次输入字符”nan“或”南“时,typeahead不会再向服务端发送请求了,说明这2个查询已经被缓存起来了。

3.3 prefetch和remote的比较

  • prefetch在页面第一次加载时就会缓存,并且在之后的输入动作或者页面再刷新时都不会再请求数据源;
  • remote在每次加载页面时,输入字符时才会请求数据源;
  • remote在一次页面生命周期内,重复输入相同的字符只有第一次才会进行请求,后面再输入时会直接取第一次请求的缓存数据;
  • prefetch获取数据源后,会较为智能的根据文本框输入的字符进行数据源过滤;
  • remote获取数据源时需要根据请求的字符在服务端自定义数据源过滤方法。

4. 使用template

在3.2的示例中,如果希望在查询时还能显示City对应的Province,那么就需要template来实现了。

在使用template时,还需要额外引用handlebars.js文件。

html代码:

<div class="example">
    <h2>Template示例</h2>
    <div id="ajax-template-example">
        <input class="typeahead" type="text" placeholder="请输入城市">
    </div>
</div>

js代码:

/*** 4.Template示例 ***/

$('#ajax-template-example .typeahead').typeahead(null, {
    name: 'cities',
    displayKey: 'CityName',
    source: remote_cities.ttAdapter(),
    templates: {
        empty: [
            '<div class="empty-message">',
            '没有找到相关数据',
            '</div>'
        ].join('\n'),
        suggestion: Handlebars.compile('<p><strong>{{CityName}}</strong> - {{ProvinceName}}</p>')
    }
});

运行效果:

image