开发者

jquery: ajax based search - browser crash?

hey guys, i'm working on a rather weird ajax based search model. The search is not actually retrieving real searchresults from the server but rather loads the website's sitemap (with the jquery load() method) and extracts its links.

That works actually really good, but there is one little bug that might cause my browser to crash.

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);
    }
}

The only problem I have with this is that the site crashes once I type a word in my .s input field and immediately delete it within the 250ms.

Imagine this: 1.) the input is empty. 2.) i quickly type "test". 3.) the doSearch function hasn't even been fired and I hit cmd-a to select all and remove the text in the input.

complete crash of my site!

Why could that happen? It does work really smooth and fine when I just type "test" or delete the input once the doSearch() has been fired. It actually works always. Just in this rare case of typing quickly and removing the typed text within the event of doSeach() it crashes.

Any idea what could cause that?

edit/update: When I copy sitemap.html into my current procets root directory and load it doesn't crash and works fine as in your example. As soon as i change it to url: "sitemap", dataType: "html", it crashes. I call my sitemap with mydomain.com/sitemap...

the code for the sitemap looks like this:

    <?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; ?>

sorry for this last question, but any idea why that makes a difference. When I use this dynamic /sitemap as basis for my search the browser crashes. With a static html page it works fine.


I suppose the main problem of your code is that you don't abort the previous pending ajax call. What happens in the browser if it simultaneously will try to modify $sr element on two server response?

Both old XMLHttpRequest and new jqXHR has abort method which you can use.

UPDATED: As I described in the comment the jQuery.load do not much more as the jQuery.ajax call and jQuery.html to place the server response on the page. You can verify this looking in the source code of jQuery.load here (for jQuery 1.4.4) or here (for jQuery 1.5.1).

I prepared one small demo example for you which shows how you can use jQuery.ajax and jQuery.html directly instead of jQuery.load. You can download the full project here.

If one types in the input box of the demo slowly one receive the following results

jquery: ajax based search - browser crash?

If one types more quickly (I type very slow and so use 1 sec timeout on the server):

jquery: ajax based search - browser crash?

One can see that I abort the previous ajax request to the server if any pending ajax request exist. In case of aborting the error handler of the corresponding (previous) ajax request are called and then the abort() function return.

I hope if you follow the way you will never have the problems which you describes in your question.

To be sure that you receive the example I include the full code, which I used in my test demo, below. The JavaScript code is following

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 is here

<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>

As the server I use very simple WCF service with the interface

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

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

and the implementation

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);
        }
    }
}

I simulate slow request processing just with Thread.Sleep with 1 sec waiting. I used SVC file free implementation and so used as 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>

As "References" of the project three dependent assemblies needed: System, System.ServiceModel, System.ServiceModel.Web.


Try this:

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

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

The ID of the timeout is always going to start from 0 and go up as more timers are created.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜