Ich programmiere ein dynamisches Formular / Rechnungstool

von | Juni 11, 2025 | Design | 0 Kommentare

Schlagwörter: CSS - CSS3 - fff - HTML - Javascript

Inhaltsverzeichnis

Habt ihr euch schon mal gefragt wie man ein dynamisches Formular macht?

Da gibts Elemente die man mit einem Knopfdruck nachladen kann und die Leute können so das Formular mit neuen Felder erweitern?

Nun in diesem Beitrag zeige ich es euch und zwar programmieren wir ein dynamisches Formular und als Beispiel mach ich ein Rechnungstool.

Video

HTML

<h2>Dynamisches Angebotsformular / Rechnung</h2>

    <form id="dynamic-form">
      <label>
        Datum:
        <input type="date" name="datum" required />
      </label>

      <label>
        Name:
        <input type="text" name="name" required />
      </label>

      <label>
        Adresse:
        <input type="text" name="adresse" required />
      </label>

      <div class="dynamic-area" id="dynamic-area">
        <!-- Dynamische Zeilen -->
      </div>

      <button type="button" class="add-btn" onclick="addRow()">
        + Zeile hinzufügen
      </button>

      <div class="total" id="totals">
        <div>Zwischensumme: <span id="zwischensumme">0,00 €</span></div>
        <div>+ MwSt. (19 %): <span id="mwst">0,00 €</span></div>
        <div>
          <strong>Gesamtbetrag: <span id="total">0,00 €</span></strong>
        </div>
      </div>
      <button type="button" class="add-btn">Rechnung abschicken</button>
    </form>

CSS

:root {
  --seitenhintergrund: #3088fc;
  --formhintergrund: #edf8fd;
  --schattenfarbe: rgba(0, 0, 0, 0.05);
  --button: #007bff;
  --buttonhover: #000;
  --titel: #fff;
  --rand: #535353;
}

body {
  background: var(--seitenhintergrund);
  margin: 2rem;
}

h2 {
  color: var(--titel);
  text-align: center;
}

form {
  background: var(--formhintergrund);
  padding: 2rem;
  border-radius: 10px;
  box-shadow: 0 0 10px var(--schattenfarbe);
  max-width: 800px;
  margin: auto;
}

label {
  display: block;
  margin-bottom: 1rem;
}

input,
select {
  padding: 8px;
  margin-top: 5px;
  width: 100%;
  border: 1px solid var(--rand);
  border-radius: 5px;
  box-sizing: border-box;
}

.row {
  display: grid;
  grid-template-columns: 0.9fr 1.5fr 0.8fr 0.4fr 1fr;
  gap: 10px;
  align-items: center;
  margin-bottom: 10px;
}

.dynamic-area {
  margin-top: 20px;
  border-top: 2px solid var(--rand);
  padding-top: 15px;
}

.add-btn {
  display: inline-block;
  margin-top: 10px;
  background: var(--button);
  color: #fff;
  border: none;
  padding: 8px 16px;
  border-radius: 5px;
  cursor: pointer;
}

.add-btn:hover {
  background: var(--buttonhover);
}

.total {
  margin-top: 30px;
  font-size: 1.1rem;
  line-height: 1.6;
}

.total span {
  float: right;
  font-weight: bold;
}

.total div {
  border-top: 1px solid #eee;
  padding-top: 8px;
  margin-top: 8px;
}

@media (max-width: 700px) {
  .row {
    grid-template-columns: 1fr;
  }
}

JS

const prices = {
        webdesign: 800,
        "social media": 400,
        ecommerce: 8200,
        flyer: 150,
      };

      let rowCount = 0;

      function addRow() {
        rowCount++;
        const area = document.getElementById("dynamic-area");
        const row = document.createElement("div");
        row.className = "row";
        row.dataset.index = rowCount;

        row.innerHTML = `
      <select name="type" onchange="updatePrice(${rowCount})">
        <option value="">-- Wähle --</option>
        <option value="webdesign">Webdesign</option>
        <option value="social media">Social Media Post</option>
        <option value="ecommerce">E-Commerce</option>
        <option value="flyer">Flyer</option>
      </select>

      <input type="text" placeholder="Projektname" name="project"/>

      <input type="text" name="price" id="price-${rowCount}" readonly />

      <input type="number" name="quantity" min="1" value="1" oninput="updateRowTotal(${rowCount})" />

      <input type="text" name="row-total" id="row-total-${rowCount}" readonly value="0,00 €" />
    `;

        area.appendChild(row);
      }

      function updatePrice(index) {
        const row = document.querySelector(`.row[data-index='${index}']`);
        const type = row.querySelector("select").value;
        const priceField = document.getElementById(`price-${index}`);
        const price = prices[type] || 0;
        priceField.value = `${price.toFixed(2)} €`;
        updateRowTotal(index);
      }

      function updateRowTotal(index) {
        const row = document.querySelector(`.row[data-index='${index}']`);
        const type = row.querySelector("select").value;
        const quantity =
          parseInt(row.querySelector("input[name='quantity']").value) || 0;
        const price = prices[type] || 0;
        const total = price * quantity;
        document.getElementById(`row-total-${index}`).value = `${total.toFixed(
          2
        )} €`;
        updateGrandTotal();
      }

      function updateGrandTotal() {
        let netTotal = 0;
        for (let i = 1; i <= rowCount; i++) {
          const rowTotalField = document.getElementById(`row-total-${i}`);
          if (rowTotalField) {
            const value =
              parseFloat(
                rowTotalField.value.replace("€", "").replace(",", ".").trim()
              ) || 0;
            netTotal += value;
          }
        }

        const vatRate = 0.19;
        const vatAmount = netTotal * vatRate;
        const grossTotal = netTotal + vatAmount;

        document.getElementById("zwischensumme").textContent = `${netTotal
          .toFixed(2)
          .replace(".", ",")} €`;
        document.getElementById("mwst").textContent = `${vatAmount
          .toFixed(2)
          .replace(".", ",")} €`;
        document.getElementById("total").textContent = `${grossTotal
          .toFixed(2)
          .replace(".", ",")} €`;
      }

      // Initial eine Zeile anzeigen
      addRow();

0 Kommentare

Einen Kommentar abschicken

Du kannst auf Fediverse-Profile verlinken, indem du fl:@benutzername in deinem Kommentar eingibst.

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