2

Update per comments

@adeneo: Armed with answers from this question about syntax for disabled attribute and this question for custom attributes, I felt content adding that attribute to elements other than the ones listed in the docs as ones that can be "actually disabled". So indeed some of my <a> and even <span> elements may obtain that attribute, for more unified way to address them with CSS and JS.

In an effort to be concise and future-proof (e.g. somebody adds an input or two to the page later on), I attach my click event listeners to all the similar elements at once - and handle them according to what the element is. This question was prompted by necessity to disable some elements until ajax request completes. The call gets triggered with click, and I want to prevent additional calls before the first one completes.

@Patrick Q I don't know how to convey feasibility of my intention with a really short code snippet - so I wrote an example in codepen:

example on codepen.


original question:

I would like to add a condition to my "click" event listeners to check if target element is disabled.

Some of my links have or obtain "disabled" attribute (for a number of reasons using links is more convenient - although the interaction is handled with js). In this case, I would like the click handler to just return false (i.e. preventDefault and stopPropogation) right away, so disabled link would indeed be disabled.

I could of course search for all click handler attachments in my code and add the condition in each individually - but that's really really un-DRY. Another way would be to bind/unbind the events based on the attribute - but that seems just way messy as well... I'd like to change it in one place so it just works everywhere :-).

Per jQuery docs, click for attaching listeners is just a shorthand for $.on(). So I imagine I could extend functionality of that... But HOW?

Is it even the right direction to think in?

Community
  • 1
  • 1
apprenticeDev
  • 8,029
  • 3
  • 23
  • 25
  • 2
    Have you read the docs for [.on()](http://api.jquery.com/on/)? Have you made an attempt to use it? – Patrick Q Jun 18 '14 at 16:59
  • 2
    This is the code for `on`: http://james.padolsey.com/jquery/#v=1.10.2&fn=jQuery.fn.on. You can extend it yourself to wrap the `fn`/`origFn` in your own function logic which will prevent the call to it if the `disabled` property is present. – Rory McCrossan Jun 18 '14 at 16:59
  • 2
    How is this an issue, disabled elements do not fire click events ? – adeneo Jun 18 '14 at 17:02
  • 2
    Here's proof -> **http://jsfiddle.net/LFjH9/** – adeneo Jun 18 '14 at 17:04
  • @adeneo Those are buttons, not "links" (presumably anchor elements) – Patrick Q Jun 18 '14 at 17:05
  • 1
    @PatrickQ - An anchor has no disabled property, how should we know when it's disabled ? – adeneo Jun 18 '14 at 17:06
  • @adeneo That's a question for OP. I'm just saying that your example probably isn't relevant to the question. – Patrick Q Jun 18 '14 at 17:07
  • @PatrickQ - And yet, the OP is saying the target elements have a disabled attribute, which would indicate that they are inputs/buttons, as other elements can't really be disabled. Anyway, it's easy to patch `on()`, like this **http://jsfiddle.net/K6rBy/**, but that checks for a disabled property that anchors doesn't have. – adeneo Jun 18 '14 at 17:10
  • OP should provide some relevant HTML. – Patrick Q Jun 18 '14 at 17:13
  • Thanks for the comments! Especially @PatrickQ. I'll edit the post with example and more reasoning. – apprenticeDev Jun 18 '14 at 18:05
  • @adeneo: your patch for `on()` will do the 'disabled' test on the event binding, not on event fire - so, if the element is `disabled` on page load, it will never have a binding even if it becomes enabled later, and conversely, if an element is enabled on page load, it will not be filtered correctly once it becomes `disabled`. – Aeon Jun 18 '14 at 23:00

3 Answers3

2

You can monkey patch (replace) the jQuery $.on method to add your logic, but it's going to be a pain to maintain, since any time jQuery changes, you'll need to update your patch as well.

As proof of concept, here's a working monkey patch for $.click - :

jQuery.fn['click'] = function( data, fn ) {

  var wrap = function(handler) {
    return function(e) {
      if ($(e.currentTarget).hasClass('disabled')) { 
        e.stopImmediatePropagation();
        return false;
      } else {
        handler(e); 
      } 
    }
  }

  if(arguments.length > 0) {
    if(typeof fn == 'undefined') {
      this.on('click', null, wrap(data), fn );
    } else {
      this.on('click', null, data, wrap(fn) );
    }
  } else {
    this.trigger('click');
  }
};

Note that I ended up having to reproduce the logic from the original implementation to handle the variation in possible arguments correctly.

You could also write a wrapper for $.on and force your team to use it, but someone will inevitably forget about it, and you'll end up with mysterious discrepancies that will drive people slowly mad.

In the end, you probably just want to let the browser worry about it by using form elements like <button> which act correctly when disabled, and styling them as necessary.

Aeon
  • 6,467
  • 5
  • 29
  • 31
1

I am not sure I understand the reason you want to override on, overriding Jquery.clickshould be enough.

var old = jQuery.fn.click;
jQuery.fn.click = function(e) {
    //if (e.target == 'unworthy'){ do your stuff;}
old();
};
Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
  • You're right. I'll update the title accordingly. Having read that `click` is just a shortcut for `on` I thought I had to go to the latter. However, just tried your solution to my example (linked in updated description) didn't do the trick :/ - I appending the following: var oldClick = $.fn.click; $.fn.click = function(e) { if($(e.currentTarget).attr('disabled')) { return false; } else { oldClick(); } }; – apprenticeDev Jun 18 '14 at 20:25
  • `$(e.currentTarget).attr('disabled')`, gets attribute, I assume what you want to check on is `if($(e.currentTarget).is(':disabled'))...` – Matas Vaitkevicius Jun 18 '14 at 21:41
  • Good point - but is(':disabled') works only for elements that *should* have such an attribute (linked in first paragraph of updated question). `.attr('name')` usually gives the value of that attribute. Just tested it in console, I discovered that curiously `.attr('disabled')` doesn't work at all - so I guess I might as well just add a class... But still no luck - see updated codepen: http://codepen.io/istro/pen/kJnym?editors=001 – apprenticeDev Jun 18 '14 at 22:02
  • If he only overrides `$.click`, any time his fellow developers use `$.on` in the codebase, the wrapper will not be run. – Aeon Jun 18 '14 at 22:57
1

Click handlers are executed in the order they are attached. If you can, add this before your other click handlers are attached.

$('.yourLinks').click(function(e){
    if(this.disabled){  // or whatever you are checking for
        e.stopImmediatePropagation();  // stops all other click handlers
    }
});

DEMO: http://jsfiddle.net/LxK3U/

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • Yes, I'm aware of this and this would work - but it's brittle, what if some day down the road the order got screwed up? What I'm actually trying to do is to patch "click" listener itself, so regardless of where it's added - it wouldn't execute if the element had a "disabled" attribute :) – apprenticeDev Jun 18 '14 at 20:29
  • @apprenticeDev: You'd still have to "patch" the click listener *before* binding your events. Basically use this code as the "patch". – gen_Eric Jun 18 '14 at 20:30
  • That's true enough, but that could be done once at the top of the "main" file. This "patching" individually for each set of elements I have a listener for would be redundant, also if pulled out of the relevant file it would be confusing, no? – apprenticeDev Jun 18 '14 at 21:00