Moduł obsługi zdarzeń JavaScript z parametrami

89

Chcę utworzyć eventHandler, który przekazuje zdarzenie i niektóre parametry. Problem w tym, że funkcja nie pobiera elementu. Oto przykład:

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

Element „elem” musi być zdefiniowany poza funkcją anonimową. Jak mogę uzyskać przekazany element do użycia w funkcji anonimowej? Czy jest na to sposób?

A co z addEventListener? Wydaje się, że w ogóle nie mogę przekazać zdarzenia przez addEventListener, prawda?

Aktualizacja

Wydawało się, że rozwiązałem problem z „tym”

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

Gdzie ten element zawiera this.element, do którego mam dostęp w funkcji.

AddEventListener

Ale zastanawiam się nad addEventListener:

function doClick(elem, func){
    element.addEventListener('click', func(event, elem), false);
}
sebas2day
źródło
2
Masz element, elementy, element, którego dotyczy twoje pytanie. Czy możesz być mniej zagmatwany?
mihai
Przepraszam, próbowałem zrobić przykład z mojej sytuacji, ale nie poszło dobrze, zaktualizowałem kod.
sebas2day
Czy szukałbyś e.target/ e.currentTarget?
Rolf
1
jak wysłać parametr, jeśli używam tego: ok.addEventListener ("click", this.changeText.bind (this));
Alberto Acuña

Odpowiedzi:

103

Nie rozumiem dokładnie, co próbuje zrobić Twój kod, ale możesz udostępnić zmienne w dowolnym programie obsługi zdarzeń, korzystając z zalet zamknięć funkcji:

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

W zależności od dokładnej sytuacji kodowania, prawie zawsze możesz dokonać pewnego rodzaju zamknięcia, aby zachować dostęp do zmiennych.

Z twoich komentarzy, jeśli próbujesz osiągnąć to:

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

Następnie możesz to zrobić za pomocą samo wykonującej się funkcji (IIFE), która przechwytuje argumenty, które chcesz w zamknięciu, podczas wykonywania i zwraca rzeczywistą funkcję obsługi zdarzenia:

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

Aby uzyskać więcej informacji na temat działania IIFE, zobacz te inne źródła:

JavaScript opakowujący kod wewnątrz funkcji anonimowej

Wyrażenie funkcji wywoływane natychmiastowo (IIFE) w JavaScript - przekazywanie jQuery

Jakie są dobre przypadki użycia dla samodzielnie wykonujących się anonimowych funkcji JavaScript?

Ta ostatnia wersja może być łatwiejsza do zobaczenia, co robi w ten sposób:

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

Możliwe jest również użycie .bind()do dodania argumentów do wywołania zwrotnego. Wszelkie argumenty, do których .bind()przekażesz, zostaną dołączone do argumentów, które będzie miało samo wywołanie zwrotne. Możesz więc zrobić to:

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
źródło
tak, to prawie to, co próbuję zrobić, ale co, jeśli funkcja anonimowa zostanie przekazana jako parametr przez addClickHandler i chcesz nadać tej funkcji kilka parametrów ze zdarzeniem takim jak: element.addEventListener ('click', func (event, this.elements [i]));
sebas2day
5
@ sebas2day - Nie możesz tego zrobić element.addEventListener('click', func(event, this.elements[i])). JavaScript po prostu nie działa w ten sposób. Są inne sposoby obejścia tego, używając domknięć, ale nie możesz tego zrobić tak, jak napisałeś. addEventListener wywołuje wywołanie zwrotne z dokładnie jednym argumentem (zdarzeniem) i nie możesz tego w żaden sposób zmienić.
jfriend00
Na końcu mojej odpowiedzi dodałem kolejny przykład.
jfriend00
@ jfriend00 To, co możesz zrobić, to użyć metody 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])) Należy zauważyć, że argument zdarzenia jest zawsze ostatni w argumentach funkcji i nie jest jawnie deklarowany w metodzie bind.
Knight Yoshi
2
@ Bosh19 - zobacz, co dodałem na końcu mojej odpowiedzi, aby wyjaśnić konstrukcję, o którą pytałeś. Jest to natychmiast wywołane wyrażenie funkcyjne (IIFE), które jest zasadniczo anonimowo zadeklarowaną funkcją, która jest natychmiast wykonywana. Jest to skrót do definiowania funkcji, a następnie wywoływania jej - przydatne, gdy chcesz, aby treść była wbudowana i nie będzie wywoływana nigdzie indziej, więc nie potrzebuje nazwy.
jfriend00
28

To stare pytanie, ale powszechne. Więc pozwól mi dodać ten tutaj.

Dzięki składni funkcji strzałek można uzyskać bardziej zwięzły sposób, ponieważ jest ona powiązana leksykalnie i można ją połączyć w łańcuch.

Wyrażenie funkcji strzałkowej jest kompaktową pod względem składniowym alternatywą dla wyrażenia funkcji regularnej, chociaż nie ma własnych powiązań ze słowami kluczowymi this, arguments, super lub new.target.

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

Jeśli chcesz wyczyścić detektor zdarzeń:

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

Oto szalona jedna linijka:

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

Bardziej potulna wersja : bardziej odpowiednia do każdej rozsądnej pracy.

el.addEventListener('click', (event) => ((arg) => {
  console.log(event, arg);
})('An argument'));
snnsnn
źródło
2
Wydaje się, że jest to rozsądne rozwiązanie, ale co w przypadku, gdy trzeba usunąć ten sam detektor zdarzeń?
Dushyant Sabharwal
@SnnSnn: +1. Użyłem tego, aby zapewnić właściwość klasy do programu obsługi zdarzeń. Właśnie przeszedłem thisi opiekun mógł uzyskać dostęp do tego, czego potrzebuje.
Robotron
Jeśli moje rozumienie jest poprawne, w rzeczywistości przekazywałbyś anonimową funkcję jako moduł obsługi zdarzeń: (e) => {....}, a nie yourEventHandlerFunction (). Spowoduje to problemy, jeśli zechcesz później usunąć program obsługi zdarzeń, ponieważ przekazałeś anonimową funkcję na początku ???
PowerAktar
Odpowiedź jasno wyjaśnia, jak usunąć nasłuchiwanie zdarzeń, więc nie ma problemu.
snnsnn
9

Coś, co możesz spróbować, to użycie metody wiązania, myślę, że to osiąga to, o co prosiłeś. Jeśli nic więcej, nadal jest bardzo przydatne.

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);
Rycerz Yoshi
źródło
2

Biorąc pod uwagę aktualizację pierwotnego pytania, wydaje się, że wystąpił problem z kontekstem („to”) podczas przekazywania programów obsługi zdarzeń. Podstawy są wyjaśnione np. Tutaj http://www.w3schools.com/js/js_function_invocation.asp

Prosta działająca wersja twojego przykładu mogłaby czytać

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

Zobacz także wywołanie () i zastosowanie () w JavaScript vs bind ()?

Echsecutor
źródło
2

Krótka odpowiedź:

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

... 

function myfunction(e, param1, param1) {
    ... 
} 
user3093134
źródło
To nie działa poprawnie. Jeśli wartość parametrów zmieni się za każdym razem, gdy program obsługi zdarzeń zostanie ustawiony, spowoduje to nieoczekiwane wyniki, ponieważ nie zamknie się nad wartością param1lub param2. Jeśli te wartości ulegną zmianie, nie zostaną ustawione na wartość, która była w czasie tworzenia procedury obsługi zdarzeń. Zostaną ustawione tylko na aktualną wartość. Ponadto nie różni się to od ustawiania zmiennej poza zakresem funkcji detektora zdarzeń i odwoływania się do tej zmiennej w module obsługi zdarzeń, więc w ogóle nie dokonuje przekazywania parametrów.
TetraDev
Nie rozumiem, o co ci chodzi, więc moja odpowiedź może być nieaktualna. W każdym razie, obsługa zdarzeń jest ustawiana raz (chyba nie zawsze, ale zazwyczaj ktoś tego chce). Po drugie, to prawie mówi, że funkcja obsługi to moja funkcja i że ma 2 parametry oraz zmienną „event”. Po trzecie, tak, to działa, a wyniki są dokładnie takie, jak oczekiwano.
user3093134
Nie, to nie działa. Przetestowałem to dokładnie tak, jak ty i mój punkt widzenia pozostaje aktualny. Jeśli param1jest noodleto pierwsze wywołanie addEventListener, a drugie wywołanie go, paramto cheesewtedy, gdy wywoła się detektor zdarzenia click, param1zostanie ustawione na, cheesea nie noodle. Dlatego nie używa „zamknięcia” do zapamiętania wartości, która istniała w momencie dodawania detektora zdarzeń. Istnieją scenariusze, w których odbiornik zdarzeń musi być dodawany wiele razy, których nie trzeba ilustrować, aby mój punkt widzenia był ważny.
TetraDev
Nie jestem ekspertem i nie słyszałem wcześniej o „zamknięciu” w programowaniu, więc nie rozumiem, co przez to rozumiesz.
user3093134
1
Dziękuję za sprawdzenie, na pewno odwiedzę również linki, które zamieściłeś. Muszę też powiedzieć, że jest pan bardzo uprzejmy i uczciwy, proszę tak trzymać i życzę
miłego
0

thiswewnątrz doThingsznajduje się obiekt okna. Spróbuj tego zamiast tego:

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
źródło
-2
let obj = MyObject();

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

//calls the MyObject.func, passing the param.
Δημήτρης Βουζουναράς
źródło
param może być również funkcją zwrotną (w kontekście globalnym -say-), a więc można ją łatwo wywołać z żądanymi parametrami.
Δημήτρης Βουζουναράς