このツールは …回 使われています。
Simple ToDo List
0 tasks
使い方マニュアル
- 追加: 入力欄にタスクを書き、Enterキーまたは「+」ボタンを押します。
- 編集: タスクの文字をクリックすると直接編集できます。
- 完了: チェックボックスをクリックして完了/未完了を切り替えます。
- 並べ替え: タスクをドラッグ&ドロップして順番を入れ替えられます。
- 削除: 「×」ボタンで個別削除。「完了削除」でチェック済みを一括削除します。
- モード切替: 右上のアイコンでライト/ダークモードを切り替えられます。
ご利用にあたっては、本Webアプリの 利用規約 も合わせてご確認ください。
バグや表示の乱れなどがあれば、こちらよりお知らせください。
目次
To Do リストの実装コード
<div id="web-apps-todo-list">
<div class="app-container">
<div class="app-header">
<div class="app-title">Simple ToDo List</div>
<button id="theme-toggle" title="Switch Theme"></button>
</div>
<div class="input-group">
<input type="text" id="new-todo" placeholder="タスクを入力..." autocomplete="off">
<button id="add-btn">
<span class="btn-text">追加</span>
<span class="btn-icon">+</span>
</button>
</div>
<ul id="todo-list"></ul>
<div class="app-footer">
<span id="task-count">0 tasks</span>
<div class="filters">
<button class="filter-btn active" data-filter="all">すべて</button>
<button class="filter-btn" data-filter="active">未完了</button>
<button class="filter-btn" data-filter="completed">完了</button>
</div>
<div class="footer-actions">
<button id="clear-completed-btn">完了削除</button>
<button id="clear-btn">全削除</button>
</div>
</div>
</div>
<div id="drag-overlay" class="hidden"></div>
</div>/* Scoped styles for ToDo List App */
#web-apps-todo-list {
--primary-color: #4a90e2;
--primary-hover: #357abd;
--secondary-color: #f5f6fa;
--bg-color: #ffffff;
--text-color: #333333;
--text-sub: #7f8c8d;
--border-color: #e1e4e8;
--danger-color: #ff4757;
--success-color: #2ed573;
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--radius: 8px;
/* Variable for item background in list */
--item-bg: var(--secondary-color);
--item-hover-border: var(--border-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: var(--text-color);
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
transition: all 0.3s ease;
}
/* Dark Mode */
#web-apps-todo-list.dark {
--primary-color: #5d9cec;
--primary-hover: #4a89dc;
--secondary-color: #34495e;
--bg-color: #2c3e50;
--text-color: #ecf0f1;
--text-sub: #bdc3c7;
--border-color: #4b6584;
--danger-color: #ff6b81;
--success-color: #2ecc71;
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.2);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
--item-bg: #34495e;
--item-hover-border: #5d6d7e;
}
#web-apps-todo-list * {
box-sizing: border-box;
}
/* App Container */
#web-apps-todo-list .app-container {
background: var(--bg-color);
border-radius: var(--radius);
box-shadow: var(--shadow-md);
padding: 25px;
border: 1px solid var(--border-color);
transition: background 0.3s, border-color 0.3s;
}
#web-apps-todo-list .app-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
#web-apps-todo-list .app-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
margin: 0;
}
#web-apps-todo-list #theme-toggle {
background: none;
border: none;
width: 32px;
height: 32px;
cursor: pointer;
padding: 0;
border-radius: 50%;
transition: transform 0.2s, background 0.2s;
position: relative;
overflow: hidden;
}
#web-apps-todo-list #theme-toggle::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 14px;
height: 14px;
border-radius: 50%;
box-shadow: 4px -4px 0 0 var(--text-color);
/* Moon shape */
transition: all 0.3s ease;
}
#web-apps-todo-list.dark #theme-toggle::before {
box-shadow: none;
background-color: var(--primary-color);
/* Sun center */
width: 10px;
height: 10px;
box-shadow: 0 0 0 4px transparent;
/* Reset moon shadow */
}
#web-apps-todo-list.dark #theme-toggle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
border: 2px dashed var(--primary-color);
/* Sun rays */
border-radius: 50%;
animation: sun-spin 10s linear infinite;
}
@keyframes sun-spin {
from {
transform: translate(-50%, -50%) rotate(0deg);
}
to {
transform: translate(-50%, -50%) rotate(360deg);
}
}
#web-apps-todo-list #theme-toggle:hover {
background-color: var(--secondary-color);
}
/* Input Group */
#web-apps-todo-list .input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
#web-apps-todo-list #new-todo {
flex: 1;
padding: 12px 15px;
border: 2px solid var(--border-color);
border-radius: var(--radius);
font-size: 1rem;
transition: border-color 0.2s;
outline: none;
}
#web-apps-todo-list #new-todo:focus {
border-color: var(--primary-color);
}
#web-apps-todo-list #add-btn {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: var(--radius);
padding: 0 20px;
cursor: pointer;
font-weight: 600;
transition: transform 0.1s, background-color 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
#web-apps-todo-list #add-btn:hover {
background-color: #357abd;
}
#web-apps-todo-list #add-btn:active {
transform: scale(0.95);
}
#web-apps-todo-list .btn-icon {
font-size: 1.2rem;
margin-left: 5px;
display: none;
}
/* List Items */
#web-apps-todo-list #todo-list {
list-style: none;
padding: 0;
margin: 0;
}
#web-apps-todo-list .todo-item {
display: flex;
align-items: center;
padding: 12px;
background: var(--item-bg);
margin-bottom: 8px;
border-radius: 6px;
transition: transform 0.2s, opacity 0.2s, background 0.3s;
border: 1px solid transparent;
cursor: grab;
}
#web-apps-todo-list .todo-item:hover {
border-color: var(--item-hover-border);
transform: translateX(2px);
}
#web-apps-todo-list .todo-item.dragging {
opacity: 0.5;
background: #e8eaed;
cursor: grabbing;
}
/* Footer & Filters */
#web-apps-todo-list .app-footer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid var(--border-color);
font-size: 0.85rem;
color: var(--text-sub);
gap: 10px;
}
#web-apps-todo-list .filters {
display: flex;
background: var(--secondary-color);
padding: 3px;
border-radius: 6px;
gap: 2px;
}
#web-apps-todo-list .filter-btn {
background: none;
border: none;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
color: var(--text-sub);
transition: all 0.2s;
}
#web-apps-todo-list .filter-btn.active {
background-color: white;
color: var(--primary-color);
font-weight: bold;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
#web-apps-todo-list.dark .filter-btn.active {
background-color: var(--bg-color);
color: var(--primary-color);
}
#web-apps-todo-list .footer-actions {
display: flex;
gap: 5px;
}
#web-apps-todo-list #clear-btn,
#web-apps-todo-list #clear-completed-btn {
background: none;
border: none;
color: var(--text-sub);
cursor: pointer;
font-size: 0.8rem;
padding: 5px 8px;
border-radius: 4px;
transition: color 0.2s;
}
#web-apps-todo-list #clear-btn:hover {
color: var(--danger-color);
background-color: rgba(255, 71, 87, 0.1);
}
#web-apps-todo-list #clear-completed-btn:hover {
color: var(--text-color);
background-color: rgba(0, 0, 0, 0.05);
}
#web-apps-todo-list .empty-state {
text-align: center;
padding: 30px;
color: #b0b0b0;
font-style: italic;
}
/* Responsive */
@media (max-width: 400px) {
#web-apps-todo-list .btn-text {
display: none;
}
#web-apps-todo-list .btn-icon {
display: inline;
}
#web-apps-todo-list {
padding: 10px;
}
}
/* Custom Checkbox (No Emojis) */
#web-apps-todo-list .checkbox-custom {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #bdc3c7;
border-radius: 4px;
margin-right: 12px;
cursor: pointer;
position: relative;
flex-shrink: 0;
}
#web-apps-todo-list .checkbox-custom:checked {
background-color: var(--success-color);
border-color: var(--success-color);
}
#web-apps-todo-list .checkbox-custom:checked::after {
content: '';
position: absolute;
left: 6px;
top: 2px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
#web-apps-todo-list .todo-text {
flex: 1;
font-size: 0.95rem;
word-break: break-all;
cursor: text;
}
#web-apps-todo-list .todo-item.completed .todo-text {
text-decoration: line-through;
color: #a0a0a0;
}
#web-apps-todo-list .item-controls {
display: flex;
gap: 5px;
opacity: 0;
transition: opacity 0.2s;
}
#web-apps-todo-list .todo-item:hover .item-controls {
opacity: 1;
}
#web-apps-todo-list .action-btn {
background: none;
border: none;
cursor: pointer;
padding: 6px;
border-radius: 4px;
color: #95a5a6;
width: 28px;
height: 28px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
#web-apps-todo-list .delete-btn::before,
#web-apps-todo-list .delete-btn::after {
content: '';
position: absolute;
width: 14px;
height: 2px;
background-color: currentColor;
border-radius: 2px;
}
#web-apps-todo-list .delete-btn::before {
transform: rotate(45deg);
}
#web-apps-todo-list .delete-btn::after {
transform: rotate(-45deg);
}
#web-apps-todo-list .delete-btn:hover {
background-color: #fee;
color: var(--danger-color);
}(function () {
'use strict';
// Config
const CONFIG = {
ROOT_ID: 'web-apps-todo-list',
STORAGE_KEY: 'web_apps_todo_list_data',
THEME_KEY: 'web_apps_todo_list_theme'
// API_URL removed for public release
};
// DOM Elements
const root = document.getElementById(CONFIG.ROOT_ID);
if (!root) return; // Guard clause
const elements = {
input: root.querySelector('#new-todo'),
addBtn: root.querySelector('#add-btn'),
list: root.querySelector('#todo-list'),
clearBtn: root.querySelector('#clear-btn'),
clearCompletedBtn: root.querySelector('#clear-completed-btn'),
taskCount: root.querySelector('#task-count'),
themeToggle: root.querySelector('#theme-toggle'),
filterBtns: root.querySelectorAll('.filter-btn')
};
// State
let todos = JSON.parse(localStorage.getItem(CONFIG.STORAGE_KEY)) || [];
let currentFilter = 'all'; // 'all', 'active', 'completed'
// --- Core Logic ---
function saveTodos() {
localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(todos));
updateTaskCount();
}
function updateTaskCount() {
const remaining = todos.filter(t => !t.completed).length;
elements.taskCount.textContent = `${remaining} items left`;
// Empty State for current filter
const visibleTodos = filterTodos(todos);
if (visibleTodos.length === 0) {
let message = "タスクがありません。";
if (currentFilter === 'active' && todos.length > 0) message = "未完了のタスクはありません。";
if (currentFilter === 'completed' && todos.length > 0) message = "完了済みのタスクはありません。";
// Keep empty state simple
if (elements.list.children.length === 0) {
elements.list.innerHTML = `<li class="empty-state">${message}</li>`;
}
}
}
function filterTodos(todos) {
if (currentFilter === 'active') return todos.filter(t => !t.completed);
if (currentFilter === 'completed') return todos.filter(t => t.completed);
return todos;
}
function renderTodos() {
elements.list.innerHTML = '';
const visibleTodos = filterTodos(todos);
if (visibleTodos.length === 0) {
updateTaskCount();
return;
}
visibleTodos.forEach(todo => {
// Find original index for actions
const index = todos.indexOf(todo);
const li = createTodoElement(todo, index);
elements.list.appendChild(li);
});
updateTaskCount();
}
function createTodoElement(todo, index) {
const li = document.createElement('li');
li.className = `todo-item ${todo.completed ? 'completed' : ''}`;
li.draggable = true;
li.dataset.index = index;
// Checkbox
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox-custom';
checkbox.checked = todo.completed;
checkbox.addEventListener('change', () => toggleComplete(index));
// Text
const textSpan = document.createElement('span');
textSpan.className = 'todo-text';
textSpan.textContent = todo.text;
textSpan.contentEditable = true;
textSpan.addEventListener('blur', (e) => updateTodoText(index, e.target.textContent));
textSpan.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
textSpan.blur();
}
});
// Controls
const controls = document.createElement('div');
controls.className = 'item-controls';
const deleteBtn = document.createElement('button');
deleteBtn.className = 'action-btn delete-btn';
deleteBtn.title = '削除';
deleteBtn.ariaLabel = '削除';
deleteBtn.addEventListener('click', () => deleteTodo(index));
controls.appendChild(deleteBtn);
li.appendChild(checkbox);
li.appendChild(textSpan);
li.appendChild(controls);
// Drag Events
li.addEventListener('dragstart', handleDragStart);
li.addEventListener('dragover', handleDragOver);
li.addEventListener('drop', handleDrop);
li.addEventListener('dragend', handleDragEnd);
return li;
}
// --- Actions ---
function addTodo() {
const text = elements.input.value.trim();
if (text) {
todos.push({ text, completed: false });
elements.input.value = '';
saveTodos();
renderTodos();
elements.input.focus();
}
}
function deleteTodo(index) {
if (confirm('このタスクを削除しますか?')) {
todos.splice(index, 1);
saveTodos();
renderTodos();
}
}
function updateTodoText(index, newText) {
const text = newText.trim();
if (text) {
todos[index].text = text;
saveTodos();
} else {
renderTodos();
}
}
function toggleComplete(index) {
todos[index].completed = !todos[index].completed;
saveTodos();
renderTodos();
}
function clearTodos() {
if (todos.length > 0 && confirm('すべてのタスクを削除しますか?')) {
todos = [];
saveTodos();
renderTodos();
}
}
function clearCompleted() {
const completedCount = todos.filter(t => t.completed).length;
if (completedCount > 0 && confirm(`完了済みのタスク(${completedCount}件)を削除しますか?`)) {
todos = todos.filter(t => !t.completed);
saveTodos();
renderTodos();
} else if (completedCount === 0) {
alert("完了済みのタスクはありません。");
}
}
function setFilter(filterType) {
currentFilter = filterType;
elements.filterBtns.forEach(btn => {
if (btn.dataset.filter === filterType) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
renderTodos();
}
// --- Theme Logic ---
function initTheme() {
const savedTheme = localStorage.getItem(CONFIG.THEME_KEY);
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
}
function toggleTheme() {
const isDark = root.classList.toggle('dark');
localStorage.setItem(CONFIG.THEME_KEY, isDark ? 'dark' : 'light');
}
// --- Drag & Drop ---
let draggedItemIndex = null;
function handleDragStart(e) {
const item = e.target.closest('li');
if (!item) return;
draggedItemIndex = +item.dataset.index;
e.dataTransfer.setData('text/plain', draggedItemIndex);
e.dataTransfer.effectAllowed = 'move';
setTimeout(() => {
item.classList.add('dragging');
}, 0);
}
function handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
return false;
}
function handleDrop(e) {
e.preventDefault();
e.stopPropagation();
if (currentFilter !== 'all') return; // Disable drag/drop when filtered
const targetItem = e.target.closest('li');
if (targetItem && targetItem.dataset.index !== undefined) {
const targetIndex = +targetItem.dataset.index;
if (draggedItemIndex !== null && draggedItemIndex !== targetIndex) {
swapTodos(draggedItemIndex, targetIndex);
}
}
return false;
}
function handleDragEnd(e) {
const items = elements.list.querySelectorAll('.todo-item');
items.forEach(item => item.classList.remove('dragging'));
draggedItemIndex = null;
}
function swapTodos(fromIndex, toIndex) {
if (fromIndex === null || fromIndex === undefined || fromIndex < 0 || fromIndex >= todos.length) return;
if (toIndex < 0 || toIndex >= todos.length) return;
const itemToMove = todos.splice(fromIndex, 1)[0];
todos.splice(toIndex, 0, itemToMove);
saveTodos();
renderTodos();
}
// --- Usage Counter REMOVED for public release ---
// --- Initialization ---
function init() {
elements.addBtn.addEventListener('click', addTodo);
elements.clearBtn.addEventListener('click', clearTodos);
if (elements.clearCompletedBtn) elements.clearCompletedBtn.addEventListener('click', clearCompleted);
elements.themeToggle.addEventListener('click', toggleTheme);
elements.filterBtns.forEach(btn => {
btn.addEventListener('click', () => setFilter(btn.dataset.filter));
});
elements.input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addTodo();
});
initTheme();
renderTodos();
// initCounter(); // Removed
}
init();
})();
より詳しく知りたい方の参考ページ
