2

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?

Designer
  • 477
  • 2
  • 12
  • 2
    `async : false` could be a culprit here, since that blocks anything else (including JS events) from happening in the browswer while the AJAX request is taking place. It's not necessary and just causes problems. Remove that and try again, would be my first suggestion. – ADyson Jun 03 '21 at 13:31
  • 1
    Where are the `$("#NotAllowed")` and `$("#loader_image")` elements? Is the `` inside the `#NotAllowed` element? – Sebastian Simon Jun 03 '21 at 13:32
  • @SebastianSimon - No, outside of both div's. They are below the input – Designer Jun 03 '21 at 13:33
  • @ADyson - Removed `async : false` but it made no difference... – Designer Jun 03 '21 at 13:34
  • You should wrap your keyup function in a [debounce](https://www.joshwcomeau.com/snippets/javascript/debounce/). – Mr. Polywhirl Jun 03 '21 at 13:38
  • @Mr.Polywhirl - Anychance you could show me an example with my code? – Designer Jun 03 '21 at 13:42
  • 1
    Any errors / warnings in the browser console? What do you see in the network tool - just one instance of the AJAX call, it sounds like from your description? Are there any subsequent unsuccessful ones, or just nothing? Have you added any console logging to see how many times the keyup event is fired? The debugging seems to be a bit thin on the ground so far. – ADyson Jun 03 '21 at 13:44
  • Please share more details - is this even related to PHP? – Nico Haase Jun 03 '21 at 13:50
  • 2
    Since I just copied this over and am able to execute without reproducing your error, there must be something else involved. You might as well paste any other page code that could possibly be relevant. What version of jquery? any possible conflicting libraries you're also including? – Kinglish Jun 03 '21 at 13:50
  • @ADyson - No errors in the console. I can see in the network it pulls the correct Ajax url 5 times (for 5 letters), but the get variable is only the first letter (If that makes sense) – Designer Jun 03 '21 at 13:50
  • 1
    Are you sure? That's weird...and also not reproducible using the code you've shown - demo: https://jsfiddle.net/k3461n7h/1/ – ADyson Jun 03 '21 at 13:53
  • @ADyson - can we move to chat? – Designer Jun 03 '21 at 13:55
  • @ADyson. If I add `console.log(html);` it shows correctly in the log, but the `$("#NotAllowed").replaceWith(html);` is only showing the first letter (or two if I type fast!) – Designer Jun 03 '21 at 14:01
  • @SebastianSimon, sorry not data, I have amended to say `html` – Designer Jun 03 '21 at 14:03
  • @Designer Then what is `html`, exactly? Have you tried logging that? Have you read the documentation on [`replaceWith`](//api.jquery.com/replacewith/)? – Sebastian Simon Jun 03 '21 at 14:03
  • @SebastianSimon - `success: function(html)` - it's returning the value from CheckPageName.php (`$PageName=$_GET['PageName']; echo $PageName`) – Designer Jun 03 '21 at 14:04
  • I assume that's only some test code in the PHP then, because it doesn't do anything useful. Anyway show the HTML of this "notallowed" element please. – ADyson Jun 03 '21 at 14:05
  • @ADyson `

    `
    – Designer Jun 03 '21 at 14:05
  • I have fixed it by changing `$("#NotAllowed").replaceWith(html);` to `$("#NotAllowed").html(html);` – Designer Jun 03 '21 at 14:10
  • Then it’s a duplicate of [What's the difference between jQuery's replaceWith() and html()?](/q/730916/4642212)… `.replaceWith` removes the selected element. – Sebastian Simon Jun 03 '21 at 14:12
  • @Designer lol looks like you figured it out at the exact moment I did - I was writing the answer below as you added your comment :-) – ADyson Jun 03 '21 at 14:13

2 Answers2

2

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/

ADyson
  • 57,178
  • 14
  • 51
  • 63
  • Was troubling with same problem using Django & HTMX, was using outerHTML to replace response instead of innerHTML. Your answer helped me. – Aashutosh Kumar Jul 18 '22 at 15:29
-3

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>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132