4lackof 4lackof - 3 months ago 12
Javascript Question

JS - Debounce a delegated event

Take a look at this JS code (here's also a snippet):



//debounce function by davidwalsh: https://davidwalsh.name/javascript-debounce-function
//Returns a function, that, as long as it continues to be invoked, will not
//be triggered. The function will be called after it stops being called for
//N milliseconds. If `immediate` is passed, trigger the function on the
//leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
$(document).on('click', '#foo-one, #bar-one', debounce(function(e) {
$('#counter-one').append('<span>1</span>');
}, 350));
$('#foo-two').click(debounce(function(e) {
$('#counter-two').append('<span>2</span>');
}, 350));
$('#bar-two').click(debounce(function(e) {
$('#counter-two').append('<span>2</span>');
}, 350));
$(document).on('click', '#foo-three, #bar-three', function(e) {
var $this = $(this);
if ($this.is('#foo-three')) {
//should happen immediately
$('#counter-three').append('<span>3</span>');
}
if ($this.is('#bar-three')) {
//should debounce
debounce(function() {
$('#counter-three').append('<span>3</span>');
}, 350);
}
});

div > div {
height: 64px;
width: 64px;
border: 1px solid black;
display: inline-block;
}
#counter-one,
#counter-two,
#counter-three {
background-color: red;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div>
<div id="foo-one">Foo1</div>
<div id="bar-one">Bar1</div>
<div id="counter-one"><span>Counter1</span>
</div>
</div>
<div>
<div id="foo-two">Foo2</div>
<div id="bar-two">Bar2</div>
<div id="counter-two"><span>Counter2</span>
</div>
</div>
<div>
<div id="foo-three">Foo3</div>
<div id="bar-three">Bar3</div>
<div id="counter-three"><span>Counter3</span>
</div>
</div>





I am trying to add a debouncer to a delegated click event, only when a certain item is clicked. As you can see, I show two ways of debouncing code (above). The first is on a delegated event. It works if all of them elements being delegated should have their clicks debounced. The second way is by using undelegated clicks, and each function is delegated.


The third way is where the code stops working. In this instance, the click handler is delegated; however, only the second element delegated should debounce. I use
$(this).is()
to tell the difference between the two elements. Now, if you call the debouncer function (tku David Walsh) by itself, it does not work...


I need the event to be delegated as it needs to be updated/refreshed and per jQuery's docs (also, this question):


Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.


Does anyone know how I can accomplish a debouncing effect on one of two elements in a delegated event, while leaving the other one to not debounce when triggered?

Answer

debounce() returns a function that you have to call multiple times, and those calls will be debounced. Each function created by a debounce() call does this separately. See also Can someone explain the "debounce" function in Javascript or What does _.debounce do?.

So you have to create multiple functions via multiple debounce() calls, one per element, and call them separately.

var functions = {
  "foo-three": function() {
    //should happen immediately
    $('#counter-three').append('<span>3</span>');
  },
  "bar-three": debounce(function() {
    $('#counter-three').append('<span>3</span>');
  }, 350)
};
$(document).on('click', '#foo-three, #bar-three', function(e) {
  functions[this.id].call(this, e);
});

Delegating events doesn't buy you much in this scenario.