Як передавати аргументи для функції слухача addEventListener?


304

Ситуація дещо схожа на

var someVar = some_other_function();
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);

Проблема полягає в тому, що значення someVarне видно всередині функції слухача addEventListener, де воно, ймовірно, трактується як нова змінна.


8
Дуже чітка стаття з цього питання: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita

Не найчистіший спосіб, але робить роботу. Зауважте, що якщо someVar міг бути лише цифрою або текстом: eval ('someObj.addEventListener ("клацання", функція () {some_function (' + someVar + ');});');
Ignas2526

Просто мав це питання сьогодні - рішення , дане тут правильно (інші рішення мають такі питання , як для випуску петлі і т.д.) - stackoverflow.com/a/54731362/984471
Манохар Редді Poreddy

Відповіді:


206

З написаним вами кодом абсолютно нічого поганого. І вони, some_functionі someVarповинні бути доступними, якщо вони були доступні в контексті, коли анонімні

function() { some_function(someVar); } 

було створено.

Перевірте, чи отримує сповіщення значення, яке ви шукали, переконайтеся, що воно буде доступне в межах анонімної функції (якщо ви не маєте більше коду, який працює над тією ж someVarзмінною поруч із дзвінком addEventListener)

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);

86
Це не працює для циклу. Я завжди отримую останнє значення, а не те, що належало до цієї ітерації. Будь-яке рішення?
iMatoria

6
Хтось знає, чому це не працює в циклі? У чому причина такої поведінки?
Морфідон

3
@Morfidon, оскільки функція з глобальними змінними виступає як закриття в JavaScript, а це означає, що вони запам'ятовують навколишнє середовище за межами своєї лексичної сфери. Якщо ви просто створюєте різні функції в одному середовищі, вони посилаються на одне середовище.
помилки94

16
@Morfidon: У циклі значення someVar - це не значення, яке воно мало під час додавання слухача, а значення, яке воно має при виконанні слухача. Коли слухач виконаний, цикл вже закінчився, тому значення someVar буде значенням, яке воно мало, коли цикл закінчився.
www.admiraalit.nl

4
@iMatoria я тільки що виявив , що створення з bound functionдопомогою .bind()методу дозволить вирішити проблему з петлями developer.mozilla.org/en/docs/Web/JavaScript/Reference / ...
Люк T О'Брайен

353

Чому б просто не отримати аргументи з цільового атрибуту події?

Приклад:

const someInput = document.querySelector('button');
someInput.addEventListener('click', myFunc, false);
someInput.myParam = 'This is my parameter';
function myFunc(evt)
{
  window.alert(evt.currentTarget.myParam);
}
<button class="input">Show parameter</button>

JavaScript - орієнтована на прототип мова, пам’ятайте!


16
Це правильна відповідь, оскільки вона дозволить нам використовуватись після функції "deleteEventListener".
user5260143

14
Чи не повинно бути evt.currentTarget.myParam? Якщо всередині 'someInput' є ще один елемент, evt.targetможливо, це стосується внутрішнього елемента. ( jsfiddle.net/qp5zguay/1 )
Гербертуш

Це зберігає this! Використання цього методу в typecript вимагає, щоб елемент був anyабо створив підтип.
Старий Бадман Грей

1
Мої змінні постійно повертаються як невизначені ... будь-які думки, як це виправити?
nomaam

1
Якщо addEventListenerє document, evt.target.myParamне працював для мене. Мені довелося використовувати evt.currentTarget.myParamзамість цього.
turrican_34

67

Це питання давнє, але я подумав, що запропоную альтернативу, використовуючи .bind () ES5 - для нащадків. :)

function some_func(otherFunc, ev) {
    // magic happens
}
someObj.addEventListener("click", some_func.bind(null, some_other_func), false);

Просто майте на увазі, що вам потрібно налаштувати функцію слухача з першим парам, як аргумент, який ви передаєте в зв'язок (ваша інша функція), а другий парам - це подія (замість першої, як це було б) .


1
Function.prototype.bind () дійсно найкращий спосіб вирішити цю проблему. Крім того, він працює інтуїтивно всередині циклів - ви отримуєте потрібну лексичну область. Ніяких анонімних функцій, IIFE або спеціальних властивостей, прикріплених до об'єктів.
Клінт Пахл

Див. Плюси та мінуси IIFE vs bind () .
Клінт Пахл

1
Якщо Function.prototype.bind()ви не можете видалити слухача подій , краще замість цього скористайтеся функцією currying (див. Відповідь @ tomcek112)
pldg

ПРИМІТКА: some_other_funcце змінна, ви можете передавати будь-яке значення, яке ви хочете some_func.
hitautodestruct

28

Ви можете просто зв'язати всі необхідні аргументи із 'bind':

root.addEventListener('click', myPrettyHandler.bind(null, event, arg1, ... ));

Таким чином , ви завжди будете отримувати event, arg1і інші речі , передані myPrettyHandler.

http://passy.svbtle.com/partial-application-in-javascript-using-bind


Дякую! Я вже намагався, .bind()але без нуля, як перший парам. яка не працювала.
Ларфоїд

немає необхідності null, він чудово працює .bind(event, arg1), принаймні, з VueJS.
DevonDahon

27

Досить старе питання, але у мене було те саме питання і сьогодні. Найбільш чітке рішення, яке я знайшов - це використовувати поняття каррінг.

Код для цього:

someObj.addEventListener('click', some_function(someVar));

var some_function = function(someVar) {
    return function curried_func(e) {
        // do something here
    }
}

Називаючи функцію curry, вона дозволяє викликати Object.removeEventListener, щоб скасувати реєстрацію eventListener в наступний час виконання.


4
Радий зіткнутися з цією відповіддю, згадавши про функцію викривлення. Як би ви видалили слухача подій?
боб

3
Приємно бачити гарну термінологію. Ви повинні мати змогу видалити слухача подій, назвавши функцію curried. Я запропоную редагувати.
Меттью Брент

Ця відповідь буде реєструвати функцію стільки разів, скільки викликається addEventListener, оскільки some_function (var) щоразу повертає новостворену функцію.
Yahia

Мені не подобається думка про те, щоб назвати функцію curried, щоб видалити слухача, тому що ур має справу з двома різними просторами імен, за якими вам слід слідкувати
oldboy

19

Ви можете додавати та видаляти eventlisteners з аргументами, оголошуючи функцію змінною.

myaudio.addEventListener('ended',funcName=function(){newSrc(myaudio)},false);

newSrcце метод з myaudio, оскільки параметр funcName- це змінна назва функції

Ви можете видалити слухача за допомогою myaudio.removeEventListener('ended',func,false);


12

Ви можете передати somevar за значенням (не за посиланням) через функцію javascript, відому як закриття :

var someVar='origin';
func = function(v){
    console.log(v);
}
document.addEventListener('click',function(someVar){
   return function(){func(someVar)}
}(someVar));
someVar='changed'

Або ви можете написати звичайну функцію обгортання, таку як wrapEventCallback:

function wrapEventCallback(callback){
    var args = Array.prototype.slice.call(arguments, 1);
    return function(e){
        callback.apply(this, args)
    }
}
var someVar='origin';
func = function(v){
    console.log(v);
}
document.addEventListener('click',wrapEventCallback(func,someVar))
someVar='changed'

Ось wrapEventCallback(func,var1,var2)як:

func.bind(null, var1,var2)

1
Дякую за цю відповідь! ОП не шукала цього, але я думаю, що люди, які набирають "Як передавати аргументи до addEventListener" в Google, шукатимуть вашу відповідь. Просто потрібно трохи більше пояснень :) Я це редагую.
Сіндар

9

someVarЗначення має бути доступним лише в some_function()контексті, а не у слухача. Якщо вам подобається, щоб це було у слухача, ви повинні зробити щось на кшталт:

someObj.addEventListener("click",
                         function(){
                             var newVar = someVar;
                             some_function(someVar);
                         },
                         false);

і використовувати newVarзамість цього.

Інший спосіб - повернути someVarзначення з some_function()використання його далі у слухачі (як новий локальний var):

var someVar = some_function(someVar);

9

Ось ще один спосіб (Цей працює всередині циклів):

var someVar = some_other_function();
someObj.addEventListener("click", 

function(theVar){
    return function(){some_function(theVar)};
}(someVar),

false);

2
Це найкращий спосіб. Некрасивий, але ефективний в циклі, оскільки надсилаючи аргумент в анонімну функцію, захопить var.
боб

9

Function.prototype.bind () - це спосіб прив’язати цільову функцію до конкретної області та необов'язково визначити thisоб'єкт у межах цільової функції.

someObj.addEventListener("click", some_function.bind(this), false);

Або захопити частину лексичної області, наприклад, у циклі:

someObj.addEventListener("click", some_function.bind(this, arg1, arg2), false);

Нарешті, якщо thisпараметр не потрібен у цільовій функції:

someObj.addEventListener("click", some_function.bind(null, arg1, arg2), false);

7

Використовуйте

   el.addEventListener('click',
    function(){
        // this will give you the id value 
        alert(this.id);    
    },
false);

І якщо ви хочете передати будь-яке спеціальне значення в цю анонімну функцію, то це найпростіший спосіб зробити це

 // this will dynamically create property a property
 // you can create anything like el.<your  variable>
 el.myvalue = "hello world";
 el.addEventListener('click',
    function(){
        //this will show you the myvalue 
        alert(el.myvalue);
        // this will give you the id value 
        alert(this.id);    
    },
false);

Чудово працює в моєму проекті. Сподіваюсь, це допоможе


Так, безумовно, це допомогло, оскільки воно також підтримувало очікувані рамки в forциклі.
j4v1

4
    $form.addEventListener('submit', save.bind(null, data, keyword, $name.value, myStemComment));
    function save(data, keyword, name, comment, event) {

Ось як я правильно пройшов захід.


Відмінно, ось що я майже робив висновок - просто помилково пройшов додаткову подію у зв’язку, коли її немає (як у кутовій), яка автоматично приходить у цьому випадку.
Манохар Редді Поредді

3

Надсилання аргументів функції зворотного виклику eventListener вимагає створення ізольованої функції та передачі аргументів на цю ізольовану функцію.

Ось приємна маленька помічна функція, яку ви можете використовувати. Спираючись на приклад "привіт, світ", наведений вище.)

Одне, що також потрібно, - це підтримувати посилання на функцію, щоб ми могли видалити слухача чисто.

// Lambda closure chaos.
//
// Send an anonymous function to the listener, but execute it immediately.
// This will cause the arguments are captured, which is useful when running 
// within loops.
//
// The anonymous function returns a closure, that will be executed when 
// the event triggers. And since the arguments were captured, any vars 
// that were sent in will be unique to the function.

function addListenerWithArgs(elem, evt, func, vars){
    var f = function(ff, vv){
            return (function (){
                ff(vv);
            });
    }(func, vars);

    elem.addEventListener(evt, f);

    return f;
}

// Usage:

function doSomething(withThis){
    console.log("withThis", withThis);
}

// Capture the function so we can remove it later.
var storeFunc = addListenerWithArgs(someElem, "click", doSomething, "foo");

// To remove the listener, use the normal routine:
someElem.removeEventListener("click", storeFunc);

Ця відповідь - з 15 року, але саме це мені потрібно було вирішити з використанням гачка useRef. Якщо ви використовуєте гачок для повторного користування і вам потрібен слухач, який ви можете очистити від демонтажу компонента, це все. 4-й аргумент storeFuncповинен бути вашою змінною ref. Використовуйте видалення слухача таким чином, як це, і ви готові йти:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Один із способів зробити це зовнішньою функцією :

elem.addEventListener('click', (function(numCopy) {
  return function() {
    alert(numCopy)
  };
})(num));

Цей спосіб загортання анонімної функції в круглих дужках та виклик її відразу називається IIFE (вираз функцій негайно викликаних функцій)

Ви можете перевірити приклад з двома параметрами в http://codepen.io/froucher/pen/BoWwgz .

catimg.addEventListener('click', (function(c, i){
  return function() {
    c.meows++;
    i.textContent = c.name + '\'s meows are: ' + c.meows;
  }
})(cat, catmeows));

3

Якщо я не помиляюсь, використовуючи функцію виклику за допомогою, bindфактично створюється нова функція, яка повертається bindметодом. Це призведе до виникнення проблем пізніше або якщо ви хочете видалити слухача події, оскільки це в основному як анонімна функція:

// Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', myCallback);
someObject.removeEventListener('event', myCallback);

// Not Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', function() { myCallback });
someObject.removeEventListener('event', /* can't remove anonymous function */);

Тож майте це на увазі.

Якщо ви використовуєте ES6, ви можете зробити те саме, що було запропоновано, але трохи чистіше:

someObject.addEventListener('event', () => myCallback(params));

3

приємно одна лінія альтернатива

element.addEventListener('dragstart',(evt) => onDragStart(param1, param2, param3, evt));
function onDragStart(param1, param2, param3, evt) {

 //some action...

}

2

Спробуйте також (IE8 + Chrome. Я не знаю для FF):

function addEvent(obj, type, fn) {
    eval('obj.on'+type+'=fn');
}

function removeEvent(obj, type) {
    eval('obj.on'+type+'=null');
}

// Use :

function someFunction (someArg) {alert(someArg);}

var object=document.getElementById('somObject_id') ;
var someArg="Hi there !";
var func=function(){someFunction (someArg)};

// mouseover is inactive
addEvent (object, 'mouseover', func);
// mouseover is now active
addEvent (object, 'mouseover');
// mouseover is inactive

Сподіваюся, немає помилок :-)


Наскільки важко було б дати повну відповідь? Чи слід перевірити це на FF? Ну, я не буду турбуватися ...
StefanNch

2

Я застряг у цьому, коли використовував його в циклі для пошуку елементів та додавання до нього списку. Якщо ви використовуєте його в циклі, то це буде прекрасно працювати

for (var i = 0; i < states_array.length; i++) {
     var link = document.getElementById('apply_'+states_array[i].state_id);
     link.my_id = i;
     link.addEventListener('click', function(e) {   
        alert(e.target.my_id);        
        some_function(states_array[e.target.my_id].css_url);
     });
}

2

У 2019 році багато api змінюється, найкраща відповідь вже не працює, без виправлення помилок.

поділитися деяким робочим кодом.

Натхненна всією вищезгаданою відповіддю.

 button_element = document.getElementById('your-button')

 button_element.setAttribute('your-parameter-name',your-parameter-value);

 button_element.addEventListener('click', your_function);


 function your_function(event)
   {
      //when click print the parameter value 
      console.log(event.currentTarget.attributes.your-parameter-name.value;)
   }

1

Всередині всіх функцій існує спеціальна змінна: аргументи . Ви можете передати свої параметри як анонімні параметри та отримати доступ до них (на замовлення) через аргументи змінну .

Приклад:

var someVar = some_other_function();
someObj.addEventListener("click", function(someVar){
    some_function(arguments[0]);
}, false);

Хм ... Що є причиною протистороннього руху? Якщо ви не шукали цього, то, будь ласка, поясніть чіткіше, що ви маєте на увазі (я знаю, що на питання вже відповіли). Але чи не відповідає мій код того, що ви просили? Спеціальна змінна "аргументи" надає вам доступ до всіх параметрів всередині функції.
StanE

1
    var EV = {
        ev: '',
        fn: '',
        elem: '',
        add: function () {
            this.elem.addEventListener(this.ev, this.fn, false);
        }
    };

    function cons() {
        console.log('some what');
    }

    EV.ev = 'click';
    EV.fn = cons;
    EV.elem = document.getElementById('body');
    EV.add();

//If you want to add one more listener for load event then simply add this two lines of code:

    EV.ev = 'load';
    EV.add();

1

Наступний підхід спрацював для мене добре. Змінено звідси .

function callback(theVar) {
  return function() {
    theVar();
  }
}

function some_other_function() {
  document.body.innerHTML += "made it.";
}

var someVar = some_other_function;
document.getElementById('button').addEventListener('click', callback(someVar));
<!DOCTYPE html>
<html>
  <body>
    <button type="button" id="button">Click Me!</button>
  </body>
</html>


0

Наступна відповідь правильна, але наведений нижче код не працює в IE8, якщо припустимо, що ви стиснули файл js за допомогою yuicompressor. (Насправді, досі більшість американських народів, які використовують IE8)

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click",
                         function(){
                          some_function(someVar);
                         },
                         false);

Отже, ми можемо виправити вищезазначене питання наступним чином, і воно добре працює у всіх браузерах

var someVar, eventListnerFunc;
someVar = some_other_function();
eventListnerFunc = some_function(someVar);
someObj.addEventListener("click", eventListnerFunc, false);

Сподіваюся, було б корисно для того, хто стискає файл js у виробничому середовищі.

Щасти!!


0

Наступний код добре працював для мене (firefox):

for (var i=0; i<3; i++) {
   element = new ...   // create your element
   element.counter = i;
   element.addEventListener('click', function(e){
        console.log(this.counter);
        ...            // another code with this element
   }, false);
}

Вихід:

0
1
2

Що в світі це?
NiCk Newman


0

Напевно, не оптимально, але досить просто для тих, хто не надто кмітливий. Покладіть функцію, яка викликає addEventListener, у свою функцію. Таким чином, будь-які значення функції, передані в нього, зберігають власну область, і ви можете перебирати цю функцію скільки завгодно.

Приклад Я працював з читанням файлів, як мені потрібно було захопити та переглянути попередній перегляд зображення та імені файлу. У мене знадобилося деякий час, щоб уникнути асинхронних проблем при використанні декількох типів завантаження файлів. Я випадково побачив би те саме "ім'я" на всіх рендерах, незважаючи на завантаження різних файлів.

Спочатку вся функція readFile () знаходилася в межах функції readFiles (). Це спричинило проблеми асинхронного обстеження.

    function readFiles(input) {
      if (input.files) {
        for(i=0;i<input.files.length;i++) {

          var filename = input.files[i].name;

          if ( /\.(jpe?g|jpg|png|gif|svg|bmp)$/i.test(filename) ) {
            readFile(input.files[i],filename);
          }
       }
      }
    } //end readFiles



    function readFile(file,filename) {
            var reader = new FileReader();

            reader.addEventListener("load", function() { alert(filename);}, false);

            reader.readAsDataURL(file);

    } //end readFile

0

просто хотів би додати. якщо хтось додає функцію, яка оновлює прапорці слухачеві подій, вам доведеться використовувати event.targetзамість цього, thisщоб оновлювати прапорці.


0

У мене дуже спрощений підхід. Це може працювати для інших, як це допомогло мені. Це ... Коли у вас є кілька елементів / змінних, призначених одній і тій же функції, і ви хочете пройти посилання, найпростішим рішенням є ...

function Name()
{

this.methodName = "Value"

}

Це воно. Це працювало для мене. Так просто.


-1

Інша альтернатива, можливо, не така елегантна, як використання палітурки, але вона справедлива для подій у циклі

for (var key in catalog){
    document.getElementById(key).my_id = key
    document.getElementById(key).addEventListener('click', function(e) {
        editorContent.loadCatalogEntry(e.srcElement.my_id)
    }, false);
}

Він перевірений на розширення з хрому Google і, можливо, e.srcElement повинен бути замінений на e.source в інших браузерах

Я знайшов це рішення за допомогою коментаря, опублікованого Imatoria, але не можу визначити його корисним, оскільки мені не вистачає репутації: D


-1

Це рішення може корисно виглядати

var some_other_function = someVar => function() {
}

someObj.addEventListener('click', some_other_function(someVar));

або прив'язувати валідні речі також буде добре

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.