37
votes

I have a textarea where people enter some text (naturally), and I want to make it so that an AJAX request is made every now and then to get some suggestions regarding what the textarea is about (like stack overflow's Related Questions, but for a textarea, not a text input). The problem is that I can't do an AJAX request at every keypress (it'd be useless and very resource consuming), and I am not sure what would be the most efficient way to do it (every X words? every X seconds? or something else?).

What would be the best way to go about doing this?

Thank you in advance.

7
possible duplicate of jQuery .keyup() delay - kenorb
@kenorb this question was actualy first, and you referenced was considered as a duplicate of this questions, but admins did not want to close the question because the title was better and it has about 132931 views which is quite remarkable. - Adam
It doesn't matter this is older, the other one has more votes and views, see: Should I flag a question as duplicate if it has received better answers?. The title can be always edited to the better one. They can be always merged. - kenorb
Pretty cool to see my question from 2009 being reviewed in 2016. Talk about thorough moderation. - Bruno De Barros

7 Answers

70
votes

You could combine a keypress event handler with setTimeout so that you send an Ajax request a set amount of time after a keypress, cancelling and restarting the timer if another keypress occurs before the timer finishes. Assuming you have a textarea with id 'myTextArea' and an Ajax callback function called doAjaxStuff:

function addTextAreaCallback(textArea, callback, delay) {
    var timer = null;
    textArea.onkeypress = function() {
        if (timer) {
            window.clearTimeout(timer);
        }
        timer = window.setTimeout( function() {
            timer = null;
            callback();
        }, delay );
    };
    textArea = null;
}

addTextAreaCallback( document.getElementById("myTextArea"), doAjaxStuff, 1000 );
18
votes

What you're looking for is called debouncing. Here is a generic algorithm in native JavaScript:

function debounce(fn, duration) {
  var timer;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(fn, duration)
  }
}

And here's an example of how to use it with an onkeyup event:

function debounce(fn, duration) {
  var timer;
  return function(){
    clearTimeout(timer);
    timer = setTimeout(fn, duration);
  }
}

const txt = document.querySelector('#txt')
const out = document.querySelector('#out')
const status = document.querySelector('#status')

const onReady = () => {
  txt.addEventListener('keydown', () => {
    out.classList.remove('idle')
    out.classList.add('typing')
    status.textContent = 'typing...'
  })
  
  txt.addEventListener('keyup', debounce(() => {
    out.classList.remove('typing')
    out.classList.add('idle')
    status.textContent = 'idle...'
  }, 800))
}

document.addEventListener('DOMContentLoaded', onReady)
#wrapper{
  width: 300px;
}

input{
  padding: 8px;
  font-size: 16px;
  width: 100%;
  box-sizing: border-box;
}

#out{
  margin: 10px 0;
  padding: 8px;
  width: 100%;
  box-sizing: border-box;
}

.typing{
  background: #A00;
  color: #FFF;
}

.idle{
  background: #0A0;
  color: #FFF;
}
<div id="wrapper">
  <input id="txt" placeholder="Type here" />
  <div id="out">Status: <span id="status">waiting...</span></div>
</div>
12
votes

Another alternative is to use a small jQuery plugin called bindWithDelay. It uses the same setTimeout technique that the accepted answer has, but handles the timeouts transparently so your code is a little easier to read. The source code can be seen on github.

$("#myTextArea").bindWithDelay("keypress", doAjaxStuff, 1000)
4
votes

If you are using lodash.js (or underscore.js) you can use the debounce function.

Example (jQuery used for keyup):

$('#myTextArea').keyup(_.debounce(function() {
  alert('hello');
}, 500));
1
votes

I would personally use a setInterval that would monitor the textarea for changes and only perform AJAX callbacks when it detects a change is made. every second should be fast and slow enough.

0
votes

If you're interested in jQuery solutions, jQuery Suggest is a good implementation.

No sense reinventing the wheel every time.

0
votes

If you are using this functionality alot around the site and don't want to keep track of all of the setTimeout refs etc then i packaged this up into a plugin available here.

The plugin adds a few options to the normal $.ajax method for buffering and canceling previous ajax calls.