How can I add an event listener for multiple events that only triggers once?

Viewed 158

If I have an event listener that listens for multiple events, is it possible to make it only trigger once per user action, even though the single user action spawns multiple events? Alternatively is there a way to determine if that handler has already been fired for the user action?

For example, in this jsfiddle (snippet below) the handler is called once each time I type in the text box (input) and once when I leave the text box (change).

When I click the checkbox, however, it is called twice (once for input and once for change). Although there are various ways I can mitigate this (for example by seeing what type of input the event came from and if it's a checkbox ignoring the 'input' event EDIT: or by changing the handler to only listen for the input event, which would mean the handler is called only once for this specific example, but doesn't answer my question), I'm wondering if there is a built in way to prevent multiple calls to the handler when it comes from a single user action.

$('input').on('input change', function(e) {
$('#events').append('triggered by '+$(e.target).attr('id')+'<br>');
});
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<div>
Text input: <input type="text" id="text-input">
</div>

<div>
Checkbox: <input type="checkbox" id="checkbox-input">
</div>

<div>
  <input type="button" value="Clear" onclick="$('#events').html('');">
</div>
<div id="events">
</div>

1 Answers

This is a conceptual solution only and I'm not sure of it's practicality in production.

With that disclaimer, you could store something in the element's data with a timer to reset it. For the case of a checkbox not triggering both chaange and input events this works fine. For a text input the scenario is different since the change event doesn't get triggered until the element is unfocused whereas the input event gets triggered for every key, paste or cut.

$('input').on('input change', (e) => {
   const $el = $(e.currentTarget),
         prevEvent = $el.data('prevEvent');
   if(prevEvent){
      e.stopPropagation();
      e.preventDefault();
   }else{
      $el.data('prevEvent', true);
      setTimeout(()=> $el.data('prevEvent', false), 200)
      $('#events').append(e.type + 'event triggered on '+e.currentTarget.id+'<br>');
   }
         

});
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<div>
Text input: <input type="text" id="text-input">
</div>

<div>
Checkbox: <input type="checkbox" id="checkbox-input">
</div>

<div>
  <input type="button" value="Clear" onclick="$('#events').html('');">
</div>
<div id="events">
</div>

Related