carefulnow

LiveSearch.js

Jul 2nd, 2017
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * LiveSearch.js
  3.  * @author Oliver Dixon (2017)
  4.  *
  5.  * Allows the user to get search results as they're typing (800ms lag to
  6.  * prevent too much of a heavy load on the database/server).
  7.  */
  8.  
  9. /* jshint -W097 */
  10. "use strict";
  11.  
  12. /*
  13. * "Live Search" stuff
  14. * Allows users to get instant search results by performing AJAX requests.
  15. * */
  16.  
  17. var target = document.getElementById("searchOutput");
  18. var container = document.getElementById("searchContainer");
  19. var searchBox = document.getElementById("searchBox");
  20. var errOutput = document.getElementById("searchErr");
  21.  
  22. searchBox.addEventListener("keyup", invokeLS);
  23.  
  24. /**
  25.  * Function invokeLS
  26.  *
  27.  * Invokes the liveSearch function, passing in the search terms.
  28.  *
  29.  * I wrapped this in a function so it's friendly with the awful addEventListener
  30.  * and removeEventListener functions...
  31.  *
  32.  * It also waits for 800ms of keyUp inactivity before it invokes liveSearch,
  33.  * so the server doesn't constantly get hammered with requests.
  34.  */
  35.  
  36. var timeout;
  37.  
  38. function invokeLS() {
  39.  
  40.     // If there is no query, no point to have any lag, just get rid of the output box!
  41.  
  42.     if (searchBox.value.length === 0) {
  43.         /* The outputSearchResults function takes care of resetting target.innerHTML etc.
  44.          * before showing any new results. */
  45.  
  46.         container.style.display = "none";
  47.         return;
  48.     }
  49.  
  50.     // Otherwise, wait for 0.8 seconds of key inactivity then send the request.
  51.  
  52.     clearTimeout(timeout);
  53.  
  54.     timeout = setTimeout(function() {
  55.         liveSearch(searchBox.value);
  56.     }, 800);
  57.  
  58. }
  59.  
  60. /**
  61.  * Function errHandler
  62.  *
  63.  * Should be called when a fatal error happens with the liveSearch function.
  64.  *
  65.  * When called, it disables the textbox and disconnects the event listener, and displays
  66.  * an error.
  67.  *
  68.  * @param {string} msg
  69.  */
  70.  
  71. function errHandler(msg) {
  72.     errOutput.innerHTML = "<div style='color: #f00;'>An error occurred (" + msg + "). Check the console for more details.</div>";
  73.  
  74.     searchBox.value = ""; // allow placeholder
  75.     searchBox.placeholder = "An error occurred. Try refreshing the page.";
  76.  
  77.     searchBox.removeEventListener("keyup", invokeLS);
  78.     searchBox.disabled = true;
  79. }
  80.  
  81. /**
  82.  * Function liveSearch
  83.  *
  84.  * Performs the AJAX request and deals with error handling.
  85.  * If everything is OK, send to outputSearchResults to display them.
  86.  *
  87.  * Expects a JSON response from the server.
  88.  *
  89.  * @param {string} query - the search term to send to the server
  90.  */
  91.  
  92. function liveSearch(query) {
  93.  
  94.     if (query.length !== 0) {
  95.         var xmlhttp = new XMLHttpRequest();
  96.  
  97.         /**
  98.          * Anonymous error function
  99.          *
  100.          * Fired when a network-level error occurs on the XMLHTTPRequest.
  101.          * Also gets fired when an attempted cross-domain request is made.
  102.          */
  103.  
  104.         xmlhttp.onerror = function() {
  105.             errHandler(this.error);
  106.         };
  107.  
  108.         xmlhttp.ontimeout = function() {
  109.             errHandler("the timeout was exceeded, try the <a href='search.php'>advanced search</a> page");
  110.         };
  111.  
  112.         /**
  113.          * Anonymous onreadystatechange function
  114.          *
  115.          * Gets fired when something about the XMLHTTPRequest object has changed.
  116.          * Usually it means it has completed, this can be checked if xmlhttp.status = 200.
  117.          */
  118.  
  119.         xmlhttp.onreadystatechange = function() {
  120.  
  121.             if (this.readyState === 4) {
  122.  
  123.                 if (this.status === 200) {
  124.  
  125.                     var json = null;
  126.  
  127.                     try {
  128.                         json = JSON.parse(this.responseText);
  129.                     } catch (e) {
  130.                         // Error parsing JSON (invalid response)
  131.  
  132.                         errHandler("The response was not valid JSON!");
  133.                         return;
  134.                     }
  135.  
  136.                     /* Error checking
  137.                      * If the err attribute is set, and error has happened */
  138.  
  139.                     //noinspection JSUnresolvedVariable
  140.                     if (json.err !== undefined) {
  141.                         //noinspection JSUnresolvedVariable
  142.                         errHandler(json.err);
  143.                         return;
  144.                     }
  145.  
  146.                     if (json.length === 0) {
  147.                         target.innerHTML = "No results were found.<br />Try widening your search.";
  148.                         return;
  149.                     }
  150.  
  151.                     outputSearchResults(json);
  152.  
  153.                 } else {
  154.                     errHandler("invalid HTTP response code of " + this.status);
  155.                 }
  156.  
  157.             }
  158.  
  159.         };
  160.  
  161.         xmlhttp.open("GET", "apiResponder.php?mode=0&query=" + query, true);
  162.         xmlhttp.timeout = 3000;
  163.  
  164.         xmlhttp.send();
  165.     }
  166.  
  167. }
  168.  
  169. /**
  170.  * Output search results
  171.  * @param {string} results (JSON)
  172.  */
  173.  
  174. function outputSearchResults(results) {
  175.  
  176.     target.innerHTML = "";
  177.     container.style.display = "block";
  178.  
  179.     var successful = 0;
  180.  
  181.     for (var i = 0; i < results.length - 1; i++) {
  182.  
  183.         if (i === 40) { // max of 40 values
  184.             break;
  185.         }
  186.  
  187.         if (
  188.             results[i] !== undefined &&
  189.             results[i].title !== /** @type {string} */ undefined &&
  190.             results[i].url !== /** @type {string} */ undefined
  191.         ) {
  192.             target.innerHTML +=
  193.                 "<div class='result'><div style='padding: 0.6em;'><h3>" +
  194.                 results[i].title + "</h3><h4>" + results[i].url + "</h4></div></div><br />";
  195.  
  196.             successful++;
  197.         } else {
  198.             console.warn("Invalid JSON at index " + i + ": " + results[i]);
  199.         }
  200.  
  201.     }
  202.  
  203.     if (
  204.         results[results.length - 1] !== undefined &&
  205.         results[results.length - 1].title !== /** @type {string} */ undefined &&
  206.         results[results.length - 1].url !== /** @type {string} */ undefined
  207.     ) {
  208.         target.innerHTML +=
  209.             "<div class='result'><div style='padding: 0.6em;'><h3>" +
  210.             results[results.length - 1].title + "</h3><h4>" + results[results.length - 1].url + "</h4></div></div>";
  211.  
  212.         successful++;
  213.     } else {
  214.         console.warn("Invalid JSON at index " + i + ": " + results[i]);
  215.     }
  216.  
  217.     if (successful === 0) {
  218.         target.innerHTML = "No results were found.<br />Try widening your search.";
  219.     }
  220. }
Add Comment
Please, Sign In to add comment