Keyup function is only working once for first letter of input

Viewed 258

I have a text input where the user enters a desired page name

<input type='text' id='pageName'  name='PageName' class='form-control pageName'>

I am trying to use the keyup function to fire Ajax to check if the page exists, however when typing something like index Ajax is only sending the I (first letter) and not running again.

Script:

<script>
  $("#pageName").keyup(function() {
    $.ajax({
      type: "GET",
      async: false,
      url: "CheckPageName.php",
      data: {
        'PageName': jQuery(this).val()
      },
      cache: false,
      success: function(html) {
        $("#NotAllowed").replaceWith(html);
        $('#loader_image').hide();
      }
    });
  });
</script>

If I add console.log(html); It shows the values correctly in the log, but $("#NotAllowed").replaceWith(data); is only showing the first letter or 2 (if I type fast!)

What am I missing?

2 Answers

The issue has nothing to do with your AJAX call (although async:false is still unnecessary and bad practice in general).

The problem is what the jQuery replaceWith function does. As per the documentation it removes the element(s) it matches, and replaces the entire element with the content you supply to the function. Note it replaces the entire element, not just its inner content!

So the reason it only works once is simply because after the first execution, the "NotAllowed" element no longer exists - replaceWith has removed it and replaced it with the response from your AJAX request!

If you want to update the content of an element without removing it, in jQuery you can use .html() or .text() (depending on whether the content is HTML or plain text). In this case .text() would be fine:

success: function(response) {
    $("#NotAllowed").text(response);
    $('#loader_image').hide();
}

Demo: https://jsfiddle.net/k3461n7h/2/

You should wrap your keyup function in a debounce. This will save you some time and load when requesting data from the server.

const debounce = (callback, wait) => {
  let timeoutId = null;
  return (...args) => {
    const [ { target } ] = args;
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(() => {
      callback.apply(target, args);
    }, wait);
  };
}

const handleKeyUp = debounce(function() {
  $('#loader-image').show();
  $.ajax({
    type: 'GET',
    async: false,
    url: 'CheckPageName.php',
    data: { 'PageName': $(this).val() },
    cache: false,
    success: function(data, textStatus, jqXHR) {
      $('#not-allowed').html(data);
      $('#loader-image').hide();
    },
    error: function(jqXHR, textStatus, errorThrown) {
      $('#not-allowed').html('<p>Not found...</p>');
      // Hide the mask after 1 second, to mimic waiting for a response
      // Remove this timeout once the response is hooked-up
      setTimeout(function() {
        $('#loader-image').hide();
      }, 1000);
    }
  });
}, 250); // Search only after 250ms once typing has stopped

$('#loader-image').hide();
$('#page-name').on('keyup', handleKeyUp);
#loader-image {
  display: flex;
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  margin: 0;
  padding: 0;
  z-index: 999;
  background: rgba(0, 0, 0, 0.5);
  justify-content: center;
  align-items: center;
  font-weight: bold;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<div class="container">
  <form>
    <div class="mb-3">
      <label for="page-name" class="form-label">Search existing page name</label>
      <input type="text" id="page-name" name="page-name" class="form-control">
      <div id="page-name-help" class="form-text">Check for an existing page.</div>
    </div>
  </form>
  <hr />
  <div id="not-allowed"></div>
</div>
<div id="loader-image">Loading...</div>

Related