220

While working with browser events, I've started incorporating Safari's touchEvents for mobile devices. I find that addEventListeners are stacking up with conditionals. This project can't use JQuery.

A standard event listener:

/* option 1 */
window.addEventListener('mousemove', this.mouseMoveHandler, false);
window.addEventListener('touchmove', this.mouseMoveHandler, false);

/* option 2, only enables the required event */
var isTouchEnabled = window.Touch || false;
window.addEventListener(isTouchEnabled ? 'touchmove' : 'mousemove', this.mouseMoveHandler, false);

JQuery's bind allows multiple events, like so:

$(window).bind('mousemove touchmove', function(e) {
    //do something;
});

Is there a way to combine the two event listeners as in the JQuery example? ex:

window.addEventListener('mousemove touchmove', this.mouseMoveHandler, false);

Any suggestions or tips are appreciated!

isherwood
  • 58,414
  • 16
  • 114
  • 157
johnkreitlow
  • 2,203
  • 2
  • 14
  • 6

10 Answers10

177

Some compact syntax that achieves the desired result, POJS:

   "mousemove touchmove".split(" ").forEach(function(e){
      window.addEventListener(e,mouseMoveHandler,false);
    });
Collin Anderson
  • 14,787
  • 6
  • 68
  • 57
Isaac
  • 11,409
  • 5
  • 33
  • 45
  • 233
    Or directly `['mousemove', 'touchmove'].forEach(...)` – Dan Dascalescu Jun 03 '15 at 14:46
  • 1
    +1 AND it doesn't need «ECMAScript 2015 arrow functions » extension! ;-) – Pedro Ferreira Mar 08 '16 at 02:39
  • 3
    @zuckerburg To start with, instead of hardcoding the array you hardcoded the strning and then splitted it, are you really sure that this is the way to go? Are you sure that this is the most readable way to write this? `['mousemove', 'touchmove'].forEach(function(event) { window.addEventListener(event, handler);});` would not only be way more readable but also would be much faster not having to split the string and then running a function for each item in the resulting array. – Iharob Al Asimi Jan 10 '19 at 15:08
  • 3
    @IharobAlAsimi this code was written over 4 years ago, i've found my spacebar in that time. The string split was to relate to the OP's use of a string – Isaac Jan 10 '19 at 23:56
  • 1
    @IharobAlAsimi I didn't write the code. You said it was unreadable. Clearly it's not since you and I are both capable of reading the code. This is like arguing over grammar. 99% of all programmers are able to read the code quite nicely – zuckerburg Jan 14 '19 at 09:44
  • @PedroFerreira Agree about ECMAScript 2015 extensions. Please help reduce global heating by not using them. We should not be forcing people to buy new computers and phones because their old ones don't work with 'modern' websites. – Jake Dec 18 '22 at 00:35
133

In POJS, you add one listener at a time. It is not common to add the same listener for two different events on the same element. You could write your own small function to do the job, e.g.:

/* Add one or more listeners to an element
** @param {DOMElement} element - DOM element to add listeners to
** @param {string} eventNames - space separated list of event names, e.g. 'click change'
** @param {Function} listener - function to attach for each event as a listener
*/
function addListenerMulti(element, eventNames, listener) {
  var events = eventNames.split(' ');
  for (var i=0, iLen=events.length; i<iLen; i++) {
    element.addEventListener(events[i], listener, false);
  }
}

addListenerMulti(window, 'mousemove touchmove', function(){…});

Hopefully it shows the concept.

Edit 2016-02-25

Dalgard's comment caused me to revisit this. I guess adding the same listener for multiple events on the one element is more common now to cover the various interface types in use, and Isaac's answer offers a good use of built–in methods to reduce the code (though less code is, of itself, not necessarily a bonus). Extended with ECMAScript 2015 arrow functions gives:

function addListenerMulti(el, s, fn) {
  s.split(' ').forEach(e => el.addEventListener(e, fn, false));
}

A similar strategy could add the same listener to multiple elements, but the need to do that might be an indicator for event delegation.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
RobG
  • 142,382
  • 31
  • 172
  • 209
  • 5
    Thank you for the sentence `"It is not common to add the same listener for two different events on the same element."` Sometimes (greener) devs need to hear this to know they're doing it right :) – Ivan Durst Jun 10 '15 at 00:51
  • 2
    Don't you mean "it is not UNcommon"? – dalgard Feb 25 '16 at 10:04
  • 3
    @dalgard—it's more common now that touch devices are more common, but only for a limited number of events (such as mousemove and touchmove). That it is required shows the short sightedness of naming events after physical items, pointer move might have been more appropriate. It can be implemented by touch or other device. Before mice (or mouses) there were x/y wheels, some I've used had hand and foot wheels (1970s Stecometer). There are also track balls and spheres, some move in 3d. – RobG Feb 25 '16 at 11:27
  • @RobG: I find that it often makes code more DRY to attach the same listener to multiple events; it's definitely not a sign that you're doing anything wrong. One example of types that I would assign a single listener to is "cut paste keyup input change". – dalgard Feb 25 '16 at 12:06
  • @dalgard—if you're attaching listeners for all those events on one element you should seriously question whether there's a more appropriate alternative. That you even need to consider doing it shows the shortcomings of the API you're dealing with. – RobG Feb 25 '16 at 22:41
  • 3
    @RobG: The API I'm dealing with is the DOM, and yes, its shortcomings are legion :) But it may also simply be a question of avoiding duplicate code in listeners that are similar. – dalgard Feb 28 '16 at 20:20
  • Be carefull with «ECMAScript 2015 arrow functions » extension AND the usual trouble-maker IE. It should be more saliented, in the text - suggestion. – Pedro Ferreira Mar 08 '16 at 02:34
  • 1
    @PedroFerreira—yes of course, probably more than half the browsers currently in use don't support arrow functions, but they're fun to play with and there is always [*Babel*](https://babeljs.io/). ;-) – RobG Mar 08 '16 at 04:07
  • 1
    there is a mistype in updated answer code part. `s.split().forEach...` split must be with space string literal `' '` – aleha_84 Jul 14 '16 at 09:18
  • @mikemaccana—the code isn't minified. The additional code does *exactly* the same as the original code, the only difference is that it uses *forEach* and an arrow function rather than a *for* loop. The arrow function body is exactly the same as the *for* loop's body. – RobG Jan 18 '17 at 23:31
  • @mikemaccana—Ok, "deminified". Even added some doco. :-0 – RobG Jan 19 '17 at 13:05
  • What if you wanted the two events to happen first and then go to the function? – shinzou Apr 13 '17 at 14:02
107

Cleaning up Isaac's answer:

['mousemove', 'touchmove'].forEach(function(e) {
  window.addEventListener(e, mouseMoveHandler);
});

EDIT

ES6 helper function:

function addMultipleEventListener(element, events, handler) {
  events.forEach(e => element.addEventListener(e, handler))
}
pmrotule
  • 9,065
  • 4
  • 50
  • 58
18

For me; this code works fine and is the shortest code to handle multiple events with same (inline) functions.

var eventList = ["change", "keyup", "paste", "input", "propertychange", "..."];
for(event of eventList) {
    element.addEventListener(event, function() {
        // your function body...
        console.log("you inserted things by paste or typing etc.");
    });
}
user3796876
  • 181
  • 1
  • 2
18

ES2015:

let el = document.getElementById("el");
let handler =()=> console.log("changed");
['change', 'keyup', 'cut'].forEach(event => el.addEventListener(event, handler));
ChrisTheButcher
  • 189
  • 1
  • 3
4

I have a simpler solution for you:

window.onload = window.onresize = (event) => {
    //Your Code Here
}

I've tested this an it works great, on the plus side it's compact and uncomplicated like the other examples here.

  • 5
    There is only one .onload possible. Perhaps you overwrite an old listener. Depending on your environment this can be a problem. – HolgerJeromin May 29 '18 at 10:21
2

One way how to do it:

const troll = document.getElementById('troll');

['mousedown', 'mouseup'].forEach(type => {
 if (type === 'mousedown') {
  troll.addEventListener(type, () => console.log('Mouse is down'));
 }
        else if (type === 'mouseup') {
                troll.addEventListener(type, () => console.log('Mouse is up'));
        }
});
img {
  width: 100px;
  cursor: pointer;
}
<div id="troll">
  <img src="http://images.mmorpg.com/features/7909/images/Troll.png" alt="Troll">
</div>
Reza Saadati
  • 5,018
  • 4
  • 27
  • 64
  • 3
    What's the point of using array and foreach if you're gonna do `if (type === 'mousedown')`?!?! – Arad Alvand Jun 30 '21 at 19:36
  • @Arad because you can only use that statement since `type` is the parameter of `forEach()`. So without the array and without the forEach function, `type` would not be defined. – Reza Saadati Jun 30 '21 at 21:33
  • 1
    No, I mean why not just do `troll.addEventListener('mousedown', () => console.log('Mouse is down'));`. Why do you need a `forEach` if you're going to check for each event type and attach a different event handler to it?! The whole point of using a `forEach` in this case is to attach the same event handler to multiple events, obviously. – Arad Alvand Jul 01 '21 at 01:07
2

You can also use prototypes to bind your custom function to all elements

Node.prototype.addEventListeners = function(eventNames, eventFunction){
    for (eventName of eventNames.split(' '))
        this.addEventListener(eventName, eventFunction);
}

Then use it

document.body.addEventListeners("mousedown touchdown", myFunction)
VityaSchel
  • 579
  • 7
  • 18
  • It's frowned upon to modify prototypes that you didn't create. For pet projects, go right ahead, but please don't ever do this in a package you intend on sharing with others. – Joe Maffei Aug 11 '21 at 14:33
1

AddEventListener take a simple string that represents event.type. So You need to write a custom function to iterate over multiple events.

This is being handled in jQuery by using .split(" ") and then iterating over the list to set the eventListeners for each types.

    // Add elem as a property of the handle function
    // This is to prevent a memory leak with non-native events in IE.
    eventHandle.elem = elem;

    // Handle multiple events separated by a space
    // jQuery(...).bind("mouseover mouseout", fn);
    types = types.split(" ");  

    var type, i = 0, namespaces;

    while ( (type = types[ i++ ]) ) {  <-- iterates thru 1 by 1
Selvakumar Arumugam
  • 79,297
  • 15
  • 120
  • 134
-2
// BAD: One for each event - Repeat code
textarea.addEventListener('keypress', (event) => callPreview);
textarea.addEventListener('change', (event) => callPreview);

// GOOD: One run for multiple events
"keypress change".split(" ").forEach((eventName) => textarea.addEventListener(eventName, callPreview));

DEV Tiago França
  • 1,271
  • 9
  • 9