如何在没有使用JQuery的情况下将多个事件绑定到一个监听器?

229

当处理浏览器事件时,我开始使用Safari的touchEvents来处理移动设备上的事件。我发现addEventListener与条件语句一起使用会导致堆积的问题。该项目不能使用jQuery。

一个标准的事件监听器:

/* 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的bind允许多个事件,使用方式如下:

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

有没有一种方法可以像 JQuery 示例中那样组合这两个事件监听器? 例如:

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

欢迎提出建议或提示!

11个回答

183

一些可以实现期望结果的简洁语法,即POJS:

   "mousemove touchmove".split(" ").forEach(function(e){
      window.addEventListener(e,mouseMoveHandler,false);
    });

242
直接使用 ['mousemove', 'touchmove'].forEach(...) - Dan Dascalescu
1
而且它不需要“ECMAScript 2015箭头函数”扩展!;-) - Pedro Ferreira
4
@zuckerburg,首先,你不是硬编码了数组,而是硬编码了字符串,然后对其进行了拆分,你确定这是正确的做法吗?你确定这种方式是最易读的写法吗?['mousemove', 'touchmove'].forEach(function(event) { window.addEventListener(event, handler);});不仅更易读,而且更快,因为它不需要拆分字符串并对结果数组中的每个项运行一个函数。 - Iharob Al Asimi
4
@IharobAlAsimi 这段代码是4年前编写的,这段时间我已经找到了我的空格键。字符串分割是为了与问题提出者使用字符串的方式相关联。 - Isaac
1
@IharobAlAsimi 我没有写这段代码。你说它难以阅读,但显然不是这样,因为你和我都能够读懂它。这就像争论语法一样。99%的程序员都能够很好地阅读代码。 - zuckerburg
@PedroFerreira 对于 ECMAScript 2015 扩展的看法我表示赞同。请不要使用它们来减少全球变暖。我们不应该强迫人们购买新的电脑和手机,因为他们的旧设备无法与“现代”网站兼容。 - Jake

135

在POJS中,你一次只能添加一个监听器。在同一个元素上为两个不同的事件添加相同的监听器并不常见。你可以编写自己的小函数来完成这项工作,例如:

/* 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(){…});

希望这能展示概念。

编辑 2016-02-25

Dalgard的评论促使我重新审视了这个问题。我想现在在一个元素上为多个事件添加相同的监听器已经更加常见,而Isaac的答案提供了一种使用内置方法来减少代码量的好方法(尽管自身较少的代码并不一定是一个优点)。使用ECMAScript 2015箭头函数扩展如下:

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

类似的策略可以将同一个监听器添加到多个元素,但这样做的需要可能是事件委托的一个指标。


5
谢谢您提供的句子:“在同一元素上为两个不同事件添加相同侦听器并不常见。” 有时(较新手的)开发人员需要听到这一点,以确保他们做得正确 :) - Ivan Durst
2
难道你的意思不是“这并不是不常见的”吗? - dalgard
3
随着触摸设备越来越普及,现在使用它们来触发事件更加常见,但仅限于有限数量的事件(如mousemove和touchmove)。这种要求表明将事件命名为物理设备的短视行为,pointer move可能更加合适。它可以被触摸或其他设备实现。在鼠标(或mouses)出现之前,有x/y轮子,一些我用过的还有手动和脚轮(1970年代的Stecometer)。还有一些跟踪球和球体,有些是三维移动的。 - RobG
3
@RobG:我正在处理的API是DOM,它的缺点确实很多 :) 但这也可能只是避免类似监听器中重复代码的问题。 - dalgard
1
@PedroFerreira 当然,目前使用的浏览器中可能超过一半都不支持箭头函数,但它们很有趣可以玩耍,而且总有 Babel。;-) - RobG
显示剩余7条评论

111

整理Isaac的回答:

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

编辑

ES6 辅助函数:

function addMultipleEventListener(element, events, handler) {
  events.forEach(e => element.addEventListener(e, handler))
}

1
FYI:map != forEach - jpoppe

18

对我而言,这段代码能够很好地工作,并且是处理具有相同(内联)函数的多个事件的最短代码。

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.");
    });
}

正是我在寻找的。谢谢! - Elharony

18

ES2015:

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

使用for...of循环的另一种方式。 - dude

4
我有一个更简单的解决方案给你:
window.onload = window.onresize = (event) => {
    //Your Code Here
}

我已经测试过它,效果很好,而且它的优点是像这里的其他示例一样紧凑和简单。


5
只能有一个 .onload。也许你会重写旧的监听器。根据你的环境,这可能会成为一个问题。 - HolgerJeromin

2

一种实现方式如下:

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>


3
使用数组和foreach的意义是什么,如果你打算这样做if (type === 'mousedown')?!?! - aradalvand
@Arad,因为你只能使用该语句,因为“type”是“forEach()”的参数。因此,如果没有数组和forEach函数,“type”将不被定义。 - Reza Saadati
1
不,我的意思是为什么不直接执行troll.addEventListener('mousedown', () => console.log('Mouse is down'));。如果你要检查每个事件类型并将不同的事件处理程序附加到它,那么为什么需要使用forEach?在这种情况下使用forEach的整个重点是将相同的事件处理程序附加到多个事件上,显然。 - aradalvand

2

你还可以使用原型将自定义函数绑定到所有元素

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

然后使用它。
document.body.addEventListeners("mousedown touchdown", myFunction)

修改你没有创建的原型是不被赞同的。对于个人项目,可以随意进行,但请不要在你打算与他人共享的程序包中这样做。 - Joe Maffei

1

AddEventListener接受一个简单的字符串,表示event.type。所以你需要编写一个自定义函数来迭代多个事件。

在jQuery中,使用.split(" ")来处理这个问题,然后迭代列表以为每个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

0
这是一个小函数,它将这个功能注入到HTMLElement类中,使其可以像其他元素方法一样访问:
HTMLElement.prototype.addEventListeners = function addEventListeners(events, handler, ...args) {
    // Allow for easy assignment of one listener to multiple events
    if ((events instanceof Array) && (handler instanceof Function)) {
        events.filter(e=>isString(e)).forEach((evtype) => {
            this.addEventListener(evtype, handler, ...args);
        });
    } else {
        throw new TypeError(`${this.constructor.name}.addEventListeners(): The events must be an Array of Strings and the handler must be a Function! (got events:${events?.constructor?.name||events} and handler:${handler?.constructor?.name||handler})`);
    }
}

这段代码将方法addEventListeners(events, handler, ...args)注入到基本的HTMLElement类中,因此每个HTML元素都会得到它。
例如,要让一个元素在控制台中打印出发生的事件,可以按照以下方式操作:
document.getElementById('elmnt-with-many-actions').addEventListeners(
    [
        'click',
        'dblclick',
        'focus',
        'blur'
    ], (event) => {
        console.log('Something happened: %O', event);
    },
    {once:true}
);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接