HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Removing jQuery event handler from single element bound by delegate

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
boundremovingjquerysingleelementdelegatefromhandlerevent

Problem

I'm binding a handler to the keyup event of all input and textareas in a document:

$(document).on('keyup','input,textarea',$.debounce(600, editor.handleGlobalChange));


I don't want the handler to fire on a specific input field. So I want to unbind the keyup event:

$('#afield').off('keyup');


However this does not unbind the keyup event, the handler is still called. I assume this is because there is no keyup event bound to the $('#afield') element, the event is just bubbling up to the $(document) where the actual handler is bound.

So to stop it triggering I can do the following:

$('#afield').on('keyup',function(e) {
    e.stopImmediatePropagation();
});


This works, but it feels grim, setting a handler to stop another.

Is this acceptable or should I just no bind the event in the first place?

I could ignore certain classes or a custom data-nokeyup attribute:

$(document).on('keyup','input,textarea,not([data-nokeyup]',$.debounce(600, editor.handleGlobalChange));


I have to use a delegated event handler because my inputs and textareas are created after the initialisation code is run. $('input,textarea').keyup(...) would return no elements. $(document).on('keyup','input,textarea',...) catches all keyup events and inspects the event's target element.

Solution

Well, as far as I can tell, there are 3 ways to deal with this; you've mentioned 2 of them already:

  • Pre-empt the event for a specific element by adding another handler,



  • Scope the event handler differently to exclude a specific element, or



  • Do the filtering in the common handler



(Edit: All of this is assuming you really want to keep the document-level, event bubble handler, rather than go with mseancole's straightforward solution)

The 3rd option would look something like:

$(document).on('keyup','input,textarea', function (event) {
  if( event.type === 'sometype' && this === someElement ) {
    return;
  }
  $.debounce(600, editor.handleGlobalChange).call(this, event);
});


... which isn't pretty either. Of course you could make it fancier by maintaining a list of event type/element combos to be ignored, rather than hardcoding it, but... eh, seems like a lot of work.

I'd say go with option 1, but perhaps wrap that bit of logic in a function or plugin. I.e. something that would let you say:

$(document).on('keyup','input,textarea',$.debounce(600, editor.handleGlobalChange));
$('#afield').ignore('keyup');


.ignore() would then set the event handler that kills the event in its tracks. In your case, you can use stopPropagation instead of stopImmediatePropagation. That way, you can still attach other handlers to that specific element, without ignore eating the event.

Edit: Now that I think about it, ignore is a bad name. Too strong a word. dontBubble is accurate, but I'm a stickler for using proper apostrophes, and doNotBubble sounds too aggressive. Maybe popBubble? Too cutesy? Maybe just something like halt? debubble? Yessss, I'm thinking way too much about this :)

Here's a demo. It's in CoffeeScript, because... well, because I like CoffeeScript. The general idea should be clear enough though. Here's the code in case jsfiddle implodes

makeNoise = ->
  $('pre').append "You typed!"

# 4-line jQuery plugin
$.fn.ignore = (eventType) ->
  $(this).each ->
    $(this).on eventType, (event) ->
      event.stopPropagation();

$ ->
  # General event handler for all inputs
  $(document).on 'keyup', 'input', makeNoise

  # Exempt a specific element
  $('#quiet').ignore 'keyup'

  # Handlers added *directly* to the #quiet element
  # are still called, only the bubbling is silenced.
  $('#quiet').on 'keyup', ->
    alert "Shhhh!"
     ​


None of this is any different from what you're already doing. It's just wrapped up for convenience.

Code Snippets

$(document).on('keyup','input,textarea', function (event) {
  if( event.type === 'sometype' && this === someElement ) {
    return;
  }
  $.debounce(600, editor.handleGlobalChange).call(this, event);
});
$(document).on('keyup','input,textarea',$.debounce(600, editor.handleGlobalChange));
$('#afield').ignore('keyup');
makeNoise = ->
  $('pre').append "You typed!<br>"

# 4-line jQuery plugin
$.fn.ignore = (eventType) ->
  $(this).each ->
    $(this).on eventType, (event) ->
      event.stopPropagation();

$ ->
  # General event handler for all inputs
  $(document).on 'keyup', 'input', makeNoise

  # Exempt a specific element
  $('#quiet').ignore 'keyup'

  # Handlers added *directly* to the #quiet element
  # are still called, only the bubbling is silenced.
  $('#quiet').on 'keyup', ->
    alert "Shhhh!"
     ​

Context

StackExchange Code Review Q#16466, answer score: 4

Revisions (0)

No revisions yet.