adding multiple event listeners to one element

JavascriptHtmlDom Events

Javascript Problem Overview


So my dilemma is that I don't want to write the same code twice. Once for the click event and another for the touchstart event.

Here is the original code:

document.getElementById('first').addEventListener('touchstart', function(event) {
	do_something();
	});

document.getElementById('first').addEventListener('click', function(event) {
	do_something(); 
	});

How can I compact this? There HAS to be a simpler way!

Javascript Solutions


Solution 1 - Javascript

I know this is an old question, but I thought some might find this approach useful; it could be applied to any similarly repetitive code:

ES6

['click','ontouchstart'].forEach( evt => 
    element.addEventListener(evt, dosomething, false)
);

ES5

['click','ontouchstart'].forEach( function(evt) {
    element.addEventListener(evt, dosomething, false);
});

Solution 2 - Javascript

Maybe you can use a helper function like this:

// events and args should be of type Array
function addMultipleListeners(element,events,handler,useCapture,args){
  if (!(events instanceof Array)){
    throw 'addMultipleListeners: '+
          'please supply an array of eventstrings '+
          '(like ["click","mouseover"])';
  }
  //create a wrapper to be able to use additional arguments
  var handlerFn = function(e){
    handler.apply(this, args && args instanceof Array ? args : []);
  }
  for (var i=0;i<events.length;i+=1){
    element.addEventListener(events[i],handlerFn,useCapture);
  }
}

function handler(e) {
  // do things
};

// usage
addMultipleListeners(
    document.getElementById('first'),
    ['touchstart','click'],
    handler,
    false);

[Edit nov. 2020] This answer is pretty old. The way I solve this nowadays is by using an actions object where handlers are specified per event type, a data-attribute for an element to indicate which action should be executed on it and one generic document wide handler method (so event delegation).

const firstElemHandler = (elem, evt) =>
  elem.textContent = `You ${evt.type === "click" ? "clicked" : "touched"}!`;
const actions = {
  click: {
    firstElemHandler,
  },
  touchstart: {
    firstElemHandler,
  },
  mouseover: {
    firstElemHandler: elem => elem.textContent = "Now ... click me!",
    outerHandling: elem => {
      console.clear();
      console.log(`Hi from outerHandling, handle time ${
        new Date().toLocaleTimeString()}`);
    },
  }
};

Object.keys(actions).forEach(key => document.addEventListener(key, handle));

function handle(evt) {
  const origin = evt.target.closest("[data-action]");
  return origin &&
    actions[evt.type] &&
    actions[evt.type][origin.dataset.action] &&
    actions[evt.type][origin.dataset.action](origin, evt) ||
    true;
}

[data-action]:hover {
  cursor: pointer;
}

<div data-action="outerHandling">
  <div id="first" data-action="firstElemHandler">
    <b>Hover, click or tap</b>
  </div>
  this is handled too (on mouse over)
</div>

Solution 3 - Javascript

You can just define a function and pass it. Anonymous functions are not special in any way, all functions can be passed around as values.

var elem = document.getElementById('first');

elem.addEventListener('touchstart', handler, false);
elem.addEventListener('click', handler, false);

function handler(event) {
    do_something();
}

Solution 4 - Javascript

For large numbers of events this might help:

var element = document.getElementById("myId");
var myEvents = "click touchstart touchend".split(" ");
var handler = function (e) {
	do something
};

for (var i=0, len = myEvents.length; i < len; i++) {
	element.addEventListener(myEvents[i], handler, false);
}

Update 06/2017:

Now that new language features are more widely available you could simplify adding a limited list of events that share one listener.

const element = document.querySelector("#myId");

function handleEvent(e) {
    // do something
}
// I prefer string.split because it makes editing the event list slightly easier

"click touchstart touchend touchmove".split(" ")
    .map(name => element.addEventListener(name, handleEvent, false));

If you want to handle lots of events and have different requirements per listener you can also pass an object which most people tend to forget.

const el = document.querySelector("#myId");

const eventHandler = {
    // called for each event on this element
    handleEvent(evt) {
        switch (evt.type) {
            case "click":
            case "touchstart":
                // click and touchstart share click handler
                this.handleClick(e);
                break;
            case "touchend":
                this.handleTouchend(e);
                break;
            default:
                this.handleDefault(e);
        }
    },
    handleClick(e) {
        // do something
    },
    handleTouchend(e) {
        // do something different
    },
    handleDefault(e) {
        console.log("unhandled event: %s", e.type);
    }
}

el.addEventListener(eventHandler);

Update 05/2019:

const el = document.querySelector("#myId");

const eventHandler = {
    handlers: {
        click(e) {
            // do something
        },
        touchend(e) {
            // do something different
        },
        default(e) {
            console.log("unhandled event: %s", e.type);
        }
    },
    // called for each event on this element
    handleEvent(evt) {
        switch (evt.type) {
            case "click":
            case "touchstart":
                // click and touchstart share click handler
                this.handlers.click(e);
                break;
            case "touchend":
                this.handlers.touchend(e);
                break;
            default:
                this.handlers.default(e);
        }
    }
}

Object.keys(eventHandler.handlers)
    .map(eventName => el.addEventListener(eventName, eventHandler))

Solution 5 - Javascript

Unless your do_something function actually does something with any given arguments, you can just pass it as the event handler.

var first = document.getElementById('first');
first.addEventListener('touchstart', do_something, false);
first.addEventListener('click', do_something, false);

Solution 6 - Javascript

Simplest solution for me was passing the code into a separate function and then calling that function in an event listener, works like a charm.

function somefunction() { ..code goes here ..}

variable.addEventListener('keyup', function() {
   somefunction(); // calling function on keyup event
})

variable.addEventListener('keydown', function() {
   somefunction(); //calling function on keydown event
})

Solution 7 - Javascript

I have a small solution that attaches to the prototype

  EventTarget.prototype.addEventListeners = function(type, listener, options,extra) {
  let arr = type;
  if(typeof type == 'string'){
    let sp = type.split(/[\s,;]+/);
    arr = sp;   
  }
  for(let a of arr){
    this.addEventListener(a,listener,options,extra);
  }
};

Allows you to give it a string or Array. The string can be separated with a space(' '), a comma(',') OR a Semicolon(';')

Solution 8 - Javascript

I just made this function (intentionally minified):

((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, events, handler)

Usage:

((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, ['click', 'touchstart'], (event) => {
    // function body
});

The difference compared to other approaches is that the handling function is defined only once and then passed to every addEventListener.

EDIT:

Adding a non-minified version to make it more comprehensible. The minified version was meant just to be copy-pasted and used.

((element, event_names, handler) => {
    
    event_names.forEach( (event_name) => {
        element.addEventListener(event_name, handler)
    })

})(element, ['click', 'touchstart'], (event) => {
    
    // function body

});

Solution 9 - Javascript

document.getElementById('first').addEventListener('touchstart',myFunction);

document.getElementById('first').addEventListener('click',myFunction);
    
function myFunction(e){
  e.preventDefault();e.stopPropagation()
  do_something();
}    

You should be using e.stopPropagation() because if not, your function will fired twice on mobile

Solution 10 - Javascript

This is my solution in which I deal with multiple events in my workflow.

let h2 = document.querySelector("h2");

function addMultipleEvents(eventsArray, targetElem, handler) {
        eventsArray.map(function(event) {
            targetElem.addEventListener(event, handler, false);
        }
    );
}
let counter = 0;
function countP() {
    counter++;
    h2.innerHTML = counter;
}

// magic starts over here...
addMultipleEvents(['click', 'mouseleave', 'mouseenter'], h2, countP);

<h1>MULTI EVENTS DEMO - If you click, move away or enter the mouse on the number, it counts...</h1>

<h2 style="text-align:center; font: bold 3em comic; cursor: pointer">0</h2>

Solution 11 - Javascript

What about something like this:

['focusout','keydown'].forEach( function(evt) {
        self.slave.addEventListener(evt, function(event) {
            // Here `this` is for the slave, i.e. `self.slave`
            if ((event.type === 'keydown' && event.which === 27) || event.type === 'focusout') {
                this.style.display = 'none';
                this.parentNode.querySelector('.master').style.display = '';
                this.parentNode.querySelector('.master').value = this.value;
                console.log('out');
            }
        }, false);
});

// The above is replacement of:
 /*   self.slave.addEventListener("focusout", function(event) { })
      self.slave.addEventListener("keydown", function(event) {
         if (event.which === 27) {  // Esc
            }
      })
*/

Solution 12 - Javascript

You can simply do it iterating an Object. This can work with a single or multiple elements. This is an example:

const ELEMENTS = {'click': element1, ...};
for (const [key, value] of Object.entries(ELEMENTS)) {
    value.addEventListener(key, () => {
        do_something();
    });
}

When key is the type of event and value is the element when you are adding the event, so you can edit ELEMENTS adding your elements and the type of event.

Solution 13 - Javascript

Semi-related, but this is for initializing one unique event listener specific per element.

You can use the slider to show the values in realtime, or check the console. On the <input> element I have a attr tag called data-whatever, so you can customize that data if you want to.

sliders = document.querySelectorAll("input");
sliders.forEach(item=> {
  item.addEventListener('input', (e) => {
    console.log(`${item.getAttribute("data-whatever")} is this value: ${e.target.value}`);
    item.nextElementSibling.textContent = e.target.value;
  });
})

.wrapper {
  display: flex;
}
span {
  padding-right: 30px;
  margin-left: 5px;
}
* {
  font-size: 12px
}

<div class="wrapper">
  <input type="range" min="1" data-whatever="size" max="800" value="50" id="sliderSize">
  <em>50</em>
  <span>Size</span>
  <br>
  <input type="range" min="1" data-whatever="OriginY" max="800" value="50" id="sliderOriginY">
  <em>50</em>
  <span>OriginY</span>
  <br>
  <input type="range" min="1" data-whatever="OriginX" max="800" value="50" id="sliderOriginX">
  <em>50</em>
  <span>OriginX</span>
</div>

Solution 14 - Javascript

//catch volume update
var volEvents = "change,input";
var volEventsArr = volEvents.split(",");
for(var i = 0;i<volknob.length;i++) {
	for(var k=0;k<volEventsArr.length;k++) {
	volknob[i].addEventListener(volEventsArr[k], function() {
		var cfa = document.getElementsByClassName('watch_televised');
		for (var j = 0; j<cfa.length; j++) {
			cfa[j].volume = this.value / 100;
		}
	});
}
}

Solution 15 - Javascript

This mini javascript libary (1.3 KB) can do all these things

https://github.com/Norair1997/norjs/

nor.event(["#first"], ["touchstart", "click"], [doSomething, doSomething]);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncoisoView Question on Stackoverflow
Solution 1 - JavascriptAllan NienhuisView Answer on Stackoverflow
Solution 2 - JavascriptKooiIncView Answer on Stackoverflow
Solution 3 - JavascriptEsailijaView Answer on Stackoverflow
Solution 4 - JavascriptTorsten WalterView Answer on Stackoverflow
Solution 5 - JavascriptMattias BuelensView Answer on Stackoverflow
Solution 6 - JavascriptArminView Answer on Stackoverflow
Solution 7 - JavascriptStrauss BornmanView Answer on Stackoverflow
Solution 8 - JavascriptIan PollakView Answer on Stackoverflow
Solution 9 - JavascriptJohn Balvin AriasView Answer on Stackoverflow
Solution 10 - JavascriptLuis FebroView Answer on Stackoverflow
Solution 11 - JavascriptHasan A YousefView Answer on Stackoverflow
Solution 12 - JavascriptMarco ConcasView Answer on Stackoverflow
Solution 13 - JavascriptVincent TangView Answer on Stackoverflow
Solution 14 - JavascriptNwaedobadooView Answer on Stackoverflow
Solution 15 - JavascriptNorairView Answer on Stackoverflow