一个jQuery搜索框,搜索结果与输入不匹配

时间:2012-02-28 13:37:31

标签: jquery ajax json delay

首先,标题是我能想到的最佳标题。

我写了一个很短的小脚本来进行实时搜索。

它的基础是:

(function ($) {
    $.fn.SearchThing = function (options) {
        options = $.extend({
            MaxCount: 5,
            inputField: '#search_input',
            sugestBox: '#search_sugest'
        }, options);

        var input = $(options.inputField);
        var sugest = $(options.sugestBox);
        sugest.hide();

        input.keyup(function (e) {
            switch (e.keyCode) {
                //Other keys.
                default:
                    queryServer(this.value);
            }
        });

        function queryServer(value) {
            var url = "/search/get?query=" + value; // value;
            $.getJSON(url, function (result) {
                if (result.Results < 1) {
                    sugest.hide();
                    return;
                }

                sugest.children().each(function (idx, itm) {
                    $(itm).remove();
                });
                sugest.show();

                //Build box.
            }
        };
    };
})(jQuery);

这几乎可以正常工作。但是有两件事需要。一个不是那么重要,另一个实际上是一个bug,可能会被另一个解决,但我不知道。但他们在这里:

BUG:结果并不总是匹配“输入字段”

中的内容

这意味着如果我输入“ASD”它会搜索“ASD”,所以如果我说输入“ASD”后跟一个快速的“退格”,它应该搜索“AS”,但是现在然后它似乎最终得到了“ASD”的结果。

我知道这是因为我目前还没有实现搜索后端,所以我返回一个静态列表,其中第一个元素是搜索词。

我认为这可能是因为搜索“AS”比搜索“ASD”更早返回,因此结果按顺序应用 - &gt; “A”,“AS”,“AS”,“ASD”,应按“A”,“AS”,“ASD”,“AS”的顺序应用......

如果我输入“ASD”,它也会按顺序应用 - > “A”,“ASD”,“AS”。 (同样的事情是,在搜索“AS”之前搜索“ASD”)

注意:像“ASD”这样的简短搜索字词很少会触发此错误,更快速输入更长的搜索条件就更容易这样做了,我在这里使用短文来做出描述问题更容易。

有人能建议一个好的解决方案吗?

DESIRED:提交搜索前的小延迟,如果击键很快,则仅处理最后一个“值”。

这可能实际上解决了上述问题,实际上我很有可能这样做。

我很快就看到了“延迟”,但由于它无法“取消”执行(据我所知),它需要配置实施,因为我看到它,但也许有人有一个很棒的想法那部分。

否则,是否有人对如何做到这一点有任何其他想法?

3 个答案:

答案 0 :(得分:1)

所有这一切的原因是请求 - 响应需要比下一次按键更长的时间,因此先前的结果会显示出来。

我认为你应该能够用一个技巧修复这两个“问题”:

var delayTime = 500;
var lastTimeout = null;
var input = $(options.inputField);
var sugest = $(options.sugestBox);
sugest.hide();

input.keyup(function (e) {
    switch (e.keyCode) {
        //Other keys.
        default:
            if (lastTimeout != null) {
                clearTimeout(lastTimeout);
            }
            lastTimeout = setTimeout('queryServer("'+this.value+'")', delayTime);
    }
});

function queryServer(value) {
    lastTimeout = null;
    // ... all the rest as before ...
};

这将在您停止输入后显示建议仅0.5秒(或暂停这么长时间)。

答案 1 :(得分:1)

基于mkilmanas的awnser,这是有用的:

if (lastTimeout != null) {
    window.clearTimeout(lastTimeout);
}

var value = this.value;
lastTimeout = window.setTimeout(function () {
    queryServer(value);
}, 300); //Choose a bit shorter timeout since 500 actually gave a "clunky" feeling.

除了细节之外,如果按顺序提交搜索“A”然后“AB”(因为我们打字很慢),如果“A”需要1000ms才能执行,但“AB”只需要100ms然后“A” “返回的时间晚于”AB“,同样的问题出现了。

我可能通过从服务器返回原始查询来解决这个问题,然后添加以下检查:

        var url = "/search/get?query=" + value; // value;
        $.getJSON(url, function (result) {
            if (result.Results < 1) {
                sugest.hide();
                return;
            }
            if (input.val() != result.Query) {
                return;
            }

这应该过滤掉与之后返回的查询相比延迟的结果,结果更快,我无法想到这样做的任何其他含义。

答案 2 :(得分:1)

Eduardo Molteni的完整资源,请注意,这是针对我的网站构建方式以及服务器如何响应查询而定制的,所以它很可能只是服务器作为灵感。

另请注意,它会对HTML提出要求。

下面的代码中“MyArea”背后的想法是因为我删除了我在其中执行搜索的区域并将其替换为单个“MyArea”,这是为了表明它支持显示一组分组的结果。 / p>

应该为你想要的每个区域调用appendSection,这就是你要构建你想要的HTML的地方,注意我作为一个例子有一个“标题”等你可能不会'那个。

// Copyright 2012 Jens Melgaard, www.dotjem.com
// Licensed under the Apache License, Version 2.0 (the "License");
// http://www.apache.org/licenses/LICENSE-2.0
(function ($) {
    $.fn.dotJEMSearch = function (options) {
        options = $.extend({
            inputField: '#search_input',
            sugestBox: '#search_sugest'
        }, options);

        var timeout = null;
        var input = $(options.inputField);
        var sugest = $(options.sugestBox);
        var current = -1;
        var displayed = 0;
        var items = [];

        sugest.hide();

        input.blur(function () { sugest.hide(); });
        input.focus(function () { if (displayed > 0) { sugest.show(); } });
        input.keydown(function (e) { if (e.keyCode == 13) e.preventDefault(); });
        input.keyup(function (e) {
            switch (e.keyCode) {
                case 13: //Return Key
                    e.preventDefault();
                    if (current != -1) {
                        window.location.href = items[current].Url;
                    } else {
                        window.location.href = '/search?query=' + this.value;
                    }
                    return false;

                case 38: //up key
                    selectItem(current - 1);
                    return false;

                case 40: //up key
                    selectItem(current + 1);
                    return false;

                default:
                    if (timeout != null) {
                        window.clearTimeout(timeout);
                    }

                    var value = this.value;
                    timeout = window.setTimeout(function () {
                        queryServer(value);
                    }, 200);
            }
        });

        function queryServer(value) {
            timeout = null;

            var url = "/search/get?query=" + value;
            $.getJSON(url, function (result) {
                items = [];
                displayed = 0;
                if (result.Results < 1) {
                    sugest.hide();
                    return;
                }
                if (input.val() != result.Query) {
                    return;
                }

                sugest.children().each(function (idx, itm) {
                    $(itm).remove();
                });
                sugest.show();
                appendSection('Search Results', result.MyArea);
            });
        };

        function appendSection(heading, elements) {
                    if (elements.Count > 0) {
            var show = elements.Items.length;
            sugest.append('<h1>' + heading + '<em>' + show + ' of ' + elements.Count + '</em></h1>');

            var elm = $('<ul>');
            for (var i = 0; i < show; i++) {
                var item = elements.Items[i];
                var listElm = $('<li><a href="' + item.Href + '">' + item.Title + '</a></li>');

                (function (index) {
                    listElm.hover(function () { selectItem(index); }, function () { deselect(); });
                })(displayed);
                items[displayed++] = { Elm: listElm, Url: item.Href };
                elm.append(listElm);
            }
            sugest.append(elm);
        };

        function selectItem(idx) {
            deselect();
            current = (idx + items.length) % items.length;
            $(items[current].Elm).addClass('active');
        };

        function deselect() {
            if (current < 0)
                return;
            $(items[current].Elm).removeClass('active');
            current = -1;
        };

    };
})(jQuery);

HTML片段

                <form id="search" class="search" name="search_form">
                    <input type="text" name="query" autocomplete="off" id="search_input"/>
                    <div id="search_sugest" style="display: none;">
                    </div>
                </form>

    <!-- somewhere at the bottom-->
    <script type="text/javascript">
        $(window).load(function () {
            $('.search').dotJEMSearch();
        });
    </script>

下一部分是“JSON”格式的结构。在我的例子中,这是由HTTP处理程序服务器端(ASP.NET)返回的。

@"{  ""Results"": 33,
     ""Query"": """ + query + @""",
     ""MyArea"": {
         ""Count"": 21,
         ""Items"": [
       { ""Title"": ""One"", ""Desc"": ""description"", ""Href"": ""/link"" },
       { ""Title"": ""Two"", ""Desc"": ""description"", ""Href"": ""/link"" },
       { ""Title"": ""Three"", ""Desc"": ""description"", ""Href"": ""/link"" },
       { ""Title"": ""Four"", ""Desc"": ""description"", ""Href"": ""/link"" },
       { ""Title"": ""Five"", ""Desc"": ""description"", ""Href"": ""/link"" }
                    ]
               }
  }";