Последнее обновление:
Как передать ВНЕШНИЕ аргументы (параметры) обработчикам событий в javascript
При назначении обработчиков тех или иных событий элементам html при помощи средств языка JS, нередко требуется передавать этим обработчикам те или иные аргументы в виде переменных. Это могут быть такие переменные, которые создаются вне тела обработчика.
Данная задача решается не совсем тривиально. Тут можно применить, скажем, «непосредственную» передачу аргументов, передачу через замыкание и т.д.
Мы рассмотрим 5 разных способов, которые, кажется, наиболее распространены. Чтобы не тратить время на вступление, сразу посмотрим код соответствующего тестового html-файла:
<html>
<body>
<button class="input1">Show parameter1</button>
<button class="input2">Show parameter2</button>
<button class="input3">Show parameter3</button>
<button class="input4">Show parameter4</button>
<button class="input5">Show parameter5</button>
<script>
(function(){
// 1.
var input1 = document.querySelector('button.input1');
input1.addEventListener('click', Func, false);
input1.Param = '1_method !';
function Func(e){
alert(e.target.Param + '\n - target \n'+ this.outerHTML);
alert(e.currentTarget.Param + ' - currentTarget');
e.currentTarget.removeEventListener('click', Func, false)
}
// 2.
var someVar = other_function;
var par = '2_method';
var input2 = document.querySelector('button.input2');
var x;
input2.addEventListener("click", x = function (e){
some_function(e, par, this);
}, false);
function other_function(par) {
alert(par + '\n - other_function');
input2.removeEventListener('click', x, false);
return 'OK';
}
function some_function(e, par, $this) {
alert($this.outerHTML);
alert(someVar(par) + '\n event.type='+e.type+ '\n - some_function');
}
//3.
var x3;
var input3 = document.querySelector('button.input3');
var par3 = '3_method';
input3.addEventListener("click", x3 = some_func.bind(null, null), false);
function some_func(not_used, e) {
alert(e.type+ '\n'+ par3 + '\n'+ not_used);
input3.removeEventListener('click', x3, false);
}
//4.
var input4 = document.querySelector('button.input4');
var par4 = '4_method';
var par41 = 'Also 4_method';
var func4_enclosure = function(v4, v41, e){
alert(v4 +'\n'+ v41 + '\n'+ e.type);
};
input4.addEventListener('click', (function(par4, par41){
return function(e){
func4_enclosure(par4, par41, e);
this.removeEventListener(e.type, arguments.callee, false); // Удаляем АНОНИМНЫЙ обработчик!!
};
})(par4, par41), false);
//5.
var input5 = document.querySelector('button.input5');
var param5 = '5_method';
function wrapEventCallback(callback){
var args = Array.prototype.slice.call(arguments, 1);
return function y(e){
callback.apply(this, args);
callback(this.outerHTML); //
input5.removeEventListener('click', y, false);
}
}
var func5 = function(v){
alert(v);
};
input5.addEventListener('click',wrapEventCallback(func5, param5))
})();
</script>
</body>
</html>
Вы можете скопировать себе этот код и протестировать самостоятельно. Или – понажимайте кнопки прямо на этой странице и проверьте, как передаются аргументы на примере обработчиков кликов по кнопкам.
ВСЕ обработчики будут срабатывать по одному разу, после чего они будут удалены – так сделано лишь в целях демонстрации возможностей. Но, если требуется, чтобы обработчики срабатывали и в дальнейшем, можно убрать из них строчку с removeEventListener().
Далее рассмотрим комментарии по каждому методу, чтобы было понятнее.
1 метод: используем переменную this
Вообще, смысл этой переменной зависит от контекста ее использования. В данном случае, при назначении обработчика, в эту переменную будет записана ссылка на объект (например, соответствующий элементу html, на который назначено событие).
1 метод использует функцию Func, у которой единственным задаваемым аргументом является событие е. Ну, и аргумент this, который передается неявно.
А уже функция Func может принимать некие аргументы. Которые могут быть только глобальными – или в целом по скрипту, или, по крайней мере, для этой функции. Например, если объявить некоторые переменные ДО назначения обработчика, они будут доступны его функции. Или же, как сделано в данном примере, можно объявить аргументы через свойство Param, которое присваивается объекту – элементу html (в данном случае – первой кнопке, имеющей класс input1).
Для примера, показана передача лишь одного параметра, но можно передать и массив. Это позволит передать не один, а несколько параметров (аргументов). Протестируйте обработчик, нажав на кнопку:
2 метод: через анонимную функцию
Как видим, конструкция анонимной функции позволяет передавать параметры прямо в нее, в виде аргументов содержащейся внутри не функции (в данном случае, some_function() ). Для примера, помимо «стандартных» e и this передана также переменная par.
Также обратите внимание, что язык JS допускает именование функций по аналогии с именованием переменных (см. переменную someVar, которая получила имя функции other_function). При этом вызов функции someVar() осуществляется точно так же, как и вызов любой другой функции – путем указания скобок (с, возможно, аргументами) после ее имени.
Т.е. этот способ использует комбинацию средств: переменную this и передачу аргумента внутри функции some_function(), вызванной, в свою очередь, внутри анонимной функции.
Как видим, обработчик, назначенный при помощи анонимной функции, ВПОЛНЕ МОЖЕТ БЫТЬ удален, как только в этом появится необходимость. Это следует взять на заметку.
3 метод: использование bind
Это, на наш взгляд, несколько экзотический метод, с использованием аргументов null. Использование bind также позволяет удалить обработчик, при необходимости. А так, это, по сути, аналог предыдущего метода, только со слегка усложненным синтаксисом.
4 метод: замыкание
Это, можно сказать, красивый и универсальный метод передачи переменных. Правда, для начинающих он может показаться немного сложным… но, как говорится – дорогу осилит идущий. Его просто стоит ПОПРОБОВАТЬ несколько раз в своих проектах, а потом все станет очевидным.
Обратите внимание на характерную конструкцию вида:
(function(…){...})();
Это – «самозамкнутая» функция, в которой все переменные, будучи объявленными, являются локальными для нее.
Внутри этой функции присутствует
return function(e){…}
И вот в эту-то функцию и передается объект события е, в данном случае – событие клика (click).
И, наконец, уже внутри нее вызывается функция func4_enclosure(). А уж в нее могут быть переданы параметры обычными образом, например, объявленные и заданные ранее переменные par4 и par41.
this.removeEventListener(e.type, arguments.callee, false); // Удаляем АНОНИМНЫЙ обработчик!!
Как видим, здесь все параметры этой команды заданы через переменную this (объект элемента, на который назначено событие) и через свойства события type и callee. Это достаточно малоизвестный способ. Но, вполне рабочий. Позволяет удалить АНОНИМНЫЙ обработчик. Который, если следовать классике, удалить, якобы, «невозможно», так как у анонимной функции неизвестно имя (а для удаления обработчика требуется явное указание имени функции). Но, как видим, анонимный обработчик в данном случае прекрасно удаляется.
5 метод: использование callback-функции
Этот метод тоже может показаться сложным для начинающих. Замечателен он тем, что callback-функция, в отличие от обычной функции. позволяет принимать аргументы и при этом. несмотря на наличие круглых скобок после ее имени, НЕ будет выполнена в момент инициализации обработчика. Например, здесь передается переменная param5, а также имя функции func5, которая (на заметку) работает В ДОПОЛНЕНИЕ к основному обработчику из этого метода.
Посмотрим, что тут происходит, ибо синтаксис callback-функции является, и в самом деле, несколько сложным, особенно для первого раза.
var args = Array.prototype.slice.call(arguments, 1);
Здесь происходит привязывание всех аргументов, полученных функцией wrapEventCallback и преобразование их в массив.
На заметку: если вам потребуется сделать преобразование массивоподобного объекта, например, коллекции в массив – лучше использовать именно этот способ. Удобно и буквально в одну строчку. Особенно это ценно, если вы захотите делать сайты не какие попало (потом – вынуждая пользователь обновлять браузеры), а кроссбраузерные. Потому что данный способ хорошо работает и в старых браузерах тоже; а вот более новые функции, предназначенные для такого преобразования, работают не везде.
Итак, массив args представляет собой массив аргументов, переданных в функцию wrapEventCallback. В данном случае это – значение переменной param5.
Далее используем обычное замыкание, как и в примере 4. Имя функции y используется только для того, чтобы потом можно было удалить обработчик через removeEventListener стандартным способом. Однако, вполне можно применить и способ из метода 4, для удаления анонимного обработчика, тогда можно было бы обойтись без использования имени функции y.
Переменная callback представляет собой содержимое (тело) функции-обработчика – в данном случае, тело функции func5. Если написать после ее имени скобки и передать в нее, в скобках, какой-нибудь аргумент (скажем, this.outerHTML - см. пример), то она выполнится с учетом этого аргумента.
Однако, перед этим следует привязать массив с аргументами к объекту нажатой кнопки, т.е. к ее функции-обработчику. Это делается путем
callback.apply(this, args);
Так что такой вот, немножно «навороченный», но зато универсальный синтаксис.
В заключение
На наш взгляд, приведенных пяти методов более, чем достаточно, для того, чтобы передать внешние аргументы в функцию-обработчик события. Наверное, этих методов хватит, чтобы использовать в каких бы то ни было ситуациях, связанных с необходимостью использования внешних (по отношению к функции-обработчику) переменных. Также мы с вами познакомились с малоизвестным способом удаления обработчика события, заданного анонимной функцией. Напишите, что вы думаете по этому поводу.