# Javascript

Mit **Javascript** kannst du Szenen mit eigener Logik erweitern – z. B. UI anpassen, kleine Effekte einbauen, Zeiten messen oder externe Schnittstellen triggern.

> **Wichtig:** JS ist **inkonstant** – beim Betreten **jeder** Szene wird es **neu** ausgeführt. Teile, die überall gelten sollen, gehören in die **Masterszene** `[[ ]]`.  
> Starte dein Script immer **leicht verzögert**, damit die Szene fertig gerendert ist:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">  
</div></div><div class="overflow-y-auto p-4" dir="ltr">  
</div></div>```
setTimeout(function () { // dein Code hier }, 300); 
```

---

## Gute Praxis (sehr wichtig)

- **Idempotent denken:** Code darf mehrfach laufen, ohne doppelten Effekt zu erzeugen.  
    Nutze einen „Once“-Guard:
    
    <div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
    </div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> () {  <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">window</span>.<span class="hljs-property">__sceneOnce</span>) <span class="hljs-keyword">return</span>;  <span class="hljs-variable language_">window</span>.<span class="hljs-property">__sceneOnce</span> = <span class="hljs-literal">true</span>;  <span class="hljs-comment">// init …</span>}, <span class="hljs-number">200</span>);`</div></div>
- **Aufräumen:** Wenn du `setInterval`/Event-Listener setzt, räume sie auf (oder nutze `{ once:true }` bei `addEventListener`).
- **Robust warten:** Wenn du ein Element brauchst, das evtl. später kommt, warte kurz darauf:
    
    <div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
    </div></div></div><div class="overflow-y-auto p-4" dir="ltr">  
    </div></div>```
    function waitSel(sel, cb, tries=20){ const el = document.querySelector(sel); if (el) return cb(el); if (tries--) return setTimeout(()=>waitSel(sel, cb, tries), 100); } setTimeout(()=>waitSel('.btn-primary', btn=>{ btn.classList.add('pulse'); }), 200); 
    ```
- **Keine Blocker:** Lange Schleifen vermeiden; lieber Timings/Promises verwenden.
- **Fallbacks:** Netzaufrufe können an CORS/Offline scheitern → Fehler behandeln.

---

## Häufige Mini-Rezepte

### 1) Button-Label/Style anpassen

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-settimeout%28function--1"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> () {  <span class="hljs-keyword">const</span> btn = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'button'</span>);  <span class="hljs-keyword">if</span> (!btn) <span class="hljs-keyword">return</span>;  btn.<span class="hljs-property">textContent</span> = <span class="hljs-string">'Los geht’s'</span>;  btn.<span class="hljs-property">style</span>.<span class="hljs-property">filter</span> = <span class="hljs-string">'drop-shadow(0 6px 12px rgba(0,0,0,.25))'</span>;}, <span class="hljs-number">200</span>);`</div></div>### 2) Szene nach X Sekunden automatisch fortsetzen

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-settimeout%28function--2"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> () {  <span class="hljs-comment">// z.B. per Klick auf einen Weiter-Button simulieren</span>  <span class="hljs-keyword">const</span> next = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'[data-action="next"], .btn-continue'</span>);  <span class="hljs-keyword">if</span> (next) next.<span class="hljs-title function_">click</span>();}, <span class="hljs-number">10000</span>); <span class="hljs-comment">// 10 s</span>`</div></div>### 3) Einfache „Einmal“-Animation

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-settimeout%28function--3"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> () {  <span class="hljs-keyword">const</span> el = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.dialog, .content'</span>);  <span class="hljs-keyword">if</span> (!el) <span class="hljs-keyword">return</span>;  el.<span class="hljs-title function_">animate</span>([{<span class="hljs-attr">opacity</span>:<span class="hljs-number">0</span>, <span class="hljs-attr">transform</span>:<span class="hljs-string">'translateY(8px)'</span>},{<span class="hljs-attr">opacity</span>:<span class="hljs-number">1</span>, <span class="hljs-attr">transform</span>:<span class="hljs-string">'none'</span>}],             {<span class="hljs-attr">duration</span>:<span class="hljs-number">400</span>, <span class="hljs-attr">easing</span>:<span class="hljs-string">'ease-out'</span>});}, <span class="hljs-number">150</span>);`</div></div>### 4) Soft-Gate per Tageszeit (Beispiel)

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-settimeout%28function--4"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> () {  <span class="hljs-keyword">const</span> h = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">getHours</span>();  <span class="hljs-keyword">if</span> (h < <span class="hljs-number">8</span> || h > <span class="hljs-number">20</span>) {    <span class="hljs-title function_">alert</span>(<span class="hljs-string">'Tipp: Diese Aufgabe klappt am besten bei Tageslicht.'</span>);  }}, <span class="hljs-number">250</span>);`</div></div>### 5) Ereignis mitzählen (lokal)

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-settimeout%28function--5"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> () {  <span class="hljs-keyword">const</span> k = <span class="hljs-string">'tries_safe_open'</span>;  <span class="hljs-keyword">const</span> n = <span class="hljs-number">1</span> + +(<span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">getItem</span>(k) || <span class="hljs-number">0</span>);  <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(k, n);  <span class="hljs-keyword">if</span> (n >= <span class="hljs-number">3</span>) {    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'3 Fehlversuche – zeige Hilfe.'</span>);    <span class="hljs-comment">// hier könntest du z.B. einen Hilfe-Button einblenden</span>  }}, <span class="hljs-number">200</span>);`</div></div><div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk--3"><div class="overflow-y-auto p-4" dir="ltr">  
</div></div>---

## Typische Stolperfallen

- **Doppelte Listener**: Bei jeder Rückkehr in die Szene wird JS neu ausgeführt → `once`-Option nutzen:
    
    <div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
    </div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-variable language_">window</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, handler, { <span class="hljs-attr">once</span>:<span class="hljs-literal">true</span> });`</div></div>
- **Zu früher Zugriff**: Ohne Delay (`setTimeout`) existieren DOM-Elemente evtl. noch nicht.
- **Starke Manipulation am Layout**: Bedenke, dass die App auf vielen Displaygrößen läuft – nur sanft stylen.
- **Blockierende Prompts/Alerts**: Sparsam einsetzen; sie unterbrechen den Flow.

---

## Checkliste

1. **Delay** einbauen (`setTimeout` 150–300 ms).
2. **Idempotent** &amp; ggf. **Once-Guard** verwenden.
3. Wo nötig: **waitSel**/Observer nutzen.
4. Netz-/Fehlerfälle abfangen.
5. Auf mehreren Geräten testen.

Damit bekommst du stabile, kleine Scripts, die zuverlässig mit der Szenenlogik zusammenspielen.

Gute Beispiele "ready to use" findest du auch unter [Hacks](https://docs.scenario.app/books/storyboard/chapter/hacks-tipps "05 Hacks & Tipps").