Notenrechner überarbeitet: DOM-Version mit moderner Event-Behandlung und Fehlervalidierung
This commit is contained in:
parent
f917afd471
commit
8eec0e06d9
3 changed files with 1015 additions and 26 deletions
484
css/style.css
484
css/style.css
|
|
@ -754,6 +754,473 @@ textarea {
|
|||
color: #ff1493;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Notenrechner DOM Version Styles
|
||||
=========================== */
|
||||
|
||||
.calculator-tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin: 1.5rem 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.calculator-tabs .tab-btn {
|
||||
padding: 0.8rem 1.5rem;
|
||||
background: rgba(255, 105, 180, 0.1);
|
||||
border: 2px solid rgba(255, 105, 180, 0.2);
|
||||
border-radius: 12px;
|
||||
color: #ff1493;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.calculator-tabs .tab-btn:hover {
|
||||
background: rgba(255, 105, 180, 0.2);
|
||||
border-color: #ff1493;
|
||||
}
|
||||
|
||||
.calculator-tabs .tab-btn.active {
|
||||
background: linear-gradient(135deg, #ff1493 0%, #ff69b4 100%);
|
||||
color: white;
|
||||
border-color: #ff1493;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
animation: slideUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.calculator-box {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 2px solid rgba(255, 105, 180, 0.2);
|
||||
border-radius: 20px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(255, 20, 147, 0.15);
|
||||
}
|
||||
|
||||
.calculator-box h3 {
|
||||
color: #ff1493;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.calculator-box p {
|
||||
color: #b8659c;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Simple Input Group */
|
||||
.simple-input-group {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr auto;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.input-field label {
|
||||
font-weight: 600;
|
||||
color: #ff1493;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
padding: 0.8rem;
|
||||
border: 2px solid rgba(255, 105, 180, 0.2);
|
||||
border-radius: 8px;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
color: #6b3a5f;
|
||||
background: white;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: #ff1493;
|
||||
box-shadow: 0 0 12px rgba(255, 20, 147, 0.2);
|
||||
}
|
||||
|
||||
.form-input::placeholder {
|
||||
color: #b8659c;
|
||||
}
|
||||
|
||||
/* Result Box */
|
||||
.result-box {
|
||||
background: rgba(255, 192, 203, 0.1);
|
||||
border: 2px solid rgba(255, 105, 180, 0.2);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: #dc143c;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.grade-result-inline {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.grade-note,
|
||||
.grade-note-large {
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.grade-percent {
|
||||
font-weight: 600;
|
||||
font-size: 1.2rem;
|
||||
color: #b8659c;
|
||||
background: rgba(255, 192, 203, 0.3);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.grade-result-detailed {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid rgba(255, 105, 180, 0.2);
|
||||
}
|
||||
|
||||
.result-header h4 {
|
||||
margin: 0;
|
||||
color: #ff1493;
|
||||
}
|
||||
|
||||
.grade-note-large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
color: #6b3a5f;
|
||||
}
|
||||
|
||||
.result-details p {
|
||||
margin: 0.3rem 0;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.progress-bar-container {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.progress-bar-background {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: rgba(255, 192, 203, 0.2);
|
||||
border: 2px solid rgba(255, 105, 180, 0.2);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.progress-bar-fill {
|
||||
height: 100%;
|
||||
transition: width 0.5s ease, background-color 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
color: #6b3a5f;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Grades Container DOM */
|
||||
.grades-container-dom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.grade-input-group-dom {
|
||||
animation: slideUp 0.4s ease-out;
|
||||
}
|
||||
|
||||
.grade-input-wrapper-dom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
background: rgba(255, 192, 203, 0.1);
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border: 2px solid rgba(255, 105, 180, 0.15);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.grade-input-wrapper-dom:focus-within {
|
||||
border-color: rgba(255, 105, 180, 0.4);
|
||||
background: rgba(255, 192, 203, 0.2);
|
||||
}
|
||||
|
||||
.grade-points-dom,
|
||||
.grade-max-dom {
|
||||
flex: 1;
|
||||
padding: 0.8rem;
|
||||
border: 1px solid rgba(255, 105, 180, 0.2);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
color: #6b3a5f;
|
||||
background: white;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.grade-points-dom:focus,
|
||||
.grade-max-dom:focus {
|
||||
outline: none;
|
||||
border-color: #ff1493;
|
||||
box-shadow: 0 0 8px rgba(255, 20, 147, 0.2);
|
||||
}
|
||||
|
||||
.grade-points-dom::placeholder,
|
||||
.grade-max-dom::placeholder {
|
||||
color: #b8659c;
|
||||
}
|
||||
|
||||
.grade-separator {
|
||||
font-weight: 600;
|
||||
color: #ff69b4;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.grade-result-dom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.grade-note-dom {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
color: #6b3a5f;
|
||||
}
|
||||
|
||||
.grade-percent-dom {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
color: #b8659c;
|
||||
background: rgba(255, 192, 203, 0.3);
|
||||
padding: 0.3rem 0.6rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.grade-remove-btn-dom {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.3rem;
|
||||
cursor: pointer;
|
||||
color: #ff69b4;
|
||||
padding: 0.5rem;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.grade-remove-btn-dom:hover {
|
||||
background: rgba(255, 20, 147, 0.1);
|
||||
color: #ff1493;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
/* Calculator Buttons */
|
||||
.calculator-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.btn-add,
|
||||
.btn-calculate,
|
||||
.btn-reset {
|
||||
padding: 1rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
background: rgba(255, 105, 180, 0.2);
|
||||
color: #ff1493;
|
||||
border: 2px solid rgba(255, 105, 180, 0.3);
|
||||
}
|
||||
|
||||
.btn-add:hover {
|
||||
background: rgba(255, 105, 180, 0.3);
|
||||
border-color: #ff1493;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-calculate {
|
||||
background: linear-gradient(135deg, #ff1493 0%, #ff69b4 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(255, 20, 147, 0.3);
|
||||
}
|
||||
|
||||
.btn-calculate:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(255, 20, 147, 0.4);
|
||||
}
|
||||
|
||||
.btn-reset {
|
||||
background: rgba(200, 100, 150, 0.15);
|
||||
color: #b8659c;
|
||||
border: 2px solid rgba(200, 100, 150, 0.3);
|
||||
}
|
||||
|
||||
.btn-reset:hover {
|
||||
background: rgba(200, 100, 150, 0.25);
|
||||
border-color: #b8659c;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Results DOM */
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.results-list h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
color: #ff1493;
|
||||
}
|
||||
|
||||
.result-item-dom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: rgba(255, 192, 203, 0.15);
|
||||
border-radius: 10px;
|
||||
border-left: 4px solid rgba(255, 105, 180, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
animation: slideUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.result-item-dom:hover {
|
||||
background: rgba(255, 192, 203, 0.25);
|
||||
border-left-color: #ff1493;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.result-number {
|
||||
font-weight: 700;
|
||||
color: #ff1493;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
flex: 1;
|
||||
color: #6b3a5f;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.result-grade-dom {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
min-width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Average Box DOM */
|
||||
.average-box-dom {
|
||||
background: linear-gradient(135deg, rgba(255, 192, 203, 0.2) 0%, rgba(255, 160, 180, 0.1) 100%);
|
||||
border: 2px solid rgba(255, 105, 180, 0.3);
|
||||
border-radius: 15px;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
animation: slideUp 0.6s ease-out;
|
||||
box-shadow: 0 4px 15px rgba(255, 20, 147, 0.1);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.average-box-dom h4 {
|
||||
margin-bottom: 1rem;
|
||||
color: #ff1493;
|
||||
}
|
||||
|
||||
.average-display-dom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.average-percent-dom {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
color: #ff1493;
|
||||
background: rgba(255, 192, 203, 0.2);
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.average-note-dom {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.average-details-dom {
|
||||
margin-top: 1rem;
|
||||
color: #b8659c;
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Grade Scale Table */
|
||||
.grade-scale-table {
|
||||
width: 100%;
|
||||
|
|
@ -1578,6 +2045,23 @@ textarea {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.simple-input-group {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.simple-input-group .btn-calculate {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.calculator-tabs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.calculator-tabs .tab-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 0.8rem;
|
||||
|
|
|
|||
436
js/notenrechner-dom.js
Normal file
436
js/notenrechner-dom.js
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
/**
|
||||
* Notenrechner DOM-Version
|
||||
* Moderne JavaScript DOM-Manipulation mit addEventListener
|
||||
* David & Karo - EIS Projekt
|
||||
*/
|
||||
|
||||
// Notenmaßstab definieren
|
||||
const NOTENMASSTAB = {
|
||||
'sehr gut': { min: 90, max: 100, color: '#ff1493' },
|
||||
'gut': { min: 75, max: 89, color: '#ff69b4' },
|
||||
'befriedigend': { min: 60, max: 74, color: '#ffa500' },
|
||||
'ausreichend': { min: 50, max: 59, color: '#ff8c00' },
|
||||
'nicht bestanden': { min: 0, max: 49, color: '#dc143c' }
|
||||
};
|
||||
|
||||
/**
|
||||
* Berechnet die Note basierend auf der Punktzahl
|
||||
* @param {number} punkte - Erreichte Punktzahl
|
||||
* @param {number} maxPunkte - Maximale Punktzahl (default: 100)
|
||||
* @returns {object} - Enthält Note, Prozentage und Details
|
||||
*/
|
||||
function berechneNote(punkte, maxPunkte = 100) {
|
||||
// Eingabe validieren
|
||||
punkte = parseFloat(punkte);
|
||||
maxPunkte = parseFloat(maxPunkte);
|
||||
|
||||
if (isNaN(punkte) || isNaN(maxPunkte)) {
|
||||
return {
|
||||
note: 'Fehler',
|
||||
prozent: 0,
|
||||
valid: false,
|
||||
nachricht: 'Bitte geben Sie Zahlen ein!'
|
||||
};
|
||||
}
|
||||
|
||||
if (maxPunkte <= 0) {
|
||||
return {
|
||||
note: 'Fehler',
|
||||
prozent: 0,
|
||||
valid: false,
|
||||
nachricht: 'Maximale Punktzahl muss größer als 0 sein!'
|
||||
};
|
||||
}
|
||||
|
||||
if (punkte < 0 || punkte > maxPunkte) {
|
||||
return {
|
||||
note: 'Fehler',
|
||||
prozent: 0,
|
||||
valid: false,
|
||||
nachricht: `Punkte müssen zwischen 0 und ${maxPunkte} liegen!`
|
||||
};
|
||||
}
|
||||
|
||||
// Prozentage berechnen
|
||||
const prozent = Math.round((punkte / maxPunkte) * 100);
|
||||
|
||||
// Note basierend auf Prozentage bestimmen
|
||||
let note = 'nicht bestanden';
|
||||
let noteColor = '#dc143c';
|
||||
|
||||
for (const [noteText, range] of Object.entries(NOTENMASSTAB)) {
|
||||
if (prozent >= range.min && prozent <= range.max) {
|
||||
note = noteText;
|
||||
noteColor = range.color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
note: note,
|
||||
prozent: prozent,
|
||||
punkte: punkte,
|
||||
maxPunkte: maxPunkte,
|
||||
valid: true,
|
||||
color: noteColor,
|
||||
nachricht: `${punkte} von ${maxPunkte} Punkten (${prozent}%) = ${note}`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validiert die Eingabe
|
||||
*/
|
||||
function validateInput(value, maxValue = 100) {
|
||||
if (value === '' || value === null) {
|
||||
return { valid: false, error: 'Bitte geben Sie eine Punktzahl ein!' };
|
||||
}
|
||||
|
||||
const num = parseFloat(value);
|
||||
|
||||
if (isNaN(num)) {
|
||||
return { valid: false, error: 'Bitte geben Sie eine Zahl ein!' };
|
||||
}
|
||||
|
||||
if (num < 0 || num > maxValue) {
|
||||
return { valid: false, error: `Punktzahl muss zwischen 0 und ${maxValue} liegen!` };
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// Warte bis DOM geladen ist
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Simple Notenrechner
|
||||
const simplePointsInput = document.getElementById('simple-points');
|
||||
const simpleMaxInput = document.getElementById('simple-max');
|
||||
const simpleCalculateBtn = document.getElementById('simple-calculate-btn');
|
||||
const simpleResult = document.getElementById('simple-result');
|
||||
const simpleProgressBar = document.getElementById('simple-progress-bar');
|
||||
|
||||
// Multiple Notenrechner
|
||||
const addGradeBtn = document.getElementById('add-grade-btn');
|
||||
const multipleCalculateBtn = document.getElementById('multiple-calculate-btn');
|
||||
const multipleResetBtn = document.getElementById('multiple-reset-btn');
|
||||
const gradesContainer = document.getElementById('grades-container');
|
||||
const multipleResult = document.getElementById('multiple-result');
|
||||
const multipleAverage = document.getElementById('multiple-average');
|
||||
|
||||
// Simple Grade Calculator Event Listener
|
||||
if (simpleCalculateBtn && simplePointsInput) {
|
||||
simpleCalculateBtn.addEventListener('click', function() {
|
||||
calculateSimpleGrade();
|
||||
});
|
||||
|
||||
// Enter-Taste auch zum Berechnen verwenden
|
||||
simplePointsInput.addEventListener('keypress', function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
calculateSimpleGrade();
|
||||
}
|
||||
});
|
||||
|
||||
// Live-Berechnung während des Tippens
|
||||
simplePointsInput.addEventListener('input', function() {
|
||||
updateSimpleGradeLive();
|
||||
});
|
||||
|
||||
simpleMaxInput.addEventListener('input', function() {
|
||||
updateSimpleGradeLive();
|
||||
});
|
||||
}
|
||||
|
||||
// Multiple Grade Calculator Event Listener
|
||||
if (addGradeBtn) {
|
||||
addGradeBtn.addEventListener('click', function() {
|
||||
addGradeInput();
|
||||
});
|
||||
}
|
||||
|
||||
if (multipleCalculateBtn) {
|
||||
multipleCalculateBtn.addEventListener('click', function() {
|
||||
calculateMultipleGrades();
|
||||
});
|
||||
}
|
||||
|
||||
if (multipleResetBtn) {
|
||||
multipleResetBtn.addEventListener('click', function() {
|
||||
resetMultipleCalculator();
|
||||
});
|
||||
}
|
||||
|
||||
// Initiale Grade hinzufügen
|
||||
if (gradesContainer) {
|
||||
addGradeInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet einfache Note und zeigt Ergebnis
|
||||
*/
|
||||
function calculateSimpleGrade() {
|
||||
const points = simplePointsInput.value;
|
||||
const max = simpleMaxInput.value || 100;
|
||||
|
||||
// Validierung
|
||||
const validation = validateInput(points, max);
|
||||
if (!validation.valid) {
|
||||
showSimpleError(validation.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Berechnung
|
||||
const result = berechneNote(parseFloat(points), parseFloat(max));
|
||||
|
||||
if (!result.valid) {
|
||||
showSimpleError(result.nachricht);
|
||||
return;
|
||||
}
|
||||
|
||||
// Erfolgreiche Anzeige
|
||||
showSimpleSuccess(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Live-Aktualisierung während des Tippens
|
||||
*/
|
||||
function updateSimpleGradeLive() {
|
||||
const points = simplePointsInput.value;
|
||||
const max = simpleMaxInput.value || 100;
|
||||
|
||||
if (points === '') {
|
||||
simpleResult.innerHTML = '';
|
||||
simpleProgressBar.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const validation = validateInput(points, max);
|
||||
if (!validation.valid) {
|
||||
simpleResult.innerHTML = `<p class="error-text">⚠️ ${validation.error}</p>`;
|
||||
simpleProgressBar.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const result = berechneNote(parseFloat(points), parseFloat(max));
|
||||
if (result.valid) {
|
||||
simpleResult.innerHTML = `
|
||||
<div class="grade-result-inline">
|
||||
<span class="grade-note" style="color: ${result.color};">${result.note}</span>
|
||||
<span class="grade-percent">${result.prozent}%</span>
|
||||
</div>
|
||||
`;
|
||||
updateProgressBar(result.prozent, result.color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fehleranzeige mit rotem Hintergrund
|
||||
*/
|
||||
function showSimpleError(errorMessage) {
|
||||
simplePointsInput.style.borderColor = 'red';
|
||||
simplePointsInput.style.backgroundColor = 'rgba(220, 20, 60, 0.1)';
|
||||
simpleResult.innerHTML = `<p class="error-text">❌ ${errorMessage}</p>`;
|
||||
simpleProgressBar.style.display = 'none';
|
||||
|
||||
// Reset nach 2 Sekunden
|
||||
setTimeout(() => {
|
||||
simplePointsInput.style.borderColor = '';
|
||||
simplePointsInput.style.backgroundColor = '';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erfolgreiche Anzeige mit Progressbar
|
||||
*/
|
||||
function showSimpleSuccess(result) {
|
||||
simplePointsInput.style.borderColor = 'green';
|
||||
simplePointsInput.style.backgroundColor = 'rgba(34, 197, 94, 0.1)';
|
||||
|
||||
simpleResult.innerHTML = `
|
||||
<div class="grade-result-detailed">
|
||||
<div class="result-header">
|
||||
<h4>📊 Ergebnis:</h4>
|
||||
<span class="grade-note-large" style="color: ${result.color};">${result.note}</span>
|
||||
</div>
|
||||
<div class="result-details">
|
||||
<p><strong>Punkte:</strong> ${result.punkte} / ${result.maxPunkte}</p>
|
||||
<p><strong>Prozentage:</strong> ${result.prozent}%</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
updateProgressBar(result.prozent, result.color);
|
||||
|
||||
// Reset nach 3 Sekunden
|
||||
setTimeout(() => {
|
||||
simplePointsInput.style.borderColor = '';
|
||||
simplePointsInput.style.backgroundColor = '';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert die Progressbar
|
||||
*/
|
||||
function updateProgressBar(percent, color) {
|
||||
simpleProgressBar.style.display = 'block';
|
||||
simpleProgressBar.innerHTML = `
|
||||
<div class="progress-bar-background">
|
||||
<div class="progress-bar-fill" style="width: ${percent}%; background-color: ${color};"></div>
|
||||
</div>
|
||||
<p class="progress-text">${percent}% Complete</p>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Neue Grade-Input-Zeile hinzufügen (Multiple)
|
||||
*/
|
||||
function addGradeInput() {
|
||||
if (!gradesContainer) return;
|
||||
|
||||
const gradeId = Date.now();
|
||||
const gradeDiv = document.createElement('div');
|
||||
gradeDiv.className = 'grade-input-group-dom';
|
||||
gradeDiv.id = `grade-${gradeId}`;
|
||||
|
||||
gradeDiv.innerHTML = `
|
||||
<div class="grade-input-wrapper-dom">
|
||||
<input
|
||||
type="number"
|
||||
class="grade-points-dom"
|
||||
placeholder="Punkte"
|
||||
min="0"
|
||||
step="0.5"
|
||||
>
|
||||
<span class="grade-separator">/</span>
|
||||
<input
|
||||
type="number"
|
||||
class="grade-max-dom"
|
||||
placeholder="Max"
|
||||
value="100"
|
||||
min="1"
|
||||
step="1"
|
||||
>
|
||||
<div class="grade-result-dom">
|
||||
<span class="grade-note-dom">-</span>
|
||||
<span class="grade-percent-dom">0%</span>
|
||||
</div>
|
||||
<button class="grade-remove-btn-dom" type="button">✕</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
gradesContainer.appendChild(gradeDiv);
|
||||
|
||||
// Event Listener für neue Eingabe
|
||||
const pointsInput = gradeDiv.querySelector('.grade-points-dom');
|
||||
const maxInput = gradeDiv.querySelector('.grade-max-dom');
|
||||
const removeBtn = gradeDiv.querySelector('.grade-remove-btn-dom');
|
||||
const noteSpan = gradeDiv.querySelector('.grade-note-dom');
|
||||
const percentSpan = gradeDiv.querySelector('.grade-percent-dom');
|
||||
|
||||
function updateGradeLive() {
|
||||
const points = parseFloat(pointsInput.value) || 0;
|
||||
const max = parseFloat(maxInput.value) || 100;
|
||||
|
||||
if (max > 0 && points >= 0 && points <= max) {
|
||||
const result = berechneNote(points, max);
|
||||
if (result.valid) {
|
||||
noteSpan.textContent = result.note;
|
||||
percentSpan.textContent = result.prozent + '%';
|
||||
noteSpan.style.color = result.color;
|
||||
}
|
||||
} else {
|
||||
noteSpan.textContent = '-';
|
||||
percentSpan.textContent = '0%';
|
||||
}
|
||||
}
|
||||
|
||||
pointsInput.addEventListener('input', updateGradeLive);
|
||||
maxInput.addEventListener('input', updateGradeLive);
|
||||
|
||||
removeBtn.addEventListener('click', function() {
|
||||
gradeDiv.remove();
|
||||
});
|
||||
|
||||
pointsInput.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet mehrere Noten
|
||||
*/
|
||||
function calculateMultipleGrades() {
|
||||
const gradeInputs = gradesContainer.querySelectorAll('.grade-input-group-dom');
|
||||
const noten = [];
|
||||
|
||||
gradeInputs.forEach(input => {
|
||||
const points = parseFloat(input.querySelector('.grade-points-dom').value);
|
||||
const max = parseFloat(input.querySelector('.grade-max-dom').value);
|
||||
|
||||
if (!isNaN(points) && !isNaN(max) && max > 0) {
|
||||
const result = berechneNote(points, max);
|
||||
if (result.valid) {
|
||||
noten.push(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (noten.length === 0) {
|
||||
multipleResult.innerHTML = `<p class="error-text">⚠️ Bitte füllen Sie mindestens eine Note aus!</p>`;
|
||||
multipleAverage.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Einzelnoten anzeigen
|
||||
let resultHTML = '<div class="results-list"><h4>📊 Ihre Noten:</h4>';
|
||||
noten.forEach((note, index) => {
|
||||
resultHTML += `
|
||||
<div class="result-item-dom">
|
||||
<span class="result-number">#${index + 1}</span>
|
||||
<span class="result-details">${note.punkte} / ${note.maxPunkte} (${note.prozent}%)</span>
|
||||
<span class="result-grade-dom" style="color: ${note.color};">${note.note}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
resultHTML += '</div>';
|
||||
multipleResult.innerHTML = resultHTML;
|
||||
|
||||
// Durchschnitt berechnen
|
||||
if (noten.length > 1) {
|
||||
const durchschnittProzent = Math.round(
|
||||
noten.reduce((sum, n) => sum + n.prozent, 0) / noten.length
|
||||
);
|
||||
|
||||
let durchschnittNote = 'nicht bestanden';
|
||||
let durchschnittColor = '#dc143c';
|
||||
for (const [noteText, range] of Object.entries(NOTENMASSTAB)) {
|
||||
if (durchschnittProzent >= range.min && durchschnittProzent <= range.max) {
|
||||
durchschnittNote = noteText;
|
||||
durchschnittColor = range.color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
multipleAverage.innerHTML = `
|
||||
<div class="average-box-dom">
|
||||
<h4>📈 Durchschnitt:</h4>
|
||||
<div class="average-display-dom">
|
||||
<div class="average-percent-dom">${durchschnittProzent}%</div>
|
||||
<div class="average-note-dom" style="color: ${durchschnittColor};">${durchschnittNote}</div>
|
||||
</div>
|
||||
<p class="average-details-dom">Von ${noten.length} Note(n)</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Console Ausgabe
|
||||
console.log(`Durchschnitt: ${durchschnittProzent}% = ${durchschnittNote}`);
|
||||
} else {
|
||||
multipleAverage.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt Multiple Grade Calculator zurück
|
||||
*/
|
||||
function resetMultipleCalculator() {
|
||||
gradesContainer.innerHTML = '';
|
||||
multipleResult.innerHTML = '';
|
||||
multipleAverage.innerHTML = '';
|
||||
addGradeInput();
|
||||
}
|
||||
});
|
||||
|
|
@ -46,12 +46,16 @@
|
|||
<p>Geben Sie Ihre erreichten Punkte und die maximale Punktzahl ein. Der Rechner berechnet automatisch den Prozentsatz.</p>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<h4>2️⃣ Mehrere Noten</h4>
|
||||
<h4>2️⃣ Live-Berechnung</h4>
|
||||
<p>Das Ergebnis wird direkt auf der Seite angezeigt - ohne Dialoge! Moderne DOM-Manipulation mit JavaScript.</p>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<h4>3️⃣ Mehrere Noten</h4>
|
||||
<p>Fügen Sie mehrere Noten hinzu, um einen Durchschnitt zu berechnen. Perfekt für Prüfungsvorbereitung!</p>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<h4>3️⃣ Sofort Feedback</h4>
|
||||
<p>Sehen Sie sofort, welche Note Sie erhalten – mit Prozentanzeige und hilfreichen Farben.</p>
|
||||
<h4>4️⃣ Visuelle Rückmeldung</h4>
|
||||
<p>Farbige Progressbars und Fehlerbehandlung mit rotem Hintergrund beim Input. Sofort Feedback!</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -97,43 +101,88 @@
|
|||
</section>
|
||||
|
||||
<section class="calculator-section">
|
||||
<h2>🧮 Interaktiver Notenrechner</h2>
|
||||
<h2>🧮 Notenrechner – DOM-Version</h2>
|
||||
|
||||
<div class="calculator-container">
|
||||
<div class="grades-input-section">
|
||||
<h3>Gebe deine Noten ein:</h3>
|
||||
<!-- Tab Navigation -->
|
||||
<div class="calculator-tabs">
|
||||
<button class="tab-btn active" onclick="switchNoteTab('simple-tab')">📊 Einfacher Rechner</button>
|
||||
<button class="tab-btn" onclick="switchNoteTab('multiple-tab')">📈 Multiple Noten</button>
|
||||
</div>
|
||||
|
||||
<!-- Simple Grade Calculator Tab -->
|
||||
<div id="simple-tab" class="tab-content active">
|
||||
<div class="calculator-box">
|
||||
<h3>Einfache Notenberechnung</h3>
|
||||
<p>Geben Sie eine Punktzahl ein und sehen Sie sofort das Ergebnis auf der Seite!</p>
|
||||
|
||||
<div id="grades-container" class="grades-container">
|
||||
<div class="simple-input-group">
|
||||
<div class="input-field">
|
||||
<label for="simple-points">Erreichte Punkte:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="simple-points"
|
||||
class="form-input"
|
||||
placeholder="z.B. 85"
|
||||
min="0"
|
||||
step="0.5"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="input-field">
|
||||
<label for="simple-max">Maximale Punktzahl:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="simple-max"
|
||||
class="form-input"
|
||||
placeholder="z.B. 100"
|
||||
value="100"
|
||||
min="1"
|
||||
>
|
||||
</div>
|
||||
|
||||
<button id="simple-calculate-btn" class="btn btn-calculate" type="button">
|
||||
🧮 Berechnen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="simple-result" class="result-box"></div>
|
||||
<div id="simple-progress-bar" class="progress-bar-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Multiple Grade Calculator Tab -->
|
||||
<div id="multiple-tab" class="tab-content">
|
||||
<div class="calculator-box">
|
||||
<h3>Mehrere Noten berechnen</h3>
|
||||
<p>Geben Sie mehrere Noten ein – der Durchschnitt wird automatisch berechnet!</p>
|
||||
|
||||
<div id="grades-container" class="grades-container-dom">
|
||||
<!-- Grades werden hier dynamisch eingefügt -->
|
||||
</div>
|
||||
|
||||
<div class="calculator-buttons">
|
||||
<button id="add-grade-btn" class="btn btn-add">➕ Weitere Note hinzufügen</button>
|
||||
<button id="calculate-btn" class="btn btn-calculate">🧮 Berechnen</button>
|
||||
<button id="reset-btn" class="btn btn-reset">🔄 Zurücksetzen</button>
|
||||
<button id="add-grade-btn" class="btn btn-add" type="button">➕ Note hinzufügen</button>
|
||||
<button id="multiple-calculate-btn" class="btn btn-calculate" type="button">🧮 Berechnen</button>
|
||||
<button id="multiple-reset-btn" class="btn btn-reset" type="button">🔄 Zurücksetzen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="results-section">
|
||||
<div id="result" class="result-container">
|
||||
<!-- Ergebnisse werden hier eingefügt -->
|
||||
</div>
|
||||
<div id="average-result" class="average-container">
|
||||
<!-- Durchschnitt wird hier eingefügt -->
|
||||
</div>
|
||||
<div id="multiple-result" class="result-box"></div>
|
||||
<div id="multiple-average" class="average-box"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>💡 Tipps & Tricks</h2>
|
||||
<h2>💡 Moderne DOM-Manipulation mit JavaScript</h2>
|
||||
<div class="tips-box">
|
||||
<ul>
|
||||
<li><strong>Live-Anzeige:</strong> Die Note wird automatisch aktualisiert, während du die Punkte eingibst!</li>
|
||||
<li><strong>Verschiedene Skalen:</strong> Du kannst auch mit anderen Maximalwerten arbeiten (z.B. 50, 80 Punkte)</li>
|
||||
<li><strong>Durchschnitt:</strong> Wenn du mehrere Noten eingibst, berechnet der Rechner automatisch den Durchschnitt</li>
|
||||
<li><strong>Mobile-freundlich:</strong> Der Rechner funktioniert auf deinem Handy genauso gut wie am Computer!</li>
|
||||
<li><strong>Schnell zurücksetzen:</strong> Mit dem "Zurücksetzen"-Button kannst du schnell von vorne anfangen</li>
|
||||
<li><strong>Keine Dialoge:</strong> Ergebnisse werden direkt auf der Webseite angezeigt – ohne prompt() oder alert()!</li>
|
||||
<li><strong>addEventListener:</strong> Event Handler sind in der JavaScript-Datei (notenrechner-dom.js) registriert, nicht im HTML</li>
|
||||
<li><strong>DOM-Zugriff:</strong> Das Skript nutzt document.getElementById() und querySelector() um auf HTML-Elemente zuzugreifen</li>
|
||||
<li><strong>Live-Validierung:</strong> Bei ungültiger Eingabe wird das Input-Feld rot hinterlegt und eine Fehlermeldung angezeigt</li>
|
||||
<li><strong>Farbige Progressbar:</strong> Ein visueller Balken zeigt den Prozentsatz in der Notenfarbe an</li>
|
||||
<li><strong>Fehlerbehandlung:</strong> Umfangreiche Validierung der Eingaben mit aussagekräftigen Fehlermeldungen</li>
|
||||
<li><strong>Template Literals:</strong> JavaScript nutzt moderne Template Literals für die HTML-Ausgabe</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -143,7 +192,27 @@
|
|||
</footer>
|
||||
</main>
|
||||
|
||||
<script src="js/notenrechner.js"></script>
|
||||
<script src="js/notenrechner-dom.js"></script>
|
||||
<script src="js/script.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* Tab-Wechsel Funktion für Notenrechner
|
||||
*/
|
||||
function switchNoteTab(tabName) {
|
||||
// Alle Tabs verstecken
|
||||
document.querySelectorAll('.tab-content').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
|
||||
// Alle Buttons deaktivieren
|
||||
document.querySelectorAll('.calculator-tabs .tab-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
|
||||
// Gewählten Tab anzeigen
|
||||
document.getElementById(tabName).classList.add('active');
|
||||
event.target.classList.add('active');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue