Markup und Struktur von Vorlagen

Vorlagenformatierung und -struktur – So erstellen und passen Sie die HTML-Vorlage für Ihren Warteraum an.

Siehe auch: Styling und CSS-Variablen | Vue-Zustand und -Methoden

Erste Schritte

Öffnen Sie die HTML-Vorlagendatei sowohl in einem Texteditor als auch in einem Browser. Die Vorlage enthält einen <script id="crowdhandler-data" type="application/ld+json"> Tag mit einem JSON-Objekt, das den Warteraum konfiguriert. Um in den Vorschaumodus zu wechseln – in dem Sie jeden Warteschlangenstatus testen können, ohne eine Verbindung zur Live-API herzustellen –, fügen Sie Folgendes hinzu: "dev": true zu diesem Objekt:

<script id="crowdhandler-data" type="application/ld+json">
{
    "dev": true,
    "status": 1,
    "title": "Test Event",
    "position": 150,
    "estimate": 12,
    "simulatePolling": true
}
</script>

Mit "dev": true Wenn diese Option aktiviert ist, verwendet die Vorlage die Werte in diesem JSON-Objekt, anstatt tatsächliche API-Aufrufe durchzuführen. Ändern Sie die Eigenschaften, speichern Sie die Datei und aktualisieren Sie den Browser, um die verschiedenen Zustände anzuzeigen. Entfernen Sie „dev“ (oder stelle es auf false), um wieder in den Produktionsmodus zu wechseln.

Vorschau-Statuswerte

Status Bundesland
1 Aktive Warteschlange
2 Fehler
3 Gesperrt
4 Countdown
5 Raum voll
100 Captcha erforderlich

Beispiele in der Vorschau

// Active queue
{"dev": true, "status": 1, "position": 100, "estimate": 10}

// Countdown
{"dev": true, "status": 4, "onsale": "2026-02-15T14:00:00Z"}

// Blocked
{"dev": true, "status": 3}

Eine vollständige Liste der Vorschau-Eigenschaften finden Sie in der Referenz zum Vorschaumodus.


Übersicht

Die Vorlage nutzt Vue-3-Direktiven, um Abschnitte je nach Status der Warteschlange bedingt darzustellen. Das Hauptstatusobjekt ch enthält alle Warteschlangendaten und wird automatisch über API-Abfragen aktualisiert (standardmäßig alle 60 Sekunden, variable TTL).

Erforderliche Elemente

<!-- Container that shows when loaded -->
<div id="crowdhandler-template" :class="{ 'active': ch.isLoaded }" role="main">
  <!-- Screen reader announcements -->
  <div class="ch-sr-only" aria-live="polite" aria-atomic="true">{{ ch.announcement }}</div>

  <!-- Your template content -->

  <!-- Captcha container (required if captcha is enabled) -->
  <div id="ch-captcha-form"></div>

  <!-- Script inclusion -->
  <script type="module" src="/src/main.js"></script>
</div>

Warteschlangen-Zustände

Die Vorlage verwendet ch.captchaRequired als oberstes Gate und überprüft anschließend andere Zustände innerhalb des Nicht-Captcha-Zweigs mithilfe eines v-if / v-else-if Kette.

1. Captcha erforderlich

ch.captchaRequired

Der Benutzer muss das Captcha ausfüllen, bevor er in die Warteschlange aufgenommen wird. Das Captcha-Widget wird automatisch angezeigt in #ch-captcha-form.

<div v-if="ch.captchaRequired" class="ch-captcha-section">
  <p class="ch-captcha-message">Please verify you're human to continue</p>
  <div id="ch-captcha-form"></div>
</div>
<div v-else>
  <!-- All other states go here -->
</div>

2. Gesperrt

ch.isBlocked

Der Nutzer wurde aufgrund verdächtiger Aktivitäten gesperrt.

<div v-if="ch.isBlocked" class="ch-state-message ch-state-danger" role="alert">
  <h3 class="ch-state-title">Access Denied</h3>
  <p class="ch-state-description">You've been blocked due to suspicious activity.</p>
</div>

3. Raum voll

ch.isRoomFull

Die Warteschlange ist voll. Sie müssen v-else-if nach einer gesperrten Zahlung.

<div v-else-if="ch.isRoomFull" class="ch-state-message ch-state-warning" role="alert">
  <h3 class="ch-state-title">Queue Full</h3>
  <p class="ch-state-description">We're at capacity. You'll be let in when a spot opens up.</p>
</div>

4. Countdown (Vorab-Warteschlange)

ch.isCountdown

Die Warteschlange ist noch nicht geöffnet. Zeige einen Countdown-Timer an.

<div v-if="ch.isCountdown" class="ch-countdown">
  <p class="ch-countdown-label">Queue opens in</p>
  <div class="ch-countdown-display">
    <div class="ch-countdown-unit">
      <div class="ch-countdown-value">{{ ch.countdown.sHours }}</div>
      <div class="ch-countdown-suffix">hrs</div>
    </div>
    <div class="ch-countdown-unit">
      <div class="ch-countdown-value">{{ ch.countdown.sMinutes }}</div>
      <div class="ch-countdown-suffix">min</div>
    </div>
    <div class="ch-countdown-unit">
      <div class="ch-countdown-value">{{ ch.countdown.sSeconds }}</div>
      <div class="ch-countdown-suffix">sec</div>
    </div>
  </div>
</div>

5. Aktive Warteschlange

ch.isActive

Der Nutzer befindet sich mit einer bestimmten Position in der aktiven Warteschlange. Sobald er an der Reihe ist, wird er automatisch weitergeleitet.

<div v-if="ch.isActive">
  <h2 v-if="ch.position">{{ ch.position }}</h2>
  <div v-else>Checking in!</div>
</div>

Vorlagenabschnitte

Kopfzeile (Logo + Titel)

Der Titel erhält je nach Zeichenanzahl eine dynamische Größenklasse.

<header class="ch-header">
  <div class="ch-logo ch-enter ch-stagger-1" v-if="ch.logo">
    <img :src="ch.logo" :alt="ch.title" />
  </div>
  <div class="ch-header-row ch-enter ch-stagger-4">
    <h1 class="ch-title" :class="ch.css.title">{{ ch.title }}</h1>
  </div>
</header>

Nachricht

Wird über der Karte angezeigt. Unterstützt Zeilenumbrüche über Leerzeichen: vor der Zeile. Wird den Bildschirmleseprogrammen mitgeteilt, sobald sich etwas ändert.

<div class="ch-message ch-message-box ch-enter ch-stagger-6" v-if="ch.message">
  {{ ch.message }}
</div>

Countdown + Active Queue Wrapper

Diese Zustände teilen sich einen Wrapper, da die Warteschlange vom Zustand „Countdown“ in den Zustand „Aktiv“ übergeht.

<div v-if="ch.isCountdown || ch.isActive" class="ch-queue-states ch-enter ch-stagger-4">
  <!-- Countdown, position, progress bar, priority code, stock -->
</div>

Positionsanzeige

<div class="ch-position-display" v-if="ch.position && ch.isActive">
  <div class="ch-position-label">Your position</div>
  <h2 class="ch-position-number" :key="ch.position"><strong>{{ ch.position }}</strong></h2>
</div>

Fortschrittsanzeige

Zeigt den Fortschritt der Warteschlange an, wenn eine Position vorhanden ist. Zeigt beim Einchecken einen auf Abfragen basierenden Fortschrittsbalken an (ch.position ist null).

<!-- With position -->
<div v-if="ch.position && ch.isActive" class="ch-progress-container">
  <div class="ch-progress-bar" role="progressbar"
    :aria-valuenow="Math.round(ch.progress || 0)"
    aria-valuemin="0" aria-valuemax="100">
    <div class="ch-progress-fill"
      :style="{ width: (isNaN(ch.progress) ? 10 : ch.progress) + '%' }"></div>
  </div>
  <div class="ch-progress-info">
    <span v-if="ch.estimate && ch.estimate <= 1">Almost there!</span>
    <span v-else-if="ch.estimate && ch.estimate > 60">More than an hour</span>
    <span v-else-if="ch.estimate">Approximately {{ ch.estimate }} minutes</span>
    <span v-if="ch.eta" class="ch-eta">ETA: {{ ch.eta }}</span>
  </div>
</div>

<!-- Checking in (no position yet) -->
<div v-else-if="ch.isActive" class="ch-progress-container">
  <div class="ch-position-label ch-checking-in">Checking in!</div>
  <div class="ch-progress-bar" role="progressbar"
    :aria-valuenow="Math.round(ch.nextPoll || 0)"
    aria-valuemin="0" :aria-valuemax="ch.pollTTL">
    <div class="ch-progress-fill"
      :style="{ width: (isNaN(ch.nextPoll) ? 0 : (ch.nextPoll / ch.pollTTL * 100)) + '%' }">
    </div>
  </div>
</div>

Aktienindikator

<div v-if="ch.stock !== null" class="ch-stock-badge"
  :class="{
    'ch-stock-low': ch.stock > 0 && ch.stock <= 50,
    'ch-stock-zero': ch.stock === 0
  }">
  <span v-if="ch.stock > 0">
    <span class="ch-stock-count">{{ ch.stock }}</span> items remaining
  </span>
  <span v-else>Sold out</span>
</div>

Informationen zur Sitzung

<!-- During countdown -->
<div v-if="ch.isCountdown" class="ch-session-info">
  Stay on this page. You'll get a random position when the queue opens.
</div>

<!-- During active queue -->
<div v-if="ch.isActive">
  <span v-if="ch.sessionsExpire" class="ch-session-info">
    Keep this window open. You'll move forward automatically.
  </span>
  <span v-else-if="ch.sessionsTimeout" class="ch-session-info">
    You'll be redirected when it's your turn.
    You have {{ ch.sessionsTimeout }} minutes to complete your transaction.
  </span>
</div>

Formular für E-Mail-Benachrichtigungen

Drei Zustände: Eingabeformular, Absenden über den Spinner und Bestätigung. Benutzer können ihre E-Mail-Adresse nach dem Absenden ändern. Wird nur während einer aktiven Warteschlange angezeigt.

<div v-if="ch.emailAvailable && ch.isActive" class="ch-form-section ch-email-section">
  <!-- Submitting -->
  <div v-if="ch.email.submitting" class="ch-email-submitting">
    <div class="ch-spinner" role="status" aria-label="Submitting"></div>
  </div>

  <!-- Form -->
  <div v-else-if="!ch.email.submitted || ch.email.changing">
    <div v-if="ch.email.changing" class="ch-form-title">Change notification email</div>
    <div v-else class="ch-form-title">Get notified when it's your turn</div>
    <div class="ch-input-group">
      <input type="email" class="ch-input" placeholder="your@email.com"
        v-model="ch.email.value" @keyup="validateEmail" />
      <button class="ch-btn" @click="submitEmail" :disabled="!ch.email.valid">Notify me</button>
      <button v-if="ch.email.changing" class="ch-btn ch-btn-secondary"
        @click="cancelChangeEmail">Cancel</button>
    </div>
  </div>

  <!-- Confirmation -->
  <div v-else role="status">
    <div class="ch-form-feedback ch-success">We'll email {{ ch.email.value }}</div>
    <button @click="changeEmail" class="ch-btn ch-btn-sm ch-btn-change">Change</button>
  </div>
</div>

Formular für den Prioritätscode

Verfügbar sowohl während des Countdowns als auch im aktiven Warteschlangenstatus.

<div v-if="ch.priority && ch.priority.available" class="ch-form-section ch-priority-section">
  <div class="ch-form-title">Got a priority code?</div>
  <div class="ch-input-group">
    <input type="text" class="ch-input" id="ch-priority-input"
      placeholder="Enter code" v-model="ch.priority.code"
      @keyup="checkValidPriorityCode" />
    <button class="ch-btn" @click="prioritySubmit"
      :disabled="ch.priority.submitting || !ch.priority.code">Apply</button>
  </div>
  <div v-if="ch.priority.submitted && ch.priority.success"
    class="ch-form-feedback ch-success" role="status">Code applied!</div>
  <div v-if="ch.priority.submitted && ch.priority.error"
    class="ch-form-feedback ch-error" role="status">Invalid code</div>
</div>

Fußzeile

Dreispaltiges Layout: Zeit des letzten Anmeldungsversuchs (links), Sitzungstoken (Mitte), „Powered by“ (rechts). Wird während des CAPTCHA-Verfahrens ausgeblendet.

<div class="ch-footer ch-enter ch-stagger-5" v-if="!ch.captchaRequired">
  <div class="ch-footer-left">
    <div class="ch-footer-meta" v-if="ch.requested">
      <span>Last check in:</span>
      <code class="ch-footer-meta-value">
        <span :class="{ 'ch-text-shimmer': ch.nextPoll < 3 }">
          {{ formatDate(ch.requested, 'toLocaleTimeString') }}
        </span>
      </code>
    </div>
  </div>
  <div class="ch-footer-center">
    <div class="ch-token-display" v-if="ch.token.obfuscated || ch.token.value">
      <span>ID:</span>
      <code class="ch-token-value" tabindex="0" role="button"
        @click="(e) => ch.token.copy(e, ch)">
        {{ ch.token.copied ? 'Copied!' : (ch.token.obfuscated || ch.token.value) }}
      </code>
    </div>
  </div>
  <div class="ch-powered-by">
    Powered by <a href="https://www.crowdhandler.com/..." target="_blank">CrowdHandler</a>
  </div>
</div>

Philosophie der individuellen Anpassung

Die index.html Die Vorlage ist eine Referenzimplementierung – sie umfasst alle Funktionen, die das System unterstützt. Ihre Vorlage muss diese nicht eins zu eins nachbilden.

Die Funktionen beibehalten, das Design ändern

Jeder Abschnitt ist optional, wir empfehlen jedoch, alle Funktionen in Ihrer Vorlage beizubehalten. Die Anwendung funktioniert weiterhin einwandfrei, wenn Sie das Markup einer Funktion entfernen – die Benutzeroberfläche dafür wird lediglich nicht angezeigt. Es ist jedoch wesentlich einfacher, wenn das Markup bereits vorhanden ist, wenn Sie eine Funktion während eines laufenden Verkaufs aktivieren müssen. Wenn ein Kunde beispielsweise beschließt, Captcha oder die Bestandsanzeige mitten in der Veranstaltung zu aktivieren, rendert die Vorlage dies sofort ohne weitere Änderungen. Ohne den Markup-Code müssten Sie die Vorlage aktualisieren und neu bereitstellen – und aufgrund des Vorlagen-Cachings würde diese Aktualisierung nicht sofort wirksam werden.

Die einzigen zwingenden Voraussetzungen sind die #crowdhandler-Vorlage Container und das Skript enthalten.

Die Zustandslogik neu gestalten

Die Vue-Direktiven können Sie nach Belieben neu anordnen. Sie könnten nachsehen, ch.stock === 0 Zunächst können Sie ein Vollbild-Fenster mit der Meldung „Ausverkauft“ anzeigen, den Countdown mit einer Teaser-Meldung kombinieren oder das Formular für den Prioritätscode in den Countdown einbetten. Die Referenzvorlage gruppiert die Zustände in einer bestimmten Hierarchie, Sie können diese jedoch nach Belieben umstrukturieren.

Die Entwicklung sollte sich an den Daten orientieren, nicht am Markup

Denken Sie an ch als Daten-API und nicht als vorgegebenes Layout. Die Status-Eigenschaften sind reaktive Werte, die Sie überall, in beliebiger Kombination und mit jeder für Ihr Design sinnvollen bedingten Logik verwenden können. Die Referenzvorlage ist ein Ausgangspunkt, keine Einschränkung.