同福

前端开发之自动完成下拉菜单AJAX版【20210806】

介绍

介绍

我们打开百度首页,在搜索输入框内输入一个关键词,会发现在输入框下面会出现一个下拉菜单,里面是一些包含我们输入的关键词的搜索关键词,下拉菜单里的关键词可以通过键盘的上下键选择或者直接通过鼠标点击选择,大大提高了我们的使用体验。

这种功能就是自动完成的功能,其实浏览器本身就支持这种自动完成功能。不过,浏览器的自动完成功能是以你的操作历史为依据推荐的,也就是说如果你没有操作过也就不会有推荐选项了。

为了解决这个问题,福哥自己提供JQ控件的方式封装了一个自动完成的控件autoComplete,和网上可以搜索到的一些自动完成控件不同之处在于福哥的这个autoComplete控件是AJAX版的,也就是说福哥的这个autoComplete控件的下拉菜单的关键词可以通过后天接口提供,是不是很棒呀~~

JQ的autoComplete控件

下面福哥就给出这个autoComplete控件的源代码,大家看看吧~~

JavaScript

(function ($) {
    var a = {
        b: null,
        c: null,
        d: null,
        e: null,
        f: null,
        start: function () {
            this.aa(this.b);
        },
        aa: function (ab) {
            var ex = this;
            if (!ab.attr('tf-autoComplete-event')) {
                ab.attr('tf-autoComplete-event', "done");
                ab.bind('mouseup', function () {
                    ex.ac();
                });
                ab.bind('blur', function () {
                    ex.ad();
                });
                ab.bind('keyup', function () {
                    ex.ae();
                });
            }
        },
        ac: function () {
            this.af(this.b);
        },
        ad: function () {
            this.ba();
        },
        ae: function (e) {
            e = window.event || e;
            if (e.keyCode == 38 || e.keyCode == 40) {
                if (e.keyCode == 38) {
                    this.bb(this.b, this.c, "desc");
                } else if (e.keyCode == 40) {
                    this.bb(this.b, this.c, "asc");
                }
            } else if (e.keyCode == 13) {
                this.bc(this.b, this.c);
            } else {
                this.af(this.b);
            }
        },
        af: function (ab) {
            if ($.trim(ab.val()) != "") {
                this.bd();
                this.be();
            } else {
                this.ba();
            }
        },
        bd: function () {
            if (this.c == null) {
                this.b.parent().css({position: "relative"});
                this.c = $(document.createElement('DIV'));
                this.c.addClass('tfhtml-autocomplete');
                this.c.css({
                    width: this.b.outerWidth(),
                    marginTop: this.b.position().top + this.b.outerHeight(),
                    marginLeft: this.b.position().left,
                    minHeight: "300px"
                });
                this.b.attr({autoComplete: "off"});
                this.b.after(this.c);
            }
        },
        be: function () {
            var ex = this;
            if (this.e != null) {
                clearTimeout(this.e);
            }
            this.e = setTimeout(function () {
                ex.bf(ex.b);
            }, 300);
        },
        ba: function () {
            if (this.c != null) {
                var ex = this;
                if (this.f != null) {
                    clearTimeout(this.f);
                }
                this.removeItv = setTimeout(function () {
                    ex.c.remove();
                    ex.c = null;
                }, 200);
            }
        },
        bf: function (ab) {
            var ex = this;
            this.ca({
                data: "keyword=" + encodeURIComponent(ab.val()), success: function (d) {
                    ex.cb(d, ex.c);
                }, error: function (d) {
                    ex.cc(d);
                }
            }, this.d.url, this.d.method, this.d.dataType);
        },
        ca: function (d, cd, ce, cf) {
            d.url = cd;
            d.method = ce;
            d.dataType = cf;
            d.processData = false;
            if (d.method == null) {
                d.method = "get";
            }
            if (d.dataType == null) {
                d.dataType = "json";
            }
            $.ajax(d);
        },
        cb: function (da, db) {
            if (da) {
                if (db != null) {
                    var ex = this;
                    db.html("");
                    for (var dc in da) {
                        db.append("<span>" + da[dc] + "</span>");
                    }
                    db.find('span').bind('mouseover', function () {
                        $(this).addClass('over');
                    });
                    db.find('span').bind('mouseout', function () {
                        $(this).removeClass('over');
                    });
                    db.find('span').bind('click', function () {
                        ex.bc(ex.b, ex.c);
                    });
                    db.show();
                }
            }
        },
        cc: function (da) {
        },
        bc: function (ab, db) {
            if (db.find('span.over').length == 1) {
                ab.val(db.find('span.over').text());
            }
            this.ba();
        },
        bb: function (ab, db, dd) {
            var de = db.find('span'), df = db.find('span.over'), ea;
            if (de.length == 0) {
                return false;
            }
            if (df.length == 0) {
                if (dd == "asc") {
                    ea = de.first();
                } else {
                    ea = de.last();
                }
                de.removeClass('over');
                ea.addClass('over');
                ab.val(ea.text());
                return true;
            }
            if (dd == "asc") {
                ea = df.next();
                if (ea.length == 0) {
                    ea = de.first();
                }
            } else {
                ea = df.prev();
                if (ea.length == 0) {
                    ea = de.last();
                }
            }
            de.removeClass('over');
            ea.addClass('over');
            ab.val(ea.text());
            return true;
        },
        init: function (b, d) {
            if (!d instanceof Object) {
                d = {};
            }
            this.b = $(b);
            this.d = d;
        }
    };
    a.init.prototype = a;
    $.fn.autoComplete = function (eb) {
        var ec = new a.init(this, eb);
        ec.start();
        return ec;
    };
})(jQuery);

前端调用

JavaScript

$('[name="keyword"]').autoComplete({
    url: "/api/resource/keyword/_autoComplete",
    method: "post"
})

讲解

JQ的autoComplete控件

要实现这个自动完成控件autoComplete需要解决几个难题:

  • 首先就是这个下拉菜单必须是位于输入框的正下方,且宽度和输入框一样。要解决这个问题,单纯的使用CSS是无法满足的,福哥在这里用到了一些JS的算法动态设置了下拉菜单的样式。

  • 其次就是触发AJAX请求的时机。如果每次用户按下键盘的按键都去请求后台接口的话,那样估计后台就被请求爆了。要解决这个问题就需要降低请求后台接口的频次,福哥想到了判断两次按下键盘的按键的时间间隔,如果间隔在一定时间范围以内则不出发AJAX请求,避免对服务器造成压力。

  • 最后一点就是下拉菜单针对键盘的上下键的响应的问题,这个就需要在输入框的keyup事件里面做文章了。如果在输入框的按下的按键是方向键那么则进行特殊处理,福哥通过一些手动地选择了下拉菜单的选择,如果是正常输入内容再去判断是否要发送AJAX请求获取菜单内容。

895c769d1a37d9ee.jpg

总结

福哥今天给大家分享了一个非常好用的自动完成控件autoComplete,使用这个控件就可以给自己的网站的用户体验加分了。

当然,福哥这里只是实现了自动完成的前端功能部分,如果要实现后台的功能还需要借助Java、PHP等后台开发语言来完成的。