Character counter with a progress bar for text fields

Bitte gib deinen Namen oder den Namen deines Unternehmens an.
Dieses Feld ist ein Pflichtfeld.
Optional: Bitte gib die URL deiner Webseite ein.
Dieses Feld ist erforderlich.
Teile deine Erfahrungen mit uns…
Einverständniserklärung
Bitte bestätige dein Einverständnis zur Veröffentlichung.
<style>
.ql-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid #d1d5db;
  border-top: none;
  border-radius: 0 0 8px 8px;
  background: #f9fafb;
  padding: 7px 12px;
  margin-top: -1px;
}
.ql-bar-track {
  flex: 1;
  height: 4px;
  background: #e5e7eb;
  border-radius: 99px;
  overflow: hidden;
}
.ql-bar-fill {
  height: 100%;
  width: 0%;
  border-radius: 99px;
  background: #6366f1;
  transition: width 0.15s ease, background 0.2s ease;
}
.ql-char-count {
  font-size: 12px;
  color: #9ca3af;
  white-space: nowrap;
  min-width: 48px;
  text-align: right;
  font-family: inherit;
}
.ql-char-count.warn  { color: #d97706; }
.ql-char-count.limit { color: #dc2626; font-weight: 500; }
</style>

<script>
(function () {
  const MAX = 200;

  function initCounter(editor) {
    if (editor.dataset.counterInit) return;
    editor.dataset.counterInit = '1';

    const footer = document.createElement('div');
    footer.className = 'ql-footer';

    const track = document.createElement('div');
    track.className = 'ql-bar-track';
    const fill = document.createElement('div');
    fill.className = 'ql-bar-fill';
    track.appendChild(fill);

    const count = document.createElement('span');
    count.className = 'ql-char-count';
    count.textContent = '0 / ' + MAX;

    footer.appendChild(track);
    footer.appendChild(count);

    const container = editor.closest('.ql-container') || editor.parentNode;
    container.parentNode.insertBefore(footer, container.nextSibling);

    function update() {
      const len = Math.min(editor.innerText.replace(/\n$/, '').length, MAX);
      const pct = Math.round(len / MAX * 100);

      fill.style.width = pct + '%';
      fill.style.background = len >= MAX ? '#dc2626' : len >= MAX * 0.8 ? '#d97706' : '#6366f1';

      count.textContent = len + ' / ' + MAX;
      count.className = 'ql-char-count' +
        (len >= MAX ? ' limit' : len >= MAX * 0.8 ? ' warn' : '');
    }

    editor.addEventListener('input', update);
    editor.addEventListener('paste', () => setTimeout(update, 0));
    update();
  }

  function scanEditors() {
    document.querySelectorAll('div.ql-editor').forEach(initCounter);
  }

  const observer = new MutationObserver(scanEditors);
  observer.observe(document.body, { childList: true, subtree: true });
  document.addEventListener('DOMContentLoaded', scanEditors);
  scanEditors();
})();
</script>

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert