0

Ok so I've been doing this little hack to achieve what I need to add multiple events at different times to one particular function.

$("#handler").bind("click",function(){ alert(1); });
$("#handler").bind("click",function(){ alert(2); });
$("#handler").bind("click",function(){ alert(3); });

When I simply call $("#handler").click() it fires all those events.

Question is, can I do this without making some arbitrary html object to bind it to?

What is the equivalent to do this with a standard javascript function?

function handler(){ alert(1); }
function handler(){ alert(2); }
function handler(){ alert(3); }
handler();

^This just fires the last one, and not all 3. How can I bind multiple events to a function instead of to a DOM element?

15
  • Bind the event only once. Commented Mar 28, 2016 at 3:29
  • @Tushar Yes of course, then I wouldn't need to ask the question right? Problem, the bindings happen at different parts of run-time. But thanks for the input Commented Mar 28, 2016 at 3:29
  • have you consider using loop? it fires the last one because the last read function was the same with the first and second one. Commented Mar 28, 2016 at 3:34
  • 1
    jsfiddle.net/arunpjohny/qcre3nxg/1 Commented Mar 28, 2016 at 3:41
  • 2
    You can't have 3 functions with same name in the same scope... the last one will override the previous definitions Commented Mar 28, 2016 at 3:42

3 Answers 3

1

If you are really just looking for a way to group individual functions together into a larger "function set" that you can then trigger later all at once, this could be achieved via a Set object in javascript. Example:

// define the Set object that you want to store the functions inside:
var handlerFunctionCollection = new Set();


// add individual functions to the Set:
handlerFunctionCollection.add( function(){ console.log(1); } );
handlerFunctionCollection.add( function(){ console.log(2); } );
handlerFunctionCollection.add( function(){ console.log(3); } );

// trigger all functions in set using forEach on Set 
// and call() method on stored Function object:
handlerFunctionCollection.forEach(function(handlerFunction) {
    if(handlerFunction instanceof Function) {
        handlerFunction.call();
    }
});

Note that I'm basing this interpretation of your question based on you saying:

When I simply call $("#handler").click() it fires all those events.

which suggests you aren't actually looking for a way to bind functions directly to an event, but a way to trigger multiple functions in one place, since otherwise you wouldn't be triggering the click event manually.

A benefit of using a Set over an Array object or similar collection is that members of a Set are unique, so that if you were try to add the same function multiple times (accidentally or otherwise), the added Function object would only occur once in the Set, so that it would therefore only get triggered once when triggering the forEach, since it would only exist once in the Set rather than for each time it was added.

Sign up to request clarification or add additional context in comments.

Comments

1

In JavaScript every function is actually a Function object.

In Javascript if you define multiple functions with the same name then the last one will actually run. JavaScript functions are not polymorphic like functions in many other languages where in other languages you might have funcA(parm1) and funcA(parm1, parm2) as two separate functions with the one that gets run depending on the number of parameters passed.

In your case , you need to declare one function in a way that can handle different situations inside the function scope .

Comments

0

You are looking for the concept of Observer or Publisher/Subscriber ("PubSub").

These are pretty easy to create on your own.
Here's a slightly cleaned version of the application-only code (that might run on DOM-ready, or whatever), to run the demo code below.

const trigger = $("#Trigger");
const system = Publisher();

// add my listeners
system
.on("trigger", triggerContentUpdate)
.on("update", updateView)
.on("update", logUpdate); // note how I have two things bound to the same event

// something that kicks off the "trigger" event
// could be a timer, or an AJAX call, or anything else
trigger.onclick = () => system.fire("trigger");

Assuming you had a Publisher library and had the above listener functions in your app, this is pretty much all of the JS you'd have to write to make the demo below work.

The library I wrote as practice, and the implementation of the demo are below.

/*** LIBRARY CODE ***/

// Channel.js
const Channel = () => {
  const listeners = new Set();

  const channel = {

    listen(listener) {
      listeners.add(listener);
      return channel;
    },

    ignore(listener) {
      listeners.delete(listener);
      return channel;
    },

    notify(data) {
      listeners.forEach(react => react(data));
      return channel;
    }
  };

  return channel;
};

// Publisher.js
const Publisher = () => {
  const channels = {};

  function getChannel(name) {
    const channel = channels[name] || Channel();
    channels[name] = channel;
    return channel;
  }

  const publisher = {

    on(name, subscriber) {
      getChannel(name).listen(subscriber);
      return publisher;
    },

    off(name, subscriber) {
      getChannel(name).ignore(subscriber);
      return publisher;
    },

    fire(name, data) {
      getChannel(name).notify(data);
      return publisher;
    }

  };

  return publisher;
}

/*** END OF LIBRARY CODE ***/



/*** APPLICATION CODE ***/

// const Publisher = require("publisher"); or import Publisher from "publisher";

const $ = selector => document.querySelector(selector);

const trigger = $("#Trigger");
const input = $("#Content");
const output = $("#Result");
const log = $("#Log");

const system = Publisher();

system
  .on("trigger", () => {
    const content = input.value;
    system.fire("update", content);
  })
  .on("update", content => output.value = content)
  // normally, I never use innerHTML, and my logic and view are never attached, but this is dirt-simple now
  .on("update", content => log.innerHTML += `<li>[${ (new Date()).toISOString() }]: ${content}</li>`);

trigger.onclick = () => system.fire("trigger");

/*** END OF APPLICATION CODE ***/
<h3>Input</h3>
<div>
  <input id="Content" type="text">
  <button id="Trigger">Update</button>
</div>
<h3>Output</h3>
<output id="Result"></output>

<h3>Log</h3>
<ul id="Log"></ul>

Of course, I'm using relatively modern syntax, but nothing that can't be rewritten in older compatible code, or transpiled and given a polyfill, to go all the way down to IE7/8 support.

If I wanted to make some arbitrary object a publisher, I could do that rather quickly as well.

4 Comments

@RealWorldCoder it's 100% JavaScript. Native, normal, every-day JavaScript. You can replace all arrow functions with old-fashioned function, you can replace the Set with [ ] and add extra checks to not insert duplicate listeners (something a set does for you). You can replace all const with var. And you're back at old-fashioned JS. If you'll note, though, if you're running this in, say Chrome or FireFox, or any self-respecting browser, the example should work just fine.
@RealWorldCoder have a look at BabelJS.io which transforms ES6 (the above) into ES3 (IE8+) compatible code, when used in conjunction with a polyfill for objects like Set and Map
I only ask because it looks different than what I'm used to seeing. Granted, it may do the trick, just looks much heavier than I would expect to see.
@RealWorldCoder Heavy? Typically, Publisher and Channel that I've written there would be a separate JS library (or jQuery plugin). I have written an implementation of basically the system that runs behind addEventListener. Have a look at the bottom dozen lines (where I create $ and on); that's where I am using the library. And then have a look at/run the updated version of the snippet, where I tried to make this a little more apparent. Note that system here is just a Publisher from the library that I just invented (there are loads of libraries for these, I'm sure).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.