1

The demo below is the instant search function I build, just like google search, printing out result right after the user's input.

When testing, it runs smoothly on localhost, but on the web, it runs lagging, that is, when user typing in multiple characters fast, the keypress will not show in input box for some seconds to wait for data processing (I guess) then the charactered typed showed many seconds after . Absolutely this is no good for user's experience. How may I resolve this?

Frontend

<input onkeyup="searchq();" type="text">
<script>
function searchq(){
        // get the value
            txt = $("input").val();
            // post the value
            if(txt){
                $.post("search.php", {searchVal: txt}, function(result){
                    $("#search_output").html(result+"<br><a class='anchor_tag_s' href='createobject.php?object="+txt+"'><div id='notfound'>Not found above? Create Here.</div><a>");
                });
            }
            else{
                $("#search_output").html("");
            }
        };
</script>

Backend

    //query the search using match
    $query=mysqli_query($conn,"SELECT * from objects WHERE Match(name) Against ('%$search%' in natural language mode) LIMIT 9") or die("could not search! Oops, Panpan might be hacked");
    $count=mysqli_num_rows($query);
   //if match no result using like query
    if($count==0){
        $query=mysqli_query($conn,"SELECT * from objects WHERE name LIKE '%$search%' LIMIT 9") or die("could not search! Oops, Panpan database might be hacked");
        $count=mysqli_num_rows($query);
        if ($count==0){
            while($count==0){

            //if matach and like show no result, remove the frist word of array, then search, if no result, do it again, if not, do it again.
                $search = explode(" ",$search);
                $search=array_slice($search,1,2);
                $search=implode(" ",$search);
                $query=mysqli_query($conn,"SELECT * from objects WHERE name LIKE '%$search%' LIMIT 9") or die("could not search! Oops, Panpan database might be hacked");
                $count=mysqli_num_rows($query);
                while($row=mysqli_fetch_assoc($query)){
                $object_id=$row["object_id"];
                $object_name=$row["name"];
                $object_description=$row["description"];
                $object_up=$row["up"];
                $object_down=$row["down"];
                $object_views=$row["views"];

                $output=$object_name;
                $result= "<a class='anchor_tag_s' href='"."object.php?id=".$object_id."'><div>".$output."</div></a>";
                echo $result;
                }
            }
        }   
        else{
             // print out the like query
            while($row=mysqli_fetch_assoc($query)){
            $object_id=$row["object_id"];
            $object_name=$row["name"];
            $object_description=$row["description"];
            $object_up=$row["up"];
            $object_down=$row["down"];
            $object_views=$row["views"];

            $output=$object_name;
            $result= "<a class='anchor_tag_s' href='"."object.php?id=".$object_id."'><div>".$output."</div></a>";
            echo $result;
            }
        }
    }
    else{
          // print out the match query
        while($row=mysqli_fetch_assoc($query)){
            $object_id=$row["object_id"];
            $object_name=$row["name"];
            $object_description=$row["description"];
            $object_up=$row["up"];
            $object_down=$row["down"];
            $object_views=$row["views"];

            $output=$object_name;
            $result= "<a class='anchor_tag_s' href='"."object.php?id=".$object_id."'><div>".$output."</div></a>";
            echo $result;
        }
    }

}


?>
ryanpika
  • 151
  • 3
  • 13
  • 3
    your posts are completing at different times and coming back out of order. you need to look into implementing a delay before the onkeyup function works its magic. – Shockwave Feb 12 '16 at 23:19
  • @shockwave Thx, could you be more specific, if possible could you write some pseudo code to articulate? And what do you mean the post are coming back out of order? – ryanpika Feb 12 '16 at 23:21
  • couple options, http://stackoverflow.com/questions/5637013/how-to-bring-ajax-search-onkeyup-with-jquery or http://stackoverflow.com/questions/7849221/ajax-delay-for-search-on-typing-in-form-field – Shockwave Feb 12 '16 at 23:25
  • if you're not opposed to using a library, jquery ui has an autocomplete widget as well here: http://api.jqueryui.com/autocomplete/ – Shockwave Feb 12 '16 at 23:27

2 Answers2

2

I'd suggest absolutely not searching directly on key-up. That's incredibly inefficient and very resource intense. I'd suggest setting a timer for say, one second. Then, on key-up, you refresh that timer to one second. If it ever reaches 0, then you search. Something like this:

timer = 1000;
started = false;

$("input[type=text]").keyup( function(){
    window.timer = 1000;

    if(window.started == false) queueTimer();
    window.started = true;
});

function queueTimer(){
    if(window.timer <= 0){
        //Timer has run out, business logic here!
        searchq();
        window.started = false;
    } else {
        window.timer -= 100;

        setTimeout(queueTimer, 100);
    }
}

Set the timer to whatever you want, in milliseconds. I recommend one second, but you can lower it as you see fit.

Check the fiddle here:

https://jsfiddle.net/kbyy2m6u/

What the code is doing

Right off the bat, we set our timer to 1000 milliseconds and started to false.

The keyup inline-function is resetting the timer to 1000 milliseconds, queues the timer, and sets started to true. We set it to true so the queueTimer() function does not get queued multiple times (which would negate the entire point of queueing instant searches). Once it's set to true, it will not run queueTimer() until the timer runs out and a search is actually conducted.

queueTimer() is the bread and butter here. If the timer has not run out yet, it will simply decrease the timer variable by 100 milliseconds, and call itself to be ran again in 100 milliseconds (looping until the timer runs out). Once it runs out, it executes your searchq() and sets started to false again so the instant search can run once more.

In plain English, every time someone types in a search box it will wait one second before doing an instant-search. The timer will reset on key-up, even after the queue is started, so that while typing a person will not be searching. This prevents multiple erroneous searches by making sure a user has paused for a bit to actually conduct a search.

Now a word of caution

Google is a giant - they make magical things happen on the web. Things like an instant search are great in theory, but for those of us who aren't able to throw billions of dollars at R&D like Google, it's something that's not typically worth the headache.

Every single call you make to the database is taxing. If you've got other functionality on your web application that hits the DB and now you're adding an instant search, be prepared for some pretty interesting side-effects. They may even be unforeseen side effects on totally separate databases due to data locks on your MySQL Engine. This is one reason why Data Locking is so prevalent in a lot of Magento installs.

I'm sure you know what you're doing, but I want to make sure anyone who sees this question will realize the trade-off to something like instant search.

Nate I
  • 946
  • 4
  • 10
  • Could you explain the logic in detail if you have time. Thanks a lot, i'm a bit new to javascript. – ryanpika Feb 13 '16 at 00:07
  • Absolutely, I'll update the answer now! Sorry for the delay. – Nate I Feb 14 '16 at 21:46
  • I'd like to point out also that the solution provided by gion_13 would be **great** in conjunction with my solution above. You could set your timer to something smaller, like 300-500 milliseconds (tune to your liking) and couple it with gion_13's answer to get a really snappy instant search that doesn't hit the database with erroneous/obsolete calls. – Nate I Feb 15 '16 at 20:32
1

You should consider two things: debounce your calls and abort any previous ajax requests.

For the first one, there are lots of information on debounce/throttle techniques. Basically you have to assure that you do not execute more than one function call (searchq) in a certain period of time. Thus, if the user types abc, it won't trigger 3 different searches for a, ab and abc. Instead, it will trigger a single search for the whole result (abc).

Another thing you should consider is aborting unresolved ajax calls.
Since you're relying on ajax calls to show results, and since ajax calls are... well... asynchronous, you shouldn't actually have multiple simultaneous ajax calls. Take the following example:

  1. the user types ab
  2. the searchq function is executed for the first character (a)
  3. the searchq function is executed for the new sequence (ab)
  4. the ajax promise for the second call gets resolved
  5. the ajax promise for the first call gets resolved
  6. the user sees the result for the wrong input

To avoid this issue, you should abort - or ignore at least - any other ajax call that is in progress by the time you fire a new one.

gion_13
  • 41,171
  • 10
  • 96
  • 108