Javascript – Hướng dẫn tạo Autocomplete Dropdown

Đã bao giờ các bạn tự hỏi làm thế nào để làm được 1 AutoComplete dropdown xịn như của Jquery chưa? Hôm nay mình sẽ hướng dẫn các bạn làm điều đó

Javascript – Vì sao phải sử dụng Autocomplete dropdown? Với 1 list tầm 100 items thì bạn dùng Dropdown thì hợp lý.

Nhưng lớn hơn thì chẳng lẽ người dùng phải đi cuộn cắm đầu xuống tìm cho ra item đó sao? Quá ko hợp lý. Khi đó chúng ta sẽ phải nghĩ tới trường hợp cho người dùng gõ vào và auto Suggest cho người ta những item theo ý của người dùng.

Nào bắt tay vào làm việc nhé

I. Chuẩn bị:

  1. Jquery
  2. 1 File Js
  3. 1 File Css

II. Coding:

Coding JS

(function ($) {
    $.fn.extend({
        tAutoComplete: function (options) {
            var that = $(this);
            var _countPaging = 0;
            var _countPagingSearch = 0;
            var _combineId = that.attr("id");
            var _divCoverId = 'dvCover' + _combineId;
            var _divTestId = 'dvTest' + _combineId;
            var _divCover = null;
            var _divTest = null;
            var _foundedItems = new Array();
            var _coverDivTop = 0;
            var _coverDivLeft = 0;
            var _coverDivWidth = that.outerWidth();
            var _body = $("body");
            var _showMoreElement = $('<p class="TShowMore">MORE...</p>');
            var _lowerArray = $.map(options.source, function (n, i) { return htmlDecode(n.toLowerCase()); });

            //Settings list and the default values
            var defaults = {
                minChar: 60,
                maxLength: 10,
                numberOfRecords: 100,
                maxHeightCss: 250,
                source: new Array(),
                // select: function (index, item) { },
            };

            // extend the options from pre-defined values:
            var defaults = $.extend({
                select: function () { }
            }, arguments[0] || {});

            var options = $.extend(defaults, options);

            that.click(function (e) {
                _coverDivTop = $(this).offset().top + $(this).outerHeight() + 1;
                _coverDivLeft = $(this).offset().left;
                var val = that.val().toLowerCase();;

                if (val.length > 0) {
                    searchStringInArray(val);
                } else {
                    GenerateParentDiv();
                }
                _divCover = _body.find("#" + _divCoverId);
            });

            that.blur(function () {
                //// Check item exist in array
                var t = '';
                clearTimeout(t);
                delay(function () {
                    var val = that.val().toLowerCase();
                    //alert(val);
                    var index = $.inArray(val, _lowerArray);
                    //alert(index);
                    if (index == -1) {
                        that.val('');
                    }
                }, 20);
            });


            that.keyup(function (e) {
                // Show list item
                if (!~$.inArray(e.which, [13, 27, 38, 40, 37, 39])) {
                    var val = that.val().toLowerCase();

                    // Set Time after input
                    var t = '';
                    if (val.length >= options.minChars) {
                        clearTimeout(t);
                        delay(function () {
                            searchStringInArray(val);
                        }, 200);
                    } else {
                        //that.last_val = val;
                        //that.sc.hide();
                    }
                }
            });

            that.keydown(function (e) {
                if (~$.inArray(e.which, [38, 40, 13, 9])) {
                    if (event.keyCode == 38)
                    {
                        // Get current index of TSelected
                        _divCover = _body.find("#" + _divCoverId);
                        var $current = _divCover.find(".TSelected").first();
                        $current = $current.prev();
                        SetSelectedForItem(_divCover, $current);
                    }

                    if (event.keyCode == 40)
                    {
                        // Get current index of TSelected
                        _divCover = _body.find("#" + _divCoverId);
                        var $current = _divCover.find(".TSelected").first();
                        $current = $current.next();
                        SetSelectedForItem(_divCover, $current);
                    }

                    if (event.keyCode == 13 || event.keyCode == 9)
                    {
                        _divCover = _body.find("#" + _divCoverId);
                        var $current = _divCover.find(".TSelected").first();
                        if ($current.length > 0) {
                            that.val($current.text());
                            _divCover.hide();

                            if ($.isFunction(options.select)) {
                                options.select.call(this, that, { value: $(this).text() });
                            }
                        }
                    }
                }
            });

            $(document).mouseup(function (e) {
                var divCover = _body.find("#" + _divCoverId);
                if (!divCover.is(e.target) // if the target of the click isn't the container...
                    && divCover.has(e.target).length === 0) // ... nor a descendant of the container
                {
                    divCover.hide();
                }
            });

            function GenerateParentDiv() {
                var _topItems = options.source.slice(0, options.numberOfRecords);
                var _countItem = options.source.length / options.numberOfRecords;
                var parent = $('<div id="' + _divCoverId + '" style="position:absolute; top:' + _coverDivTop + 'px; left:' + _coverDivLeft + 'px; width:' + (_coverDivWidth - 2) + 'px; background:white; border:1px solid #666;max-height:' + options.maxHeightCss + 'px; overflow-y: auto; overflow-x: hidden; z-index:1000;"></div>');
                if (!_body.find("#" + _divCoverId).length > 0) {

                    // Reset value to default
                    _countPaging = 0;

                    // Append TOP element
                    $.each(_topItems, function (index, item) {
                        AppendItemToList(parent, item);
                    });

                    // Append show more link
                    var top = 0 + options.numberOfRecords;
                    if (_topItems.length > 0 && (options.source.length - top > 0)) {
                        AppendShowMoreItem(parent, _countPaging);
                    }


                    _body.append(parent);
                    _divCover = parent;
                    if (_topItems.length > 0) {
                        ShowDataDiv(parent);
                        // Focus to first item
                        FocusToFirstItem();

                    } else {
                        parent.hide();
                    }
                } else {
                    // Reset value to default
                    _countPaging = 0;

                    _divCover = _body.find("#" + _divCoverId);

                    _divCover.empty();

                    // Append TOP element
                    $.each(_topItems, function (index, item) {
                        AppendItemToList(_divCover, item);
                    });

                    // Append show more link
                    // Append show more link
                    var top = 0 + options.numberOfRecords;
                    if (_topItems.length > 0 && (options.source.length - top > 0)) {
                        AppendShowMoreItem(_divCover, _countPaging);
                    }

                    if (_topItems.length > 0) {
                        ShowDataDiv(_divCover);
                        // Focus to first item
                        FocusToFirstItem();
                    }
                }
            }

            function ShowMore(divTest) {
                _countPaging++;
                AppendMoreItem(divTest, _countPaging);
            }

            function AppendShowMoreItem(divTest, count) {
                var showMoreLink = _showMoreElement;
                showMoreLink.click(function () {
                    var showMoreElement = $(this);
                    $(this).remove();
                    ShowMore(divTest);
                });
                divTest.append(showMoreLink);
            }

            function AppendMoreItem(divTest, count) {
                var bottom = count * options.numberOfRecords + 1;
                var top = bottom + options.numberOfRecords;
                var topItems = options.source.slice(bottom, top);
                // Append all element
                $.each(topItems, function (index, item) {
                    AppendItemToList(divTest, item);
                });
                if (topItems.length > 0 && (options.source.length - top > 0)) {
                    AppendShowMoreItem(divTest, count);
                }
                that.focus();
            }

            function AppendItemToList(divTest, item) {
                var currentItem = $('<p class="THoverAble">' + item + '</p>');
                currentItem.click(function () {
                    divTest.find(".TSelected").removeClass("TSelected");
                    $(this).addClass('TSelected');
                    that.val($(this).text());
                    divTest.hide();

                    if ($.isFunction(options.select)) {
                        options.select.call(this, that , { value: $(this).text() });
                    }
                });
                divTest.append(currentItem);
            }

            function searchStringInArray(str) {
                // Reset all value to default
                _countPagingSearch = 0;
                _foundedItems = new Array();
                var divTest = _body.find("#" + _divCoverId);
                if (divTest.length < 1) {
                    // Create div Test
                    var parent = $('<div id="' + _divCoverId + '" style="position:absolute; top:' + _coverDivTop + 'px; left:' + _coverDivLeft + 'px; width:' + (_coverDivWidth - 2) + 'px; background:white; border:1px solid #666;max-height:' + options.maxHeightCss + 'px; overflow-y: auto; overflow-x: hidden; z-index:1000;"></div>');
                    _body.append(parent);
                    divTest = parent;
                    _divCover = parent;
                } else {
                    _coverDivTop = that.offset().top + that.outerHeight() + 1;
                    _coverDivLeft = that.offset().left;
                    // Update css
                    divTest.css("top", _coverDivTop + "px");
                    divTest.css("left", _coverDivLeft + "px");
                }

                divTest.empty();
                for (var j = 0; j < _lowerArray.length; j++) {
                    var text = _lowerArray[j];
                    var resultTest = text.indexOf(str) > -1;
                    if (resultTest) {
                        _foundedItems.push(options.source[j]);
                    }
                }

                var count = 0;
                var bottom = count * options.numberOfRecords;
                var top = bottom + options.numberOfRecords;
                var topItems = _foundedItems.slice(bottom, top);

                $.each(topItems, function (index, item) {
                    AppendItemToList(divTest, item);
                });
                if (topItems.length > 0 && (_foundedItems.length - top > 0)) {
                    AppendShowMoreItemSearch(divTest, count);
                }

                if (topItems.length > 0) {
                    ShowDataDiv(divTest);
                    FocusToFirstItem();
                } else {
                    divTest.hide();
                }
            }

            function AppendShowMoreItemSearch(divTest, count) {
                var showMoreLink = _showMoreElement;
                showMoreLink.click(function () {
                    var showMoreElement = $(this);
                    $(this).remove();
                    ShowMoreSearch(divTest);
                });
                divTest.append(showMoreLink);
            }

            function ShowMoreSearch(divTest) {
                _countPagingSearch++;
                AppendMoreItemSearch(divTest, _countPagingSearch);
            }

            function AppendMoreItemSearch(divTest, count) {
                var bottom = count * options.numberOfRecords + 1;
                var top = bottom + options.numberOfRecords;
                var topItems = _foundedItems.slice(bottom, top);
                // Append all element
                $.each(topItems, function (index, item) {
                    AppendItemToList(divTest, item);
                });
                if (topItems.length > 0 && (_foundedItems.length - top > 0)) {
                    AppendShowMoreItemSearch(divTest, count);
                }
                that.focus();
            }

            function FocusToFirstItem() {
                _divCover = _body.find("#" + _divCoverId);
                _divCover.find(".THoverAble").removeClass("TSelected");
                var first = _divCover.find(".THoverAble").first();
                first.addClass("TSelected");
            }

            function ShowDataDiv(divData) {
                divData.show();
                // Set css
                divData.css({ "top": _coverDivTop + "px", "left": _coverDivLeft + "px" });
                $(divData).scrollTop(0);
            }

            function SetSelectedForItem(divCover, currentItem) {
                currentItem = $(currentItem);
                divCover = $(divCover);
                if (currentItem.length > 0 && currentItem.hasClass("THoverAble")) {
                    divCover.find(".TSelected").removeClass("TSelected");
                    currentItem.addClass("TSelected");
                    that.val(currentItem.text());
                    $(divCover).scrollTop(currentItem.index() * currentItem.outerHeight() - (currentItem.outerHeight() + 10));
                }
            }

            function htmlDecode(value) {
                return $('<div/>').html(value).text();
            }
        }
    });
})(jQuery);

Coding CSS

.TDropdownDiv{
    position:relative;
}

.TDropdownUL{
    position:absolute;
    width:99%;
    display:none;
    border-left: 1px solid #006498;
    border-right: 1px solid #006498;
    border-bottom: 1px solid #006498;
    background:white;
    z-index:999;
    max-height:250px;
    overflow-y:scroll;
}

.TDropdownUL li {
    list-style:none;
    position:relative;
    padding-left:3px;
    color:black;
}


.TDropdownUL li:hover{
    background: #3399ff;
    color:white;
}

.TDropdownUL li.TSelected{
    background: #3399ff;
    color:white;
}

.TDropdownUL li.Li_Header{
    background: #cacaca;
    color:#666;
    font-weight:bold;
}

.TDropdownButton{
    border:none;
    position:absolute;
    width:17px !important;
    height:18px;
    top:1px;
    right:3px;
    background: url('Images/btDropdown.png') no-repeat;
}



.LiValue{
    display:block;
}

.LiValue:hover{
    cursor:default;
}

.THoverAble{
    padding:0px 2px;
}

.TShowMore{
    color:blue;
    text-align:right;
    cursor:pointer;
	text-decoration:underline;
	text-transform:lowercase;
	padding-right: 10px;
}

Tạo dữ liệu mẫu

$(document).ready(function(){
        subNames = new Array();
        @foreach (var item in Model.Subdivisions)
        {
            @:subNames.push('@item.SubName');
        }

        $("#SubName").tAutoComplete({
            minChars:0,
            minLength: 0,
            source: subNames,
            numberOfRecords: 200,
            maxHeightCss: 250,
            select: function (object, data) {
                var _this = $(object);
                //DoMoreThingWith(_this);
            }
        });
});

 

Chạy và xem kết quả

 

F G+ T

tuandph

Khởi đầu với .NET từ năm 2013 đến nay. Hiện tại mình đang làm full-stack developer. Yêu thích lập trình & chia sẽ kiến thức. Thời gian rảnh thường làm những tool vui vui và viết lách kể lệ sự đời.

  • tuandph says:

    Các bạn thấy bài viết thế nào? 🙂

  • Leave a Reply

    We're glad you have chosen to leave a comment. Your email address is required but will not be published.
    • Không nói tục
    • Không chửi bậy
    • Comment ko hài hước không support