jQuery response() function doesn't return any results because of the variable scope

a 夏天 提交于 2019-12-11 15:23:17

问题


The problem I encountered is I can't get any results from the jQuery UI Autocomplete form because of the variable scope. Let me show you.

// TAKE A CLOSE LOOK AT THIS METHOD
select: function(e, ui) {

$('#instant-search').text(ui.item.label);
$("#search").autocomplete("option", "source",
  function(request, response) {
    getAutocompleteResults(function(d) {
      // DOESN'T WORK response(d);
    });
    // WORKS BUT IT SHOULD BE A DYNAMIC ARRAY FROM THE "D" OBJECT
    // response(["anarchism", "anarchist black cross", "black rose (symbolism)", "communist symbolism", "political symbolism"]);
  });

$("#search").autocomplete("search", ui.item.label);

In order to return results I have to use a function response([...]); outside the getAutocompleteResults(function(d) { ... }); function.

However, the source should be dynamic and not like the static array. In other words:

The function response(d); should return an object, which contains a few properties (title, value, extract). I have to access them by using response(d);, however, this function doesn't work inside getAutocompleteResults(function(d) { ... }); function. How can I achieve this?

There is a small snippet of code, however, the main problem is the select method. You can find this in the middle of the whole code block. I commented it out.

$(function() {
  $("html").removeClass("no-js");
  var autocompleteResults = [{
    title: [],
    extract: [],
    pageId: []
  }];

  var capitalizeFirstLetter = function(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  };

  var changeText2 = function(e) {
    var request = $("input").val() + String.fromCharCode(e.which);
    $("#instant-search").text(request);

    var getAutocompleteResults = function(callback) {
      $.ajax({
        url: "https://en.wikipedia.org/w/api.php?format=json&action=query&generator=search&gsrlimit=6&prop=extracts&origin=*&pilimit=max&exintro&explaintext&exsentences=1&gsrsearch=" +
          $("#instant-search").text(),
        beforeSend: function() {
          $(".loading").show();
        },
        success: function(d) {
          $(".loading").hide();
          autocompleteResults[0].title = [];
          autocompleteResults[0].extract = [];
          autocompleteResults[0].pageId = [];

          if (d.hasOwnProperty("query")) {
            if (d.query.hasOwnProperty("pages")) {
              $.each(d.query.pages, function(i) {
                autocompleteResults[0].title.push(d.query.pages[i].title);

                autocompleteResults[0].extract.push(d.query.pages[i].extract);
                autocompleteResults[0].pageId.push(d.query.pages[i].pageid);
              });
            }
          }

          if (!autocompleteResults[0].length) {
            $(".ui-autocomplete").hide();
          }


          autocompleteResults[0].title.sort(function(a, b) {
            var nameA = a.toUpperCase();
            var nameB = b.toUpperCase();
            if (nameA < nameB) {
              return -1;
            }
            if (nameA > nameB) {
              return 1;
            }

            return 0;
          });

          autocompleteResults[0].title = autocompleteResults[0].title.map(
            function(i) {
              return i.toLowerCase();
            }
          );

          callback(autocompleteResults[0]);
        },
        datatype: "json",
        cache: false
      });
    };

    $("#search").autocomplete({
      source: function(request, response) {
        getAutocompleteResults(function(d) {
          var results = [],
            filteredAutocompleteResults = [];

          filteredAutocompleteResults = d.title.filter(function(i) {
            return (
              i !=
              $("#instant-search")
              .text()
              .toLowerCase()
            );
          });

          for (var i = 0; i < d.title.length; i++) {
            results[i] = {
              label: filteredAutocompleteResults[i],
              extract: d.extract[i],
              pageId: d.pageId[i]
            };
          }

          if (results.length == 5) {
            response(results);
          } else {
            response(results.slice(0, 5));
          }
        });
      },
      response: function() {
        if ($("#instant-search").text()) {
          $("table").css("display", "table");
          $(".wikisearch-container").css("margin-top", 100);
        }
      },
      close: function() {
        if (!$(".ui-autocomplete").is(":visible")) {
          $(".ui-autocomplete").show();
        }
      },
      appendTo: ".input",
      focus: function(e) {
        e.preventDefault();
      },
      delay: 0,












      // TAKE A CLOSE LOOK AT THIS METHOD
      select: function(e, ui) {

        $('#instant-search').text(ui.item.label);
        $("#search").autocomplete("option", "source",
          function(request, response) {
            getAutocompleteResults(function(d) {
              // DOESN'T WORK response(d);
            });
            // WORKS BUT IT SHOULD BE A DYNAMIC ARRAY FROM THE "D" OBJECT
            // response(["anarchism", "anarchist black cross", "black rose (symbolism)", "communist symbolism", "political symbolism"]);
          });

        $("#search").autocomplete("search", ui.item.label);










        // EVERYTHING SHOULD BE FINE BELOW THIS LINE

        if ($(".search-results").css("opacity") != 1) {
          $(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
          $(".search-results p").text(ui.item.extract);
          $(".search-results a").prop(
            "href",
            "https://en.wikipedia.org/?curid=" + ui.item.pageId
          );
          $(".search-results").css("opacity", 1);
        } else if (
          $(".search-results h4")
          .text()
          .toLowerCase() != ui.item.label
        ) {
          $(".search-results").css("opacity", 0);
          setTimeout(function() {
            $(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
            $(".search-results p").text(ui.item.extract);
            $(".search-results a").prop(
              "href",
              "https://en.wikipedia.org/?curid=" + ui.item.pageId
            );
            $(".search-results").css("opacity", 1);
          }, 500);
        }
      },
      create: function() {
        $(this).data("ui-autocomplete")._renderItem = function(ul, item) {
          return $("<li>")
            .append(
              '<div class="ui-menu-item-wrapper"><div class="autocomplete-first-field"><i class="fa fa-search" aria-hidden="true"></i></div><div class="autocomplete-second-field three-dots">' +
              item.label +
              "</div></div>"
            )
            .appendTo(ul);
        };
      }
    });
  };


  var changeText1 = function(e) {
    if (
      /[-a-z0-90áãâäàéêëèíîïìóõôöòúûüùçñ!@#$%^&*()_+|~=`{}\[\]:";'<>?,.\s\/]+/gi.test(
        String.fromCharCode(e.which)
      )
    ) {
      $("input").on("keypress", changeText2);
    }


    // DONT TOUCH THIS AREA, IT HAS NOTHING TO DO WITH THE PROBLEM
    var getInputSelection = function(input) {
      var start = 0,
        end = 0;
      input.focus();
      if (
        typeof input.selectionStart == "number" &&
        typeof input.selectionEnd == "number"
      ) {
        start = input.selectionStart;
        end = input.selectionEnd;
      } else if (document.selection && document.selection.createRange) {
        var range = document.selection.createRange();
        if (range) {
          var inputRange = input.createTextRange();
          var workingRange = inputRange.duplicate();
          var bookmark = range.getBookmark();
          inputRange.moveToBookmark(bookmark);
          workingRange.setEndPoint("EndToEnd", inputRange);
          end = workingRange.text.length;
          workingRange.setEndPoint("EndToStart", inputRange);
          start = workingRange.text.length;
        }
      }
      return {
        start: start,
        end: end,
        length: end - start
      };
    };

    switch (e.key) {
      case "Backspace":
      case "Delete":
        e = e || window.event;
        var keyCode = e.keyCode;
        var deleteKey = keyCode == 46;
        var sel, deletedText, val;
        val = this.value;
        sel = getInputSelection(this);
        if (sel.length) {
          // 0 kai paprastai trini po viena o 1 ar daugiau kai select su pele trini
          $("#instant-search").text(
            val.substr(0, sel.start) + val.substr(sel.end)
          );
        } else {
          $("#instant-search").text(
            val.substr(0, deleteKey ? sel.start : sel.start - 1) +
            val.substr(deleteKey ? sel.end + 1 : sel.end)
          );
        }
        break;
      case "Enter":
        if ($("#instant-search").text()) {
          console.log("Redirecting...");
        }
        break;
    }

    if (!$("#instant-search").text()) {
      $("table, .ui-autocomplete").hide();
      $(".wikisearch-container").css("margin-top", "");
    }

    if (
      $(".ui-menu-item-wrapper").hasClass("ui-state-active") &&
      (e.key == "ArrowRight" || e.key == "ArrowLeft")
    ) {
      $(".ui-autocomplete").autocomplete(""); // Error metas console ir taip neturėtų būti bet nežinau kaip padaryti kad pasirinkus elementą su <-- ar --> nepadarytų tik vieno rezultato todėl paliekam laikinai ;)
    }
  };

  $("input").on("keydown", changeText1);

  $("input").on("input", function(e) {
    $("#instant-search").text($("#search").val());
  });

});
html,
body {
  height: 100%;
  width: 100%;
}

body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  background-image: url("http://www.part.lt/img/96816a00ec1fb87adc4ca8a04365b2b5719.jpg");
  background-size: cover;
  background-position: 100%;
}

.v-container {
  display: table;
  height: 100%;
  width: 100%;
}

.v-content {
  display: table-cell;
  vertical-align: middle;
}

.text-center {
  text-align: center;
}

.input {
  overflow: hidden;
  white-space: nowrap;
}

.input input#search {
  width: calc(100% - 30px);
  height: 50px;
  border: none;
  font-size: 10pt;
  float: left;
  color: #4f5b66;
  padding: 0 15px;
  outline: none;
}

.ui-autocomplete {
  list-style: none;
  background-color: #fff;
  -webkit-user-select: none;
  user-select: none;
  padding: 0;
  margin: 0;
  width: 100% !important;
  top: auto !important;
  display: table;
  table-layout: fixed;
}

.ui-helper-hidden-accessible {
  display: none;
}

.autocomplete-first-field {
  width: 15%;
  display: inline-block;
}

.autocomplete-second-field {
  width: 85%;
  display: inline-block;
  text-align: left;
  vertical-align: middle;
}

.three-dots {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

table {
  width: 100%;
  border-spacing: 0;
  border-collapse: collapse;
  display: none;
  table-layout: fixed;
}

table tr {
  background-color: #fff;
  -webkit-user-select: none;
  user-select: none;
}

tr:first-child {
  background-color: #ffc800;
  color: #fff;
}

table td,
.ui-menu-item-wrapper {
  padding: 10px 0;
}

td:nth-child(2) {
  width: 85%;
  text-align: left;
}

.ui-menu-item,
table {
  cursor: pointer;
}

:focus {
  outline: 0;
}

a {
  text-decoration: none;
  color: #fff;
  position: relative;
}

a:before {
  content: "";
  position: absolute;
  width: 100%;
  height: 0.0625rem;
  bottom: 0;
  left: 0;
  background-color: #fff;
  visibility: hidden;
  -webkit-transform: scaleX(0);
  transform: scaleX(0);
  -webkit-transition: all 0.3s ease-in-out 0s;
  transition: all 0.3s ease-in-out 0s;
}

a:hover:before {
  visibility: visible;
  -webkit-transform: scaleX(1);
  transform: scaleX(1);
}

.search-results {
  background: #fff;
  margin-top: 50px;
  border-left: 5px solid #0ebeff;
  opacity: 0;
  -webkit-transition: opacity 1s;
  transition: opacity 1s;
}

.search-results h4,
.search-results p {
  margin: 0;
  padding: 10px;
  text-align: left;
}

.search-results a {
  color: #0ebeff;
  display: inline-block;
  margin: 1rem 0;
}

.search-results a:before {
  background-color: #0ebeff;
}

.wikisearch-container {
  width: 65%;
  margin: 0 auto;
}


/* Loading animation */

@keyframes lds-eclipse {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  50% {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

@-webkit-keyframes lds-eclipse {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  50% {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

.loading {
  position: relative;
  top: 9.5px;
  right: 15px;
  pointer-events: none;
  display: none;
}

.lds-eclipse {
  -webkit-animation: lds-eclipse 1s linear infinite;
  animation: lds-eclipse 1s linear infinite;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  margin-left: auto;
  box-shadow: 0.08rem 0 0 #0ebeff;
}

@media (max-width: 71.875em) {
  .wikisearch-container {
    width: 75%;
  }
}

@media (max-width: 50em) {
  .wikisearch-container {
    width: 85%;
  }
}

@media (max-width: 17.96875em) {
  .wikisearch-container {
    width: 100%;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<html class="no-js">
<div class="v-container">
  <div class="v-content text-center">
    <div class="wikisearch-container">
      <div class="input">
        <input type="text" id="search" placeholder="Search...">
        <div class="loading">
          <div class="lds-eclipse"></div>
        </div>
        <button class="icon"><i class="fa fa-search"></i></button>
        <table>
          <tr>
            <td class="fa fa-search">
              <td id="instant-search" class="three-dots"></td>
          </tr>
        </table>
      </div>
      <div class="search-results">
        <h4></h4>
        <p></p>
        <a target="_blank">Click here for more</a>
      </div>
    </div>
  </div>
</div>

EDIT 1 After some changes, the results are shown, however, before the ajax call. How can I use response() only after the ajax was successfully completed (tried using success callback, didn't work :()?

Full project code: https://codepen.io/Kestis500/pen/zRONyw?editors=0010.


Here you can see step by step how it looks like:

  1. How it looks like when you just reloaded the page:
  2. Let's try entering "a":
  3. We've got some results. Ok, let's try to click on the "anarchist symbolism" element:
  4. Results should look like "anarchist symbolism" search. However, it returns the result of the "a" search. What if we pressed "fraktur" element?
  5. Now it shows our previous search "anarchist symbolism" results. However, it should return elements of the "fraktur" search.

EDIT 2 I've fixed many things and removed some really non sense things from my code. However, the situation with the ajax call is still the same.

https://codepen.io/Kestis500/pen/pazppP?editors=0110

Any ideas?

EDIT 3 Fixed ajax lag (now the ajax request will be sent only after the previous ajax call).

https://codepen.io/Kestis500/pen/JpPLON?editors=0110


回答1:


I had to first fix a few things in your Ajax call. We then collect the results and build an array that should be returned to response(). This will populate the AutoComplete.

First we will examine the HTML. There was some closing tags missing.

HTML

<div class="v-container">
  <div class="v-content text-center">
    <div class="wikisearch-container">
      <div class="input ui-front">
        <input type="text" id="search" placeholder="Search...">
        <div class="loading">
          <div class="lds-eclipse"></div>
        </div>
        <button class="icon">
          <i class="fa fa-search"></i>
        </button>
        <table>
          <tr>
            <td class="fa fa-search"></td>
            <td id="instant-search" class="three-dots"></td>
          </tr>
        </table>
      </div>
      <div class="search-results">
        <h4></h4>
        <p></p>
        <a target="_blank">Click here for more</a>
      </div>
    </div>
  </div>
</div>

You can see the table and it's cells all have the proper closing tags now.

I didn't make any changes to your CSS or Style.

JavaScript

$(function() {
  var capitalizeFirstLetter = function(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  };
  $("#search").autocomplete({
    source: function(request, response) {
      var results = [];
      $.ajax({
        url: "https://en.wikipedia.org/w/api.php",
        data: {
          format: "json",
          action: "query",
          generator: "search",
          gsrlimit: 6,
          prop: "extracts|pageimages",
          origin: "*",
          pilimit: "max",
          exintro: false,
          explaintext: false,
          exsentences: 1,
          gsrsearch: request.term
        },
        beforeSend: function() {
          $(".loading").show();
        },
        success: function(d) {
          $(".loading").hide();
          if (d.query.pages) {
            $.each(d.query.pages, function(k, v) {
              console.log(k, v.title, v.extract, v.pageid);
              results.push({
                label: v.title,
                value: "https://en.wikipedia.org/?curid=" + v.pageid,
                title: v.title,
                extract: v.extract,
                pageId: v.pageid
              });
            });
            response(results);
          }
        },
        datatype: "json",
        cache: false
      });
      response(results);
    },
    close: function() {
      if (!$(".ui-autocomplete").is(":visible")) {
        $(".ui-autocomplete").show();
      }
    },
    focus: function(e) {
      e.preventDefault();
      return false;
    },
    delay: 0,
    select: function(e, ui) {
      if ($(".search-results").css("opacity") != 1) {
        $(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
        $(".search-results p").text(ui.item.extract);
        $(".search-results a").prop(
          "href",
          ui.item.value
        );
        $(".search-results").css("opacity", 1);
      } else if (
        $(".search-results h4")
        .text()
        .toLowerCase() != ui.item.label
      ) {
        $(".search-results").css("opacity", 0);
        setTimeout(function() {
          $(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
          $(".search-results p").text(ui.item.extract);
          $(".search-results a").prop(
            "href",
            ui.item.value
          );
          $(".search-results").css("opacity", 1);
        }, 500);
      }
      return false;
    }
  }).autocomplete("instance")._renderItem = function(ul, item) {
    var $item = $("<li>");
    var $wrap = $("<div>").appendTo($item);
    var $field1 = $("<div>", {
      class: "autocomplete-first-field"
    }).appendTo($wrap);
    $("<i>", {
      class: "fa fa-search",
      "aria-hidden": true
    }).appendTo($field1);
    $("<div>", {
      class: "autocomplete-second-field three-dots"
    }).html(item.label).appendTo($wrap);
    return $item.appendTo(ul);
  };
});

There was a lot of things to fix and improve.

Let's start with the Ajax. You're making a call to a MediaWiki API and expecting some results. When the call would come back, it would generate warnings about pilimit. Digging into the API docs, this is a parameter specific to the pageimages properties call. To fix this, the prop value had to be extracts|pageimages. Now I get a clean set of results.

You can see I broke out the data so that I could more easily make changes and see what parameters I was sending to the API. Nothing wrong with your method, I just find this a lot easier to work with.

This is all happening inside .autocomplete() when we are populating the source. When we use function as a source, it has to follow a few guidelines:

  • we pass a request and response in
  • results must be in an array
  • the array can contain objects, as long as they contain at least { label, value }
  • our results array must be passed to response function.

A brief example:

$(selector).autocomplete({
  source: function(req, resp){
    var q = req.term;
    // The Request is an object that contains 1 index: term
    // request.term will contain the content of our search
    var results = [];
    // An array to store the results
    $.getJSON("myapi.php", {query: q}, function(data){
      $.each(data, function(key, val){
        // iterate over the result data and populate our result array
        results.push({
          label: data.name,
          value: data.url
        });
        resp(results);
      });
    });
  }
});

You can sort or filter the results all you like; as long as you pass them to response in the end.

With your focus and select callbacks, you want to return false. This is discussed more here: http://jqueryui.com/autocomplete/#custom-data

We also see a good example of rendering the menu item. I switched over to making jQuery objects versus raw HTML. You do what works best for you.

Working Example: https://jsfiddle.net/Twisty/vr6gv2aw/4/

Hope this helps.



来源:https://stackoverflow.com/questions/48476560/jquery-response-function-doesnt-return-any-results-because-of-the-variable-sc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!