добавление нескольких слушателей событий к одному элементу

Итак, моя дилемма состоит в том, что я не хочу писать один и тот же код дважды. Один раз для события щелчка, а другой - для события touchstart.

Вот исходный код:

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

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

Как я могу это сжать? ДОЛЖЕН быть способ попроще!


person coiso    schedule 07.08.2012    source источник
comment
ES2020 ?! Хотел бы я.   -  person mix3d    schedule 22.01.2019


Ответы (13)


Возможно, вы можете использовать такую ​​вспомогательную функцию:

// 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);

[Редактировать ноя. 2020] Это довольно старый ответ. В настоящее время я решаю эту проблему с помощью объекта actions, в котором обработчики указаны для каждого типа события, data-attribute для элемента, указывающего, какое действие должно быть выполнено над ним, и одного универсального метода обработчика для всего документа (так что делегирование события).

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>

person KooiInc    schedule 07.08.2012
comment
Предупреждение: событие недоступно в функции дескриптора. Я исправил эту ошибку pastebin.com/raw/Lrcdv1ws - person higimo; 07.06.2017

Я знаю, что это старый вопрос, но я подумал, что некоторые сочтут этот подход полезным; его можно применить к любому такому повторяющемуся коду:

ES6

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

ES5

['click','ontouchstart'].forEach( function(evt) {
    element.addEventListener(evt, dosomething, false);
});
person Allan Nienhuis    schedule 25.03.2017
comment
Но как настроить таргетинг на конкретные селекторы? - person Adeerlike; 12.02.2018
comment
вы можете использовать что-то вроде: document.querySelectorAll (selectors) .forEach ((element) = ›{...}); - person Allan Nienhuis; 13.02.2018
comment
Когда я пытаюсь использовать это, функция, которую я пишу там, где вы написали do something, срабатывает при загрузке страницы. Это почему? Несмотря на то, что полная строка: burgerElement.addEventListener(event, alertFunction(), false), а 'burgerelement' - var burgerElement = document.querySelector('.burger'); - person Streching my competence; 22.01.2019
comment
вы выполняете функцию alertFunction () и назначаете ее возвращаемое значение (которое не определено) обработчику событий. не выполняйте функцию (с ()), просто передайте имя функции / ссылку в функцию eventListener. Обратите внимание на то, что в моем примере dosomething не имеет () после него. - person Allan Nienhuis; 19.02.2019

Вы можете просто определить функцию и передать ее. Анонимные функции ни в коем случае не являются особенными, все функции могут передаваться как значения.

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

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

function handler(event) {
    do_something();
}
person Esailija    schedule 07.08.2012

Для большого количества событий это может помочь:

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

Обновление от 06/2017:

Теперь, когда новые языковые функции стали более доступными, вы можете упростить добавление ограниченного списка событий с одним слушателем.

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));

Если вы хотите обрабатывать множество событий и предъявлять разные требования к слушателям, вы также можете передать объект о котором большинство людей склонны забывать.

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);

Обновление 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))
person Torsten Walter    schedule 07.08.2012
comment
зачем string.split(), когда можно просто получить массив строк без каких-либо затрат на вычисления? - person mix3d; 22.01.2019
comment
@Torsten, а разве вам не нужна строка в качестве первого аргумента для идентификации события? Если так, это побеждает цель - person Pacerier; 20.05.2019
comment
@ mix3d Вы правы, что определение массива лучше, и это лучший вариант для производственного кода. Разделение строки полезно в некоторых случаях, однако, когда вы читаете списки слов из строк или вам нужно часто менять строки и вы хотите сэкономить на вводе и чтении нескольких символов. - person Torsten Walter; 24.05.2019
comment
@Pacerier, ты прав, этот баг долгое время оставался незамеченным. Вы также можете определить простой объект вместо переключателя и использовать его клавиши для прослушивания событий. Удаление прослушивателей событий также становится проще, если у вас есть объект, потому что вам не нужно отслеживать точного прослушивателя, а просто нужно передать объект. - person Torsten Walter; 24.05.2019

Если ваша функция do_something на самом деле что-то не делает с любыми заданными аргументами, вы можете просто передать ее как обработчик события.

var first = document.getElementById('first');
first.addEventListener('touchstart', do_something, false);
first.addEventListener('click', do_something, false);
person Mattias Buelens    schedule 07.08.2012
comment
Я просто подумал, что может быть более быстрый способ ... что-то вроде: ('touchstart, click', do_something, false) - person coiso; 07.08.2012
comment
@ PedroEsperança Нет. Я согласен, было бы неплохо иметь возможность передавать массив строк типа ['touchstart','click'], но такого нет. - person Mattias Buelens; 08.08.2012

Самым простым решением для меня было передать код в отдельную функцию, а затем вызвать эту функцию в прослушивателе событий, что работает как шарм.

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
})
person Armin    schedule 14.03.2019

У меня есть небольшое решение, которое прикрепляется к прототипу

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

Позволяет присвоить ему строку или массив. Строку можно разделить пробелом (''), запятой (',') ИЛИ точкой с запятой (';')

person Strauss Bornman    schedule 21.09.2018
comment
как правило, переопределение прототипа - плохая практика (потенциальный конфликт в будущем при изменении спецификаций), но тем не менее идеальная функция - person mix3d; 22.01.2019

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

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

Вы должны использовать e.stopPropagation(), потому что в противном случае ваша функция будет запущена дважды на мобильном устройстве.

person John Balvin Arias    schedule 09.06.2018

Я только что сделал эту функцию (намеренно уменьшенную):

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

Использование:

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

Отличие от других подходов в том, что функция обработки определяется только один раз и затем передается каждому addEventListener.

РЕДАКТИРОВАТЬ:

Добавление неминифицированной версии, чтобы сделать ее более понятной. Уменьшенная версия предназначалась только для копирования и использования.

((element, event_names, handler) => {

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

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

    // function body

});
person Ian Pollak    schedule 06.11.2018
comment
Зачем уменьшать при показе примера? Это только ухудшает понимание. - person mix3d; 22.01.2019
comment
Отредактировано для наглядности. Спасибо за вклад. - person Ian Pollak; 24.01.2019

Наполовину связанный, но это для инициализации одного уникального прослушивателя событий для каждого элемента.

Вы можете использовать ползунок для отображения значений в реальном времени или проверить консоль. В элементе <input> у меня есть тег attr с именем data-whatever, поэтому вы можете настроить эти данные, если хотите.

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>

person Vincent Tang    schedule 29.11.2018

Это мое решение, в котором я имею дело с несколькими событиями в моем рабочем процессе.

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>

person Luis Febro    schedule 13.08.2019

Как насчет чего-то вроде этого:

['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
            }
      })
*/
person Hasan A Yousef    schedule 28.05.2020

Эта мини-библиотека javascript (1,3 КБ) может делать все это.

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

nor.event(["#first"], ["touchstart", "click"], [doSomething, doSomething]);
person Norair    schedule 27.05.2018