Tinklaraščio įrašas
3/6/2025

„JavaScript“ įvykių klausytojų (event listeners) valdymas su parametrais

Įvykių klausytojai yra būtini interaktyvumui „JavaScript“, tačiau jie gali tyliai sukelti atminties nutekėjimą, jei nėra tinkamai pašalinami. O kas, jei jūsų įvykio klausytojui reikia parametrų? Štai kur viskas pasidaro įdomu. Amejimaobari Ollornwi dalijasi, kurios „JavaScript“ funkcijos leidžia valdyti parametrus su įvykių apdorotojais (event handlers) ir yra gerai palaikomos.

„JavaScript“ įvykių klausytojai yra labai svarbūs, nes jie egzistuoja beveik kiekvienoje interneto programoje, kuriai reikalingas interaktyvumas. Kad ir kokie jie būtų įprasti, taip pat būtina juos tinkamai valdyti. Netinkamai valdomi įvykių klausytojai gali sukelti atminties nutekėjimą ir kartais, kraštutiniais atvejais, sukelti našumo problemų.

Štai tikroji problema: „JavaScript“ įvykių klausytojai dažnai nėra pašalinami po to, kai yra pridedami. Ir kai jie pridedami, dažniausiai jiems nereikia parametrų – išskyrus retus atvejus, dėl kurių juos valdyti tampa šiek tiek sudėtingiau.

Dažnas scenarijus, kai gali prireikti naudoti parametrus su įvykių apdorotojais, yra dinamiškas užduočių sąrašas, kuriame kiekviena užduotis turi mygtuką „Ištrinti“, susietą su įvykio apdorotoju, kuris naudoja užduoties ID kaip parametrą užduočiai pašalinti. Tokioje situacijoje, atlikus užduotį, gera praktika yra pašalinti įvykio klausytoją, kad būtų užtikrinta, jog ištrintas elementas gali būti sėkmingai išvalytas – procesas, žinomas kaip šiukšlių surinkimas (angl. garbage collection).

Dažna klaida pridedant įvykių klausytojus

Labai dažna klaida pridedant parametrus įvykių apdorotojams yra funkcijos iškvietimas su jos parametrais addEventListener() metodo viduje. Štai ką turime omenyje:

button.addEventListener('click', myFunction(param1, param2));

Naršyklė į šią eilutę reaguoja nedelsdama iškviesdama funkciją, nepriklausomai nuo to, ar paspaudimo įvykis įvyko, ar ne. Kitaip tariant, funkcija iškviečiama iš karto, užuot buvusi atidėta, todėl ji niekada nepasileidžia, kai paspaudimo įvykis iš tikrųjų įvyksta.

Kai kuriais atvejais konsolėje taip pat galite gauti šią klaidą:

Uncaught TypeError
„Uncaught TypeError: Failed to execute. addEventListener on EventTarget: parameter is not of type Object.“

Ši klaida yra logiška, nes antrasis addEventListener metodo parametras gali priimti tik „JavaScript“ funkciją, objektą su handleEvent() metodu arba tiesiog null. Greitas ir paprastas būdas išvengti šios klaidos yra pakeisti antrąjį addEventListener metodo parametrą į rodyklinę (angl. arrow) arba anoniminę funkciją.

button.addEventListener('click', (event) => {
  myFunction(event, param1, param2); // Vykdoma paspaudus
});

Vienintelis nesklandumas naudojant rodyklines ir anonimines funkcijas yra tas, kad jų negalima pašalinti tradiciniu removeEventListener() metodu; teks pasinaudoti AbortController, kuris paprastesniais atvejais gali būti perteklinis. AbortController puikiai tinka, kai reikia vienu metu pašalinti kelis įvykių klausytojus.

Paprastesniais atvejais, kai reikia pašalinti tik vieną ar du įvykių klausytojus, removeEventListener() metodas vis dar yra naudingas. Tačiau, norint jį naudoti, reikės išsaugoti savo funkciją kaip nuorodą į klausytoją.

Parametrų naudojimas su įvykių apdorotojais

Yra keletas būdų, kaip įtraukti parametrus į įvykių apdorotojus. Tačiau šios demonstracijos tikslais apsiribosime šiais dviem:

1 parinktis: Rodyklinės ir anoniminės funkcijos

Rodyklinių ir anoniminių funkcijų naudojimas yra greičiausias ir lengviausias būdas atlikti darbą.

Norėdami pridėti įvykio apdorotoją su parametrais naudojant rodyklines ir anonimines funkcijas, pirmiausia turime iškviesti funkciją, kurią ketiname sukurti, rodyklinės funkcijos, prijungtos prie įvykio klausytojo, viduje:

const button = document.querySelector("#myButton");

button.addEventListener("click", (event) => {
  handleClick(event, "hello", "world");
});

Po to galime sukurti funkciją su parametrais:

function handleClick(event, param1, param2) {
  console.log(param1, param2, event.type, event.target);
}

Atkreipkite dėmesį, kad naudojant šį metodą, norint pašalinti įvykio klausytoją, reikia AbortController. Norėdami pašalinti klausytoją, sukuriame naują AbortController objektą ir iš jo gauname AbortSignal objektą:

const controller = new AbortController();
const { signal } = controller;

Tada galime perduoti signalcontroller kaip parinktį addEventListener() metode:

button.addEventListener("click", (event) => {
  handleClick(event, "hello", "world");
}, { signal });

Dabar galime pašalinti įvykio klausytoją iškviesdami AbortController.abort():

controller.abort();

2 parinktis: Uždariniai (Closures)

Uždariniai „JavaScript“ yra dar viena funkcija, kuri gali mums padėti su įvykių apdorotojais. Pamenate klaidą, kuri sukėlė tipo klaidą? Tą klaidą taip pat galima ištaisyti su uždariniais. Tiksliau, su uždariniais funkcija gali pasiekti kintamuosius iš savo išorinės apimties (angl. scope).

Kitaip tariant, galime pasiekti reikiamus parametrus įvykio apdorotojuje iš išorinės funkcijos:

function createHandler(message, number) {
  // Įvykio apdorotojas
  return function (event) {
  console.log(`${message} ${number} - Paspaustas elementas:`, event.target);
    };
  }

  const button = document.querySelector("#myButton");
  button.addEventListener("click", createHandler("Hello, world!", 1));
}

Tai sukuria funkciją, kuri grąžina kitą funkciją. Sukurta funkcija tada iškviečiama kaip antrasis parametras addEventListener() metode, kad vidinė funkcija būtų grąžinta kaip įvykio apdorotojas. Ir pasitelkus uždarinių galią, parametrai iš išorinės funkcijos bus prieinami naudoti vidinėje funkcijoje.

Atkreipkite dėmesį, kaip event objektas tampa prieinamas vidinei funkcijai. Taip yra todėl, kad būtent vidinė funkcija yra prijungiama kaip įvykio apdorotojas. event objektas perduodamas funkcijai automatiškai, nes ji yra įvykio apdorotojas.

Norėdami pašalinti įvykio klausytoją, galime naudoti AbortController, kaip darėme anksčiau. Tačiau šį kartą pažiūrėkime, kaip tai galime padaryti naudodami removeEventListener() metodą.

Kad removeEventListener metodas veiktų, nuoroda į createHandler funkciją turi būti išsaugota ir naudojama addEventListener metode:

function createHandler(message, number) {
  return function (event) {
    console.log(`${message} ${number} - Paspaustas elementas:`, event.target);
  };
}
const handler = createHandler("Hello, world!", 1);
button.addEventListener("click", handler);

Dabar įvykio klausytoją galima pašalinti taip:

button.removeEventListener("click", handler);

Išvada

Gera praktika yra visada pašalinti įvykių klausytojus, kai jie nebėra reikalingi, kad būtų išvengta atminties nutekėjimo. Dažniausiai įvykių apdorotojams nereikia parametrų; tačiau retais atvejais jų prireikia. Naudojant „JavaScript“ funkcijas, tokias kaip uždariniai, AbortController ir removeEventListener, parametrų valdymas su įvykių apdorotojais yra įmanomas ir gerai palaikomas.