5

I have a button that already has an onclick-event and an assigned function. I wish to add another function call in front of it. I can imagine it should be possible to fiddle around with the onclick attribute (attr), but I do not think this is best practice.

What would you consider best practice to prepend a function call on an existing onclick-event?

Niels Brinch
  • 3,033
  • 9
  • 48
  • 75
  • possible duplicate of [jQuery events: prepend a callback handler to already existing ones](http://stackoverflow.com/questions/9052349/jquery-events-prepend-a-callback-handler-to-already-existing-ones) – Piotr Dobrogost Apr 14 '14 at 14:01

6 Answers6

10

If you are not afraid to mess with the guts of jQuery:

// "prepend event" functionality as a jQuery plugin
$.fn.extend({
  prependEvent: function (event, handler) {
    return this.each(function () {
      var events = $(this).data("events"), 
          currentHandler;

      if (events && events[event].length > 0) {
        currentHandler = events[event][0].handler;
        events[event][0].handler = function () {
          handler.apply(this, arguments);
          currentHandler.apply(this, arguments);
        }      
      }
    });
  }
});

$("#someElement").prependEvent("click", function () {
    whatever();
});​

See it live: http://jsfiddle.net/Tomalak/JtY7H/1/

Note that there must already be a currentHandler or the function will not do anything.

Disclaimer: I consider this a hack. It depends on jQuery internals that might change in the next version of the library. It works now (I tested jQuery 1.7.2), but don't be surprised if it breaks with a future version of jQuery.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • I spent last 3 hours trying to "prepend an event". @Tomalak, you rock! – frnhr Oct 22 '12 at 06:10
  • 1
    `//whatever`, really :-) – Aaron Blenkush Mar 25 '14 at 06:01
  • I realize this answer is 8 years old, but yeah, this stopped working somewhere between 1.7.2 and 1.9.1. – Regular Jo Mar 28 '20 at 16:34
  • @RegularJoe See disclaimer. ¯\\_(ツ)_/¯ – Tomalak Mar 28 '20 at 22:05
  • Haha, I know, just thought you might wanna add a note ^^. It's interesting that neither jQuery or Javascript seems to have a simple way to do this 8 years later. – Regular Jo Mar 29 '20 at 15:19
  • 1
    @RegularJoe ...but the same approach I did back then still applies: Dig through the jQuery source code, find the location where the event handlers are stored, write a function to sneak your own event handler in there. Unless of course they made all of this unavailable through private variables, in which case it's tough luck. :) – Tomalak Mar 29 '20 at 15:57
6

I've written a short example here:

​$("button").click(function() { // this is already existing
    if ($("span").length) {
        alert("ok");
    }
});

var clicks = $("button").data("events").click.slice();
$("button")
    .unbind("click")
    .click(function() { // this is the new one which should be prepended
        $("body").append($("<span>"));
    });
$.each(clicks, function(i, v) {
    $("button").click(v);
});

noob
  • 8,982
  • 4
  • 37
  • 65
2

I don't think there's a way to prepend a function call on an existing onclick event, however you can try this:

$('#foo').unbind('click');
$('#foo').click(function() { 
   // do new function call..
   // call original function again...
});

Hope that helps.

marknatividad
  • 630
  • 8
  • 15
  • Can I somehow 'get' the current click method, unbind, call my own method and then call 'whatever' was already in the click method? – Niels Brinch Jun 05 '12 at 11:08
  • As of Jquery 3.0 you should use `.off()` instead of `.unbind()`. But for my use case, i.e. removing all existing `click` event handlers so that I can replace them with my own, this was just the ticket. – Stefan Becker Feb 26 '19 at 13:55
1

You can add function on single event but I try to avoid two functions on onclick event. Instead you can
1. Add new code in existing function.
2. Add call to new function from existing function.

Ajinkya
  • 22,324
  • 33
  • 110
  • 161
0

This appears to work in 1.9.1 through 3.1.4. See Tomalak's awesome answer for versions before 1.9.1.

$(function() {
  $("input" ).mousedown( function() {
    console.log( "mousedown" );
  });

  $("input" ).click( function() {
    console.log( "click" );
  });
    
  $.fn.extend({exposeListeners: 
      function (fn) {
        return $._data( this[0], "events") || {};
      },
    prependListener: function (types, fn) {
      var elems = this;
      $(elems).each(function() {
        var elem = this;
        var $elem = $(elem)
        $elem.on(types, fn);

        var events = $._data( $elem[0], "events")[types];

        if (events.length > 1) { // its the only event, no reason to shift
            events.unshift(events.pop());  // put last as first
        }
    
        return this;
      });
    }
  })
  
  $("#prep").click(function () {
    console.log($("input").exposeListeners());
    $("input:not(:last)").prependListener("click", function () {
      console.log('click, prepended');
    })
    $("a").prependListener("click", function () {
      console.log('click event created');
    })
    console.log($("body").exposeListeners());
  })
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="button" value="input, Will accept prepend"><br>
<input type="button" value="input, Will also accept prepend"><br>
<input type="button" value="input, Will not"><br>
<a href="#">Link with no pre-exting handlers</a><br><br>

<Button id="prep">Prepend an event</Button>

And here's a barebones version

$(function() {
  $.fn.extend({prependListener: function (types, fn) {
      var elems = this;
      $(elems).each(function() {
        var elem = this;
        var $elem = $(elem)
        $elem.on(types, fn);

        var events = $._data( $elem[0], "events")[types];

        if (events.length > 1) {
          events.unshift(events[events.length-1]);
          events.pop();
        }
    
        return this;
      });
    }
  })
})
p0stm
  • 93
  • 4
Regular Jo
  • 5,190
  • 3
  • 25
  • 47
-2

If you have binded the event like this

$(elem).on('click', functionName);

you can modify that like this:

$(elem).on('click', function(){

 anotherFunctionNameOrWhateverYouWant();

 functionName();
});
simekadam
  • 7,334
  • 11
  • 56
  • 79
  • That doesn't [work](http://jsfiddle.net/n2eaq/1/). `functionName` will be called two times – noob Apr 16 '12 at 07:09
  • Yes. In the fiddle you are actually calling it twice.. I wrote modify:-) simply delete the first line in the fiddle and it will be ok.. – simekadam Apr 16 '12 at 09:10
  • But he's asking how he can prepend the event when the first one already exists and not how he can just add two events... there are even better methods than you've mentioned to do that. – noob Apr 16 '12 at 11:00