funkcja setTimeout() a pętla zdarzeń

0

Mam stronkę, na której jest button. Pod niego podpięta jest funkcja, która wyświetla jakiś content (DIV). Całość działa na zasadzie toogle, czyli show/hide content. Do tego animacja i jest ok.
Chciałem teraz dodać funkcję, która ten content będzie chowała po jakimś czasie. Ktoś może zapomnąć "zwinąć" to, co oglądał. Więc pod ten button podpiąłem kolejną funkcję:

function autoHide(content, delayTime) {
    setTimeout( () => {
        if (content.getAttribute('data-displayMode') === "active") {
            // hide content
        } 
    }, delayTime);
}

Całość wydaje się działać. Ale zauważyłem pewien problem, który - nie wiem czy słusznie - kojarzy mi się z nagraniem, które podrzucił mi @LukeJL w innym temacie:

Chodzi o to, że jak kliknę kilka razy na ten przycisk to tak jakby na "stosie" przeglądarki odkłada się kilka takich funkcji, które potrafią odpalić po zakończeniu odliczania. Czyli szybko kliknę 3 razy i po upłynięciu delayTime 3 razy ta funkcja jest wywoływana. Mam nadzieję, że to jest jasne.

Czy można temu jakoś zapobiec? A może moja implementacja jest to kitu?

1

A gdyby tak wprowadzić flagę, która nie pozwala na odpalenie '//hide content' kolejny raz przed upływem delayTime? Funkcja autoHide nadal by się pewnie wielokrotnie włączała, ale wywalało by ją zanim cokolwiek by zrobiła. Sam jestem ciekawy jakie jest profesjonalne rozwiązanie tego problemu.

let flag = false;

function autoHide(content, delayTime) {

    if (flag) return;

    flag = true;
    
    setTimeout( () => {
        if (content.getAttribute('data-displayMode') === "active") {
            // hide content
        }
        flag = false;
    }, delayTime);
}
0

Chcę uniknąć zmiennej globalnej.

1

w związku z tym, że jest to animacja elementów DOM, to ja zacząłbym od rozwiązań opartych na CSS, gdzie JS by tylko to odpalał.
Np. robisz klasy w CSS z odpowiednimi transition i później robisz np. element.classList.add https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

możesz też łapać event transitionend https://developer.mozilla.org/en-US/docs/Web/API/Element/transitionend_event

0

Czyli coś takiego?

content.addEventListener("transitionend", () => autoHide(content, delayTime));
2

Jeśli chcemy anulować setTimeout to możemy skorzystać z clearTimeout

rafal95p napisał(a):

Chcę uniknąć zmiennej globalnej.

const autoHide = {
  timeoutId: null,
  
  run(content, delayTime) {
    clearTimeout(this.timeoutId);

    this.timeoutId = setTimeout(() => {
      // ...
    }, delayTime);
  }
};

autoHide.run(content, 500);
autoHide.run(content, 500);
autoHide.run(content, 500);

Można ukryć dodatkowe zmienne jako jeden wspólny obiekt.

0

Ogólnie szukam czegoś takiego:

If (content is displayed) for at least 5 min Then {hide content}.

Z drugiej strony dodanie globalnej flagi uprościłoby logikę. Bo byłaby ona wykorzystywana przez 2 funkcje (autoHide() i inna).

A może po prostu zdefiniować sobie obiekt, który będzie czymś w rodzaju "pliku konfiguracyjnego"?

    const dispalyConfiguration = {
        mode : "dispaly",
        timeOutFlag : true

    };

let łatwo nadpisać, co innego const.

0

Chodzi o to, że jak kliknę kilka razy na ten przycisk to tak jakby na "stosie" przeglądarki odkłada się kilka takich funkcji, które potrafią odpalić po zakończeniu odliczania. Czyli szybko kliknę 3 razy i po upłynięciu delayTime 3 razy ta funkcja jest wywoływana. Mam nadzieję, że to jest jasne.

Swoją drogą dlaczego po prostu nie wyłączysz przycisku przez ustawienie atrybutu disabled?

https://jsfiddle.net/2bu390ka/

Może jakbyś więcej napisał o tym, co robisz, to dałoby się zrobić jakieś bardziej oczywiste rozwiązanie.

0

Myślę, że pominąłem ważną rzecz. Na stronie mam kilka buttonów, które aktywują określone tryby (DIVy). Czyli jeżeli ktoś szybko wyłączy jeden, a aktywuje drugi i powtórzy to kilka razy, to na "stosie przeglądarki" odłoży się kilka Tiemoutów od różnych przycisków.
Więc albo faktycznie użyć tej globalnej flagi, albo każdorazowo jakimś forEachem deaktywować pozostałe buttony.

If (content is displayed) for at least 5 min Then {hide content},
but don't show another one until this one is hidden.
0

Poniższe zdaje się działać:

function autoHideWidgetArea(content, autoHideTime) {
  clearTimeout(TimeoutId);
  TimeoutId = setTimeout( () => {
      hideContent(content);
  }, autoHideTime);
}

Zajebiste jest to, że mogę to przypiąć do każdego buttona/widgetu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1