• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

addeventlistener czy zamiast funkcja inline js

0 głosów
536 wizyt
pytanie zadane 21 maja 2025 w JavaScript przez TOWaD Mądrala (6,770 p.)

Taki offtop, niby funkcja inline jest złą praktyką, ale czy na pewno?

AI

Jeśli dodajesz nasłuchiwacze zdarzeń (addEventListener) do elementów, które później usuwasz poprzez container.innerHTML = "", event listenery NIE są automatycznie czyszczone. Elementy zostają usunięte z DOM-u, ale ich referencje mogą pozostać w pamięci, co może prowadzić do wycieków pamięci.

Garbage Collector nie usunie event listenera, dopóki nie przestanie być używany w kodzie. Jeśli jakiś obiekt nadal ma referencję do usuniętego elementu, będzie on "żył" w pamięci.

komentarz 22 maja 2025 przez overcq Pasjonat (22,630 p.)
Ciekawe spostrzeżenie, ale dotyczy to chyba głównie sposobu działania GC. Tzn. jeśli trzymasz gdzieś referencję do obiektu, to oczywiste, że ‘listener’ będzie przechowywany, ale jeśli nie, to zależy, czy zmienna używana do przechowywania obiektu była w wewnętrznym bloku składniowym, który został już opuszczony. Jeśli kod byłby ‘kompilowany’, to zmienna zostałaby usunięta wcześniej, jeśli byłaby nie używana.

Trzeba się wystrzegać ustawiania ‘listenera’ dla obiektu DOM w pamięci i ponownego używania tego obiektu bez usunięcia z niego ‘listenera’. Podobnie jak ustawienie tekstowego atrybutu znacznika HTML i pozostawienie go w kodzie wstawianym przez “innerHTML”; tylko w drugim przypadku może jest to bardziej widoczne. Więc korzyść z użycia “innerHTML” (by nie zapomnieć o ewentualnym usunięciu ‘listenera’) jest tylko wtedy, gdy generuje się kod za każdym razem od nowa, tak samo jak obiekty tworzy się od nowa, a nie przechowuje stare, oraz nie wykonuje się całego kodu w jednym bloku składniowym.

1 odpowiedź

0 głosów
odpowiedź 22 maja 2025 przez Comandeer Guru (607,980 p.)

Po pierwsze, dodanie listenera przy pomocy atrybutu [on*] może się odwoływać do dowolnej funkcji, która jest dostępna z poziomu globalnego scope. To oznacza, że też można stworzyć sytuację, w której listener będzie siedział w pamięci nawet po usunięciu elementu. Tak samo mogę sobie zapisać element z atrybutem [on*] do zmiennej i spowodować wyciek pamięci po usunięciu takiego elementu z DOM. Sposób przypięcia listenera w żaden sposób na to nie wpływa.

Po drugie, istnieją techniki, w których listener jest bezpośrednio powiązany z samym elementem, co pozwala na dość eleganckie sprzątanie po takim elemencie.

Po trzecie, w większości przypadków GC (ani nawet wydajność jako taka) nie będzie największym problemem przy wyborze sposobu przypinania zdarzeń. Atrybuty nie pozwalają delegować zdarzeń, nie pozwalają przechwytywać zdarzeń, wymagają de facto przypinania do każdego elementu osobno. Dodatkowo działają jak eval() uruchomiony w globalnym scope – i to jeszcze z kilkoma dołożonymi dziwactwami. Nie wspominając już o słabej integracji z CSP. Liczba wad zdecydowanie przewyższa liczbę "zalet".

komentarz 22 maja 2025 przez TOWaD Mądrala (6,770 p.)
edycja 22 maja 2025 przez TOWaD

Czyli rozumiem do profesjonalnych aplikacji się nie nadaje. Ale dla siebie, czy komuś bliskiemu jedno stronicowy html można stworzyć mimo luk bezpieczeństwa. Np można odpalić na androidzie bez hostingu i  bez specjalnej aplikacji tylko jako strona internetowa.

Czyli taki kod to zło wcielone tak:

       function loadNextGroup() {
            const quizBody = document.getElementById("quiz");
            quizBody.innerHTML = ""; // Czyścimy tabelę

            let groupEnd = Math.min(currentIndex + groupSize, shuffledVerbs.length);
            for (let i = currentIndex; i < groupEnd; i++) {
                const verb = shuffledVerbs[i];
                const row = document.createElement("tr");

                row.innerHTML = `
                    <td>${verb.pl}</td>
                    <td><input type="text" id="inf${i}" placeholder="Infinitive"></td>
                    <td><input type="text" id="past${i}" placeholder="Past Simple"></td>
                    <td><input type="text" id="participle${i}" placeholder="Past Participle"></td>
                    <td><button onclick="showAnswers(${i})">Pokaż</button></td>
                    <td><button onclick="fillAnswers(${i})">Wypełnij</button></td>
                `;         
                quizBody.appendChild(row);
            }
            
            // console.log(document.getElementById("nextButton"));
            console.log(document.getElementById(`inf${currentIndex}`));
            if(currentIndex < groupEnd)document.getElementById(`inf${currentIndex}`).focus();
            // console.log(document.querySelector("input"));
        }

Edit:

No niestety muszę to skończy chyba jednak w takim stanie jaki jest, bo muszę to skończyć. No ale czytując Kargula "No, ale chwalić nie ma czim".

1
komentarz 22 maja 2025 przez Comandeer Guru (607,980 p.)
Nie przesadzałbym, że zło wcielone, po prostu nie trzyma się najlepszych praktyk.
komentarz 22 maja 2025 przez TOWaD Mądrala (6,770 p.)
edycja 23 maja 2025 przez TOWaD

Ale nie można było jaśniej, że nie trzeba usuwać addEventListenera jeśli jest do elementu nadrzędnego, 

quizBody.addEventListener("keydown", function (event) {
    let activeElement = document.activeElement;
    let inputs = quizBody.querySelectorAll("input");    

    if (event.key === "jakiś klawisz")

,bo jest elementem nadrzędnym i go się nie usuwa, albo

quizBody.remove() // usuniecie niszczy wszystkie referencje dzieci ?

a co do [on*] to chodzi o nadpisywanie funkcji tak? Czy inne problemy, analizowałem kod z AI i nie widzi problemów i zdarzenia onSomething są usuwane poprawnie nie ma ich w zmiennych globalnych.

Jak coś ton nie rozmawiasz z programistą, tyko z amatorem, więc poprosiłbym językiem mało skomplikowanym.

Ps. Za linki dzięki.

Ps2. problem jednak jest coś się nie usuwa.

komentarz 23 maja 2025 przez Comandeer Guru (607,980 p.)

a co do [on*] to chodzi o nadpisywanie funkcji tak? Czy inne problemy, analizowałem kod z AI i nie widzi problemów i zdarzenia onSomething są usuwane poprawnie nie ma ich w zmiennych globalnych.

Tutaj są co najmniej dwa przypadki:

  1. sami sobie zapisujemy taki element do jakiejś zmiennej,
  2. atrybut odwołuje się do jakiejś funkcji w kodzie.

Pierwszy przypadek to rzeczy typu

code element = document.querySelector( '#element-z-atrybutem' );

Drugi jest mniej oczywisty:

<button onclick="doSomething()">Button</button>

Żeby taki kod zadziałał, funkcja doSomething() musi istnieć w globalnym scope. Więc nawet jeśli się usunie cały element, doSomething() prawdopodobnie przeżyje.

komentarz 24 maja 2025 przez TOWaD Mądrala (6,770 p.)
edycja 24 maja 2025 przez TOWaD

Ale właśnie chciałbym by funkcja doSomething() istniała, ale nie jej kolejna kopia, bo jej/ich ciągle używam. I zauważyłem ze jak wprowadzam dane ręcznie, to profil jak poprzednio. Ale jak biorę Buttom wypełnij. 

function showAnswers(index) {
      ["inf", "past", "participle"].forEach((type, i) => {
            const input = document.getElementById(`${type}${index}`);
            if (input) {
                  input.value = "";
                  input.placeholder = verbs[index].en[i];
                //  input.blur();
            }
      });
}

to profil wygląda tak 

Te jeden [1-4] to też liczy js pliki? A i dziwne że jak nie jest tryb incognito to w ogóle dziwny profil.

Ps. jednak nie dziś robiąc to samo mam nawet 300 listenerów. i zamkniecie funkcji lokalnym zasięgu niewiele daje choć funkcja działa.

{ doSomething() {...}}
komentarz 24 maja 2025 przez Comandeer Guru (607,980 p.)

Te jeden [1-4] to też liczy js pliki?

No to liczy cały uruchamiany kod JS. Ale średnio cokolwiek widać na tym wykresie. 

A i dziwne że jak nie jest tryb incognito to w ogóle dziwny profil.

Hm, możliwe, że jakieś rozszerzenia przeglądarki dodają swoje rzeczy. 

A czy są wgl jakieś problemy z wydajnością? W sensie apka się zacina, coś wolno chodzi, itd? Bo jak nie, to ja bym się raczej nie przejmował na ten moment za bardzo tym, jak to wygląda.

Ale właśnie chciałbym by funkcja doSomething() istniała, ale nie jej kolejna kopia, bo jej/ich ciągle używam

To można osiągnąć też z addEventListener():

button.addEventListener( 'click', doSomething );

function doSomething() {} 
komentarz 24 maja 2025 przez TOWaD Mądrala (6,770 p.)

Dziś tak doskoku tylko.

problemy z wydajnością?

Nie wszystko działa  super i szybko. Tylko chcę by córa na telefonie/laptopie też mogła używać i nie było luki bezpieczeństwa.

I właśnie jak jestem online to listenery i nody rosną, a jak offline to płaski wykres. I to mnie niepokoi tylko.

button.addEventListener( 'click', doSomething );

Jednak wolałbym funkcje jednorazową a nie musieć jechać po pętli. A w tym żółtym zakreśleniu to funkcje raz użyte lub nie mają zniknąć po przycisku następne.

Jak napisze Qta który mi js wrzuca do html i spowrotem, to na codepen to wrzucę kod.

Podobne pytania

+1 głos
1 odpowiedź 293 wizyt
pytanie zadane 20 czerwca 2016 w C i C++ przez C☺ndzi Stary wyjadacz (12,100 p.)
0 głosów
1 odpowiedź 326 wizyt
pytanie zadane 21 czerwca 2019 w JavaScript przez 42savage Bywalec (2,630 p.)
0 głosów
1 odpowiedź 206 wizyt
pytanie zadane 20 czerwca 2017 w JavaScript przez Radekol Bywalec (2,880 p.)

93,691 zapytań

142,610 odpowiedzi

323,216 komentarzy

63,218 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...