JQuery: поиск на основе Ajax - сбой браузера?

Эй, ребята, я работаю над довольно странной моделью поиска на основе AJAX. Поиск на самом деле не извлекает реальные результаты поиска с сервера, а скорее загружает карту сайта (с помощью метода jquery load()) и извлекает ссылки.

Это работает на самом деле очень хорошо, но есть одна маленькая ошибка, которая может привести к сбою моего браузера.

var searchTimer = 0;

$('.s').keyup(function(e) {

    switch (e.keyCode) {
        //case 8:  // Backspace
        case 9:  // Tab
        case 13: // Enter
            doSearch(e.keyCode);
            break;
        case 16: // Shift
        ...
        case 37: // Left
            break;
        case 38: // Up
            doSearch(e.keyCode);
            break;
        case 39: // Right
            break;
        case 40: // Down
            doSearch(e.keyCode);
            break;
        ...
        break;

        default:
        if (searchTimer != 0) {
            clearTimeout(searchTimer);
        }

        searchTimer = setTimeout(function () {
            doSearch(e.keyCode);
        }, 250);
    }

});

function doSearch(keyCode) {

    if ($('.s').val() != '') {

        searchTimer = 0;

        $sr.load('/sitemap/', function() {


        }); // end load

    } else {
        clearsearch(true);
    }
}

Единственная проблема, с которой я столкнулся, это то, что сайт падает, когда я набираю слово в поле ввода.s и сразу же удаляю его в течение 250 мс.

Представьте себе: 1.) вход пуст. 2.) Я быстро набираю "тест". 3.) функция doSearch даже не была запущена, и я нажал cmd-a, чтобы выделить все и удалить текст из ввода.

полный крах моего сайта!

Почему это могло случиться? Это работает действительно гладко и хорошо, когда я просто набираю "test" или удаляю ввод после того, как doSearch() был запущен. Это на самом деле работает всегда. Просто в этом редком случае быстрого набора текста и удаления набранного текста в случае doSeach() происходит сбой.

Есть идеи, что может вызвать это?

edit / update: когда я копирую sitemap.html в мою текущую корневую директорию процессов и загружаю его, он не падает и работает нормально, как в вашем примере. Как только я изменю это на url: "sitemap", dataType: "html", это падает. Я называю свою карту сайта mydomain.com/sitemap...

код для карты сайта выглядит следующим образом:

    <?php
/**
 * Template Name: Sitemap
 */
?>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>

    <div id="ajax-base">
        <h3>Pages</h3>
            <ul>
                <?php wp_list_pages('title_li=&depth=0&exclude='); ?>
            </ul>
        <h3>Posts</h3>
            <?php $first = 0;?>
            <ul>
            <?php
            $myposts = get_posts('numberposts=-1&offset=$first');
            foreach($myposts as $post) :
            ?>
            <li><a href="<?php the_permalink(); ?>#b"><?php the_title(); ?></a></li>
            <?php endforeach; ?>
            </ul>
        <h3>Categories</h3>
            <ul>
                <?php wp_list_categories('title_li=&orderby=name'); ?>
            </ul>
        <h3>Tags</h3>
            <ul>    
                <?php
                $tags = get_tags();
                foreach ($tags as $tag){
                    $tag_link = get_tag_link($tag->term_id);
                    $html .= "<li><a href='{$tag_link}#b' title='{$tag->name} Tag' class='{$tag->slug}'>";
                    $html .= "{$tag->name}</a></li>";
                }
                echo $html;
                ?>
            </ul>
    </div> <!-- ajax-base -->

<?php endwhile; endif; ?>

извините за этот последний вопрос, но любая идея, почему это имеет значение. Когда я использую эту динамическую / карту сайта в качестве основы для моего поиска, браузер падает. Со статической HTML-страницей все работает нормально.

2 ответа

Решение

Я полагаю, что основная проблема вашего кода в том, что вы не прерываете предыдущий ожидающий вызов ajax. Что происходит в браузере, если он одновременно попытается изменить $sr элемент на два ответа сервера?

Оба старые XMLHttpRequest и новый jqXHR имеет метод прерывания, который вы можете использовать.

ОБНОВЛЕНО: Как я описал в комментарии, jQuery.load не намного больше, чем вызов jQuery.ajax и jQuery.html для размещения ответа сервера на странице. Вы можете проверить это, посмотрев в исходный код jQuery.load здесь (для jQuery 1.4.4) или здесь (для jQuery 1.5.1).

Я подготовил для вас один небольшой демонстрационный пример, который показывает, как вы можете использовать jQuery.ajax и jQuery.html напрямую вместо jQuery.load. Вы можете скачать полный проект здесь.

Если кто-то медленно набирает в поле ввода демо, то получаю следующие результаты

Если один из них печатается быстрее (я набираю очень медленно и поэтому использую тайм-аут на 1 секунду на сервере):

Можно видеть, что я прерываю предыдущий запрос ajax на сервер, если существует какой-либо ожидающий запрос ajax. В случае прерывания error вызывается соответствующий (предыдущий) запрос ajax, а затем abort() функция возврата.

Я надеюсь, что если вы последуете пути, у вас никогда не возникнет проблем, которые вы описываете в своем вопросе.

Чтобы быть уверенным, что вы получите пример, я включаю полный код, который я использовал в своей тестовой демонстрации ниже. Код JavaScript следующий

var jqXHR_Old, $myinput = $('#myinput'),
    $result = $('#result'), $protocol = $('#protocol'),
    logText = function(txt) {
        $protocol.append(txt+"<br/>"); // append or prepend
    },
    doSearch = function(code) {
        var txt = $myinput.val();
        if (txt != '') {
            // send request to the server
            if (jqXHR_Old) {
                // abort the previous request
                logText("aborting...");
                jqXHR_Old.abort();
                jqXHR_Old = null;
            }

            $result.empty();
            logText('sending request to the server with '+
                    '<span style="color:blue;">'+txt+'</span>...');
            jqXHR_Old = $.ajax({
                url: "MySimpleService.svc/GetTestHtmlFragment",
                data: {str:txt},
                dataType: "html",
                success: function (data) {
                    $result.html(data);
                    logText("received from the server: "+data);
                    jqXHR_Old = null;
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    if (textStatus !== "abort" || errorThrown !== "abort") {
                        $result.html("Error Occured!" + " | " + " | " +
                                        textStatus + " | " + errorThrown +
                                        "responseText:<br/>" + XMLHttpRequest.responseText);
                    } else {
                        logText("request aborted.");
                    }
                    jqXHR_Old = null;
                }
            });
        }
    };
$myinput.keyup(function(e) {
    switch (e.keyCode) {
        //case 8:  // Backspace
        case 9:  // Tab
        case 13: // Enter
            doSearch(e.keyCode);
            break;
        case 37: // Left
            break;
        case 38: // Up
            doSearch(e.keyCode);
            break;
        case 39: // Right
            break;
        case 40: // Down
            doSearch(e.keyCode);
            break;

        default:
            doSearch(e.keyCode);
    }
});

HTML здесь

<fieldset style="float:left">
    <input type="text" id="myinput"/>
</fieldset>
<div style="clear:left">
    <fieldset style="float:left">
       <legend>Results from the server:</legend>
       <div id="result"></div>
    </fieldset>
    <div style="clear:left"/>
    <fieldset style="float:left">
       <legend>Ajax protocol:</legend>
       <div id="protocol"></div>
    </fieldset>
</div>

В качестве сервера я использую очень простой сервис WCF с интерфейсом

using System.ServiceModel; using System.ServiceModel.Web; using System.ServiceModel.Channels;

namespace AjaxLoad {
    [ServiceContract]
    public interface ISimpleService {
        [OperationContract]
        [WebGet]
        Message GetTestHtmlFragment (string str);
    }
}

и реализация

using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading;

namespace AjaxLoad {
    [AspNetCompatibilityRequirements (RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class SimpleService : ISimpleService {
        public Message GetTestHtmlFragment (string str) {
            Thread.Sleep (1000);
            return WebOperationContext.Current.CreateTextResponse ("<span style='color:red'>" + str + "</span>",
                "text/html; charset=utf-8",
                Encoding.UTF8);
        }
    }
}

Я моделирую медленную обработку запросов только с Thread.Sleep с 1 сек ожидания. Я использовал SVC-бесплатную реализацию и использовал как web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

    <system.serviceModel>
        <standardEndpoints>
            <webHttpEndpoint>
                <!-- the "" standard endpoint is used by WebServiceHost for auto creating a web endpoint. -->
                <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"  />
            </webHttpEndpoint>
        </standardEndpoints>
        <behaviors>
            <serviceBehaviors>
                <behavior name="">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true">
            <serviceActivations>
                <add relativeAddress="MySimpleService.svc" service="AjaxLoad.SimpleService"
                     factory="System.ServiceModel.Activation.WebServiceHostFactory" />
            </serviceActivations>
        </serviceHostingEnvironment>
    </system.serviceModel>
</configuration>

В качестве "Ссылки" проекта необходимы три зависимые сборки: System, System.ServiceModel, System.ServiceModel.Web.

Попробуй это:

var searchTimer; //define the scope of searchTimer and set it to null
/* ...code...*/

if (searchTimer != null) {
    clearTimeout(searchTimer);
}

Идентификатор времени ожидания всегда будет начинаться с 0 и повышаться по мере создания большего количества таймеров.

Другие вопросы по тегам