Manipulador de eventos Javascript com parâmetros

92

Eu quero fazer um eventHandler que passa o evento e alguns parâmetros. O problema é que a função não obtém o elemento. Aqui está um exemplo:

doClick = function(func){
    var elem = .. // the element where it is all about
    elem.onclick = function(e){
        func(e, elem);
    }
}
doClick(function(e, element){
    // do stuff with element and the event
});

O 'elem' deve ser definido fora da função anônima. Como posso obter o elemento passado para usar na função anônima? Existe uma maneira de fazer isso?

E quanto ao addEventListener? Parece que não consigo passar o evento por um addEventListener, não é?

Atualizar

Parece que resolvi o problema com 'isso'

doClick = function(func){
    var that = this;
    this.element.onclick = function(e){
        func(e, that);
    }
}

Onde ele contém este elemento que posso acessar na função.

O addEventListener

Mas estou me perguntando sobre o addEventListener:

function doClick(elem, func){
    element.addEventListener('click', func(event, elem), false);
}
sebas2day
fonte
2
Você tem elementos, elementos, elementos envolvidos em sua pergunta. Você pode ser menos confuso?
mihai,
Desculpe, estava tentando dar um exemplo da minha situação, mas não deu certo, atualizei o código.
sebas2dia,
Você estaria procurando por e.target/ e.currentTarget?
Rolf
1
como enviar um parâmetro se eu estiver usando: ok.addEventListener ("click", this.changeText.bind (this));
Alberto Acuña

Respostas:

103

Não entendo exatamente o que seu código está tentando fazer, mas você pode disponibilizar variáveis ​​em qualquer manipulador de eventos usando as vantagens de encerramentos de função:

function addClickHandler(elem, arg1, arg2) {
    elem.addEventListener('click', function(e) {
        // in the event handler function here, you can directly refer
        // to arg1 and arg2 from the parent function arguments
    }, false);
}

Dependendo da sua situação de codificação exata, você pode quase sempre fazer algum tipo de fechamento para preservar o acesso às variáveis ​​para você.

A partir de seus comentários, se o que você está tentando realizar é o seguinte:

element.addEventListener('click', func(event, this.elements[i]))

Então, você poderia fazer isso com uma função autoexecutável (IIFE) que captura os argumentos que você deseja em um encerramento à medida que executa e retorna a função real do manipulador de eventos:

element.addEventListener('click', (function(passedInElement) {
    return function(e) {func(e, passedInElement); };
}) (this.elements[i]), false);

Para obter mais informações sobre como funciona um IIFE, consulte estas outras referências:

Código de empacotamento Javascript dentro da função anônima

Expressão de Função Imediatamente Invocada (IIFE) em JavaScript - Passando jQuery

Quais são bons casos de uso para funções anônimas autoexecutáveis ​​de JavaScript?

Esta última versão talvez seja mais fácil de ver o que está fazendo assim:

// return our event handler while capturing an argument in the closure
function handleEvent(passedInElement) {
    return function(e) {
        func(e, passedInElement); 
    };
}

element.addEventListener('click', handleEvent(this.elements[i]));

Também é possível usar .bind()para adicionar argumentos a um retorno de chamada. Quaisquer argumentos que você passar .bind()serão acrescentados aos argumentos que o próprio retorno de chamada terá. Então, você pode fazer isso:

elem.addEventListener('click', function(a1, a2, e) {
    // inside the event handler, you have access to both your arguments
    // and the event object that the event handler passes
}.bind(elem, arg1, arg2));
jfriend00
fonte
sim, isso é quase o que estou tentando fazer, mas e se a função anônima for passada como parâmetro pelo addClickHandler e você quiser dar a esta função alguns parâmetros com o evento como: element.addEventListener ('click', func (event, this.elements [i]));
sebas2day
5
@ sebas2day - Você não pode fazer element.addEventListener('click', func(event, this.elements[i])). Javascript simplesmente não funciona dessa forma. Existem outras maneiras de contornar isso, usando encerramentos, mas você não pode fazer da maneira que escreveu. addEventListener chama seu retorno de chamada com exatamente um argumento (o evento) e você não pode alterar isso de forma alguma.
jfriend00,
Acrescentei outro exemplo de como fazer isso no final da minha resposta.
jfriend00,
@ jfriend00 O que você pode fazer é usar o método bind. function eventFunction (arg, evt) { console.log(arg[0],arg[1],arg[2]) console.log(evt) } var el = document.getElementById('elementID'); el.addEventListener('click', eventFunction.bind(el,[1,2,3])) Observe que o argumento do evento é sempre o último nos argumentos da função e não é declarado explicitamente no método de vinculação.
Cavaleiro Yoshi
2
@ Bosh19 - veja o que adicionei ao final da minha resposta para explicar a construção sobre a qual você perguntou. É uma expressão de função imediatamente chamada (IIFE), que é essencialmente uma função declarada anonimamente que é executada imediatamente. É um atalho para definir uma função e, em seguida, chamá-la - útil quando você quer o corpo embutido e não será chamado em nenhum outro lugar, então não precisa de um nome.
jfriend00
29

É uma pergunta antiga, mas comum. Então, deixe-me adicionar este aqui.

Com a sintaxe da função de seta, você pode obtê-la de maneira mais sucinta, pois é ligada lexicamente e pode ser encadeada.

Uma expressão de função de seta é uma alternativa sintaticamente compacta para uma expressão de função regular, embora sem seus próprios vínculos com as palavras-chave this, arguments, super ou new.target.

const event_handler = (event, arg) => console.log(event, arg);
el.addEventListener('click', (event) => event_handler(event, 'An argument'));

Se você precisar limpar o ouvinte de evento:

// Let's use use good old function sytax
function event_handler(event, arg) {
  console.log(event, arg);
}

// Assign the listener callback to a variable
var doClick = (event) => event_handler(event, 'An argument'); 

el.addEventListener('click', doClick);

// Do some work...

// Then later in the code, clean up
el.removeEventListener('click', doClick);

Aqui está uma frase maluca:

// You can replace console.log with some other callback function
el.addEventListener('click', (event) => ((arg) => console.log(event, arg))('An argument'));

Versão mais dócil : Mais apropriada para qualquer trabalho sensato.

el.addEventListener('click', (event) => ((arg) => {
  console.log(event, arg);
})('An argument'));
snnsnn
fonte
2
Esta parece ser uma solução razoável, mas e quando você tiver que remover o mesmo ouvinte de evento?
Dushyant Sabharwal
@SnnSnn: +1. Usei isso para fornecer uma propriedade de classe ao manipulador de eventos. Acabei de passar thise o manipulador pode acessar o que precisa.
Robotron
1
Se meu entendimento estiver correto, você na verdade estaria passando uma função anônima como seu manipulador de eventos: (e) => {....} e não yourEventHandlerFunction (). Isso causará problemas se você quiser remover o manipulador de eventos mais tarde, já que você passou uma função anônima para começar com ???
PowerAktar
A resposta explica claramente como remover o ouvinte de evento, então não há problema nisso.
snnsnn
9

Algo que você pode tentar é usar o método bind, acho que isso atinge o que você estava pedindo. No mínimo, ainda é muito útil.

function doClick(elem, func) {
  var diffElem = document.getElementById('some_element'); //could be the same or different element than the element in the doClick argument
  diffElem.addEventListener('click', func.bind(diffElem, elem))
}

function clickEvent(elem, evt) {
  console.log(this);
  console.log(elem); 
  // 'this' and elem can be the same thing if the first parameter 
  // of the bind method is the element the event is being attached to from the argument passed to doClick
  console.log(evt);
}

var elem = document.getElementById('elem_to_do_stuff_with');
doClick(elem, clickEvent);
Cavaleiro Yoshi
fonte
2

Dada a atualização da pergunta original, parece que há problemas com o contexto ("this") ao passar os manipuladores de eventos. Os princípios básicos são explicados, por exemplo, aqui http://www.w3schools.com/js/js_function_invocation.asp

Uma versão simples de trabalho do seu exemplo poderia ler

var doClick = function(event, additionalParameter){
    // do stuff with event and this being the triggering event and caller
}

element.addEventListener('click', function(event)
{
  var additionalParameter = ...;
  doClick.call(this, event, additionalParameter );
}, false);

Veja também Javascript call () & apply () vs bind ()?

Echsecutor
fonte
1

Resposta curta:

x.addEventListener("click", function(e){myfunction(e, param1, param2)});

... 

function myfunction(e, param1, param1) {
    ... 
} 
user3093134
fonte
1
Isso não funciona corretamente. Se o valor de seus parâmetros mudar cada vez que o manipulador de eventos for definido, ele produzirá resultados inesperados porque não fecha sobre o valor de param1ou param2. Se esses valores mudarem, eles não serão configurados com o valor que estavam durante o tempo em que o manipulador de eventos foi criado. Eles serão definidos apenas para o valor atual. Além disso, isso não é diferente de definir uma variável fora do escopo da função de ouvinte de evento e fazer referência a essa variável no manipulador de eventos, portanto, não é possível passar parâmetros.
TetraDev
Eu realmente não entendi seu ponto, então minha resposta pode estar errada. De qualquer forma, o manipulador de eventos é definido uma vez (nem sempre, mas geralmente é o que alguém deseja). Em segundo lugar, isso basicamente diz que a função do manipulador é minha função e que ela tem 2 parâmetros mais a variável "evento". Em terceiro lugar, sim, funciona e os resultados são exatamente os esperados.
user3093134
1
Não, não funciona. Eu testei exatamente como você fez e meu argumento continua válido. Se param1for noodlea primeira vez que addEventListener é chamado, e a segunda vez que você o chama, paramé cheesesempre que o ouvinte de evento clickdispara, param1será definido como cheesee não noodle. Portanto, ele não usa um "encerramento" para lembrar o valor que existia no momento da inclusão do ouvinte de evento. Existem cenários em que o ouvinte de evento deve ser adicionado várias vezes que não precisam ser ilustrados para que meu argumento seja válido.
TetraDev
Não sou um especialista e nunca ouvi falar de "encerramento" em programação antes, então não entendi o que você quis dizer com isso.
user3093134
2
Obrigado por conferir, com certeza visitarei os links que você postou também. Além disso, devo dizer que você é muito educado e honesto, senhor, continue assim e tenha um bom dia :)
user3093134
0

thisdentro de doThingsestá o objeto janela. Em vez disso, tente isto:

var doThings = function (element) {
    var eventHandler = function(ev, func){
        if (element[ev] == undefined) {
            return;
        }

        element[ev] = function(e){
            func(e, element);
        }
    };

    return {
        eventHandler: eventHandler
    };
};
Jbabey
fonte
-2
let obj = MyObject();

elem.someEvent( function(){ obj.func(param) } );

//calls the MyObject.func, passing the param.
Δημήτρης Βουζουναράς
fonte
param também pode ser uma função de retorno de chamada (no contexto -say- global) e, portanto, pode ser facilmente chamado de volta, com os parâmetros solicitados.
Δημήτρης Βουζουναράς