My little To-do list with HTML5/CSS3 and JavaScript
Beside the Linux stuff, I try to do something more easy, even on a static (no online) webpage. Only for me.
How it looks like

The code
HTML5
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Meine ToDo-Liste</title>
<link rel="stylesheet" href="CSS/aufgabenliste.css">
</head>
<body>
<div id="myDIV" class="header">
<h2>My To Do List</h2>
<div class="input-group">
<input type="text" id="myInput" placeholder='z. B. "Milch kaufen morgen" oder "Steuer bis 31.12."' autocomplete="off">
<span onclick="newElement()" class="addBtn">Add</span>
</div>
</div>
<ul id="myUL"></ul>
<div id="counter">Wird geladen...</div>
<div class="backup-buttons">
<button onclick="exportBackup()" class="backup-btn">Backup erstellen</button>
<button onclick="document.getElementById('importFile').click()" class="backup-btn">Backup laden</button>
<input type="file" id="importFile" accept=".json" onchange="importBackup(event)">
</div>
<script src="javascript/aufgabenliste.js"></script>
</body>
</html>
CSS3
/* aufgabenliste.css – finale, saubere Version */
body {
margin: 0;
min-width: 250px;
font-family: system-ui, -apple-system, sans-serif;
background: #f5f7fa;
color: #333;
}
h2 {
color: white;
margin: 0 0 20px 0;
font-size: 2em;
}
/* Box-Modell */
* { box-sizing: border-box; }
/* Zähler */
#counter {
margin: 20px 0;
font-size: 17px;
color: #680895;
text-align: center;
font-weight: 500;
}
/* Liste */
ul {
margin: 0;
padding: 0;
list-style: none;
}
ul li {
cursor: pointer;
position: relative;
padding: 14px 8px 14px 50px;
background: white;
font-size: 18px;
transition: 0.3s;
border-bottom: 1px solid #eee;
user-select: none;
}
ul li:nth-child(odd) { background: #fbfbff; }
ul li:hover { background: #f0f0ff; }
ul li.checked {
background: #680895;
color: white;
text-decoration: line-through;
}
ul li.checked::before {
content: “;
position: absolute;
border: 3px solid white;
border-width: 0 3px 3px 0;
top: 16px;
left: 18px;
transform: rotate(45deg);
height: 18px;
width: 9px;
}
/* Close-Button */
.close {
position: absolute;
right: 0;
top: 0;
padding: 14px 20px;
font-weight: bold;
font-size: 24px;
}
.close:hover {
background-color: #c20c45;
color: white;
}
/* Header */
.header {
background: linear-gradient(135deg, #07cfee, #0099cc);
padding: 40px 20px;
color: white;
text-align: center;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.header:after { content: „“; display: table; clear: both; }
/* Eingabe */
.input-group {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
margin: 20px auto;
max-width: 600px;
}
#myInput {
flex: 1;
min-width: 250px;
padding: 14px;
border: none;
border-radius: 8px;
font-size: 17px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.addBtn {
padding: 14px 28px;
background: #680895;
color: white;
font-size: 17px;
border: none;
border-radius: 8px;
cursor: pointer;
transition: 0.3s;
font-weight: bold;
}
.addBtn:hover {
background: #520b77;
transform: translateY(-2px);
}
/* Fälligkeitsdatum-Anzeige */
.due-date {
margin-left: 12px;
font-size: 0.9em;
color: #555;
font-weight: 500;
}
/* Überfällig / Heute */
.li-overdue {
border-left: 6px solid #e74c3c !important;
background-color: #ffeaea !important;
font-weight: bold;
}
.li-due-today {
border-left: 6px solid #f39c12;
background-color: #fff8e1;
}
/* Backup-Buttons */
.backup-buttons {
margin: 30px 0;
text-align: center;
}
.backup-btn {
margin: 0 10px;
padding: 12px 20px;
background: white;
border: 2px solid #680895;
color: #680895;
border-radius: 8px;
cursor: pointer;
font-size: 15px;
font-weight: bold;
transition: all 0.3s ease;
}
.backup-btn:hover {
background: #680895;
color: white;
transform: translateY(-3px);
box-shadow: 0 6px 12px rgba(104, 8, 149, 0.3);
}
#importFile {
display: none;
}
JavaScript
const STORAGE_KEY = ‚todoListItems‘;
// ==================== DATUM FORMATIEREN ====================
function formatDate(dateString) {
if (!dateString) return „“;
const date = new Date(dateString);
const options = { weekday: ’short‘, day: ’numeric‘, month: ’short‘ };
return `Fällig: ${date.toLocaleDateString(‚de-DE‘, options)}`;
}
// ==================== SPEICHERN ====================
function saveTasks() {
const listItems = document.querySelectorAll(‚#myUL li‘);
const tasks = [];
listItems.forEach(item => {
tasks.push({
text: item.firstChild.textContent.trim(),
checked: item.classList.contains(‚checked‘),
dueDate: item.dataset.dueDate || null
});
});
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
updateCounter();
}
// ==================== LADEN ====================
function loadTasks() {
const storedTasks = localStorage.getItem(STORAGE_KEY);
if (storedTasks) {
const tasks = JSON.parse(storedTasks);
tasks.forEach(task => {
createListItem(task.text, task.checked, task.dueDate, false);
});
} else {
createListItem(„Milch kaufen morgen“, false, null, true);
createListItem(„Steuererklärung bis 31.12.“, true, null, true);
createListItem(„Eltern anrufen Freitag“, false, null, true);
}
updateCounter();
}
// ==================== NEUE AUFGABE ERSTELLEN ====================
function createListItem(text, isChecked = false, dueDate = null, shouldSave = true) {
const li = document.createElement(„li“);
li.appendChild(document.createTextNode(text));
if (isChecked) li.classList.add(‚checked‘);
if (dueDate) li.dataset.dueDate = dueDate;
if (dueDate) {
const dateSpan = document.createElement(„SPAN“);
dateSpan.className = „due-date“;
dateSpan.textContent = “ “ + formatDate(dueDate);
dateSpan.style.cssText = „margin-left: 12px; font-size: 0.9em; color: #555;“;
li.appendChild(dateSpan);
const today = new Date();
today.setHours(0, 0, 0, 0);
const due = new Date(dueDate);
due.setHours(0, 0, 0, 0);
if (due < today) li.classList.add(„li-overdue“);
else if (due.getTime() === today.getTime()) li.classList.add(„li-due-today“);
}
const span = document.createElement(„SPAN“);
span.className = „close“;
span.appendChild(document.createTextNode(„ד));
span.onclick = () => { li.remove(); saveTasks(); };
li.appendChild(span);
document.getElementById(„myUL“).appendChild(li);
if (shouldSave) saveTasks();
return li;
}
// ==================== INTELLIGENTE EINGABE ====================
function newElement() {
const input = document.getElementById(„myInput“);
const rawText = input.value.trim();
if (!rawText) { alert(„Bitte eine Aufgabe eingeben!“); return; }
let taskText = rawText;
let dueDate = null;
const heute = new Date(); heute.setHours(0,0,0,0);
const lower = rawText.toLowerCase();
// 1. Datum: 25.12. / 25.12 / 25/12 / 25-12
const dateMatch = rawText.match(/(?:\s|^)(\d{1,2})[.\-\/](\d{1,2})(?:[.\-\/](\d{2,4}))?(?=\s|$)/);
if (dateMatch) {
let day = parseInt(dateMatch[1], 10);
let month = parseInt(dateMatch[2], 10) – 1;
let year = dateMatch[3] ? parseInt(dateMatch[3], 10) : heute.getFullYear();
if (year < 100) year += 2000;
const d = new Date(year, month, day);
if (!isNaN(d.getTime())) {
dueDate = d.toISOString().split(‚T‘)[0];
taskText = rawText.replace(dateMatch[0], „“).trim();
}
}
// 2. morgen / übermorgen / Wochentage
else if (lower.includes(„morgen“)) {
const d = new Date(heute); d.setDate(d.getDate() + 1);
dueDate = d.toISOString().split(‚T‘)[0];
taskText = rawText.replace(/morgen/gi, „“).trim();
}
else if (lower.includes(„übermorgen“)) {
const d = new Date(heute); d.setDate(d.getDate() + 2);
dueDate = d.toISOString().split(‚T‘)[0];
taskText = rawText.replace(/übermorgen/gi, „“).trim();
}
else {
const days = [„sonntag“,“montag“,“dienstag“,“mittwoch“,“donnerstag“,“freitag“,“samstag“];
for (let i = 0; i < days.length; i++) {
if (lower.includes(days[i])) {
const diff = (i – heute.getDay() + 7) % 7 || 7;
const d = new Date(heute); d.setDate(d.getDate() + diff);
dueDate = d.toISOString().split(‚T‘)[0];
taskText = rawText.replace(new RegExp(days[i], „gi“), „“).trim();
break;
}
}
}
if (!taskText) taskText = rawText;
createListItem(taskText, false, dueDate);
input.value = „“; input.focus();
}
// ==================== ENTER-TASTE ====================
document.getElementById(„myInput“).addEventListener(„keydown“, e => {
if (e.key === „Enter“) { e.preventDefault(); newElement(); }
});
// ==================== ABHAKEN ====================
document.querySelector(‚ul‘).addEventListener(‚click‘, e => {
if (e.target.tagName === ‚LI‘) {
e.target.classList.toggle(‚checked‘);
saveTasks();
}
}, false);
// ==================== ZÄHLER ====================
function updateCounter() {
const total = document.querySelectorAll(‚#myUL li‘).length;
const done = document.querySelectorAll(‚#myUL li.checked‘).length;
const pending = total – done;
const el = document.getElementById(„counter“);
if (el) el.textContent = `${pending} offen • ${done} erledigt • ${total} insgesamt`;
}
// ==================== BACKUP ERSTELLEN ====================
function exportBackup() {
const data = localStorage.getItem(STORAGE_KEY);
if (!data || JSON.parse(data).length === 0) {
alert(„Keine Aufgaben zum Sichern!“);
return;
}
const date = new Date().toISOString().slice(0,10);
const blob = new Blob([data], { type: ‚application/json‘ });
const url = URL.createObjectURL(blob);
const a = document.createElement(‚a‘);
a.href = url;
a.download = `todolist-backup-${date}.json`;
a.click();
URL.revokeObjectURL(url);
alert(`Backup gespeichert: todolist-backup-${date}.json`);
}
// ==================== BACKUP WIEDERHERSTELLEN ====================
function importBackup(event) {
const file = event.target.files[0];
if (!file || !file.name.endsWith(‚.json‘)) {
alert(„Bitte eine gültige .json-Datei auswählen!“);
return;
}
const reader = new FileReader();
reader.onload = function(e) {
try {
const content = e.target.result;
JSON.parse(content); // Gültigkeit prüfen
localStorage.setItem(STORAGE_KEY, content);
document.getElementById(„myUL“).innerHTML = „“;
loadTasks();
alert(„Backup erfolgreich wiederhergestellt!“);
} catch (err) {
alert(„Fehler: Ungültige oder beschädigte Datei!“);
}
};
reader.readAsText(file);
}
// ==================== AUTOMATISCHES LOGGING (kann später erweitert werden) ====================
setInterval(() => {
if (document.querySelectorAll(‚#myUL li‘).length > 0) {
console.log(„Automatisches Backup aktiv – alles ist sicher in localStorage gespeichert“);
}
}, 5 * 60 * 1000);
// ==================== START ====================
loadTasks();
updateCounter();