junkerstock
 todo-test03 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Mobile Ready TODO List</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f0f2f5;
display: flex;
justify-content: center;
padding: 20px 10px;
margin: 0;
-webkit-tap-highlight-color: transparent;
}

.container {
width: 100%;
max-width: 500px;
background: #fff;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}

h2 {
margin: 0;
color: #333;
font-size: 1.5rem;
}

#edit-toggle-btn {
background-color: #6c757d;
color: white;
border: none;
padding: 6px 12px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: background-color 0.2s;
}
#edit-toggle-btn.active {
background-color: #28a745;
}

.input-group {
display: flex;
margin-bottom: 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
border-radius: 8px;
overflow: hidden;
}

#task-input {
flex-grow: 1;
padding: 12px 15px;
border: 1px solid #ddd;
border-right: none;
outline: none;
font-size: 16px;
border-radius: 8px 0 0 8px;
}

#add-btn {
padding: 0 20px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
font-weight: bold;
font-size: 16px;
border-radius: 0 8px 8px 0;
min-width: 60px;
}

.todo-list {
list-style: none;
padding: 0;
margin: 0;
min-height: 50px;
}

.todo-item {
background-color: #fff;
border-bottom: 1px solid #eee;
padding: 15px 10px;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
touch-action: pan-y;
}

.todo-text {
flex-grow: 1;
font-size: 16px;
padding-right: 10px;
word-break: break-all;
}

/* 編集モード用要素の初期状態(非表示) */
.handle, .delete-btn, .modify-btn {
display: none;
}

/* 編集モード時のスタイル */
.edit-mode .handle {
display: inline-block;
color: #ccc;
margin-right: 10px;
cursor: grab;
font-size: 20px;
}

.btn-group {
display: flex;
gap: 5px; /* ボタン間の隙間 */
}

.edit-mode .modify-btn {
display: block;
background-color: #17a2b8; /* 水色 */
color: white;
border: none;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
flex-shrink: 0;
}

.edit-mode .delete-btn {
display: block;
background-color: #ff4d4d; /* 赤色 */
color: white;
border: none;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
flex-shrink: 0;
}

.sortable-ghost { opacity: 0.4; background-color: #e3f2fd; }
.sortable-drag { background: #fff; box-shadow: 0 5px 15px rgba(0,0,0,0.15); opacity: 1; }
</style>
</head>
<body>

<div class="container" id="main-container">
<div class="header">
<h2>My Tasks</h2>
<button id="edit-toggle-btn">編集</button>
</div>

<div class="input-group">
<input type="text" id="task-input" placeholder="タスクを入力..." autocomplete="off">
<button id="add-btn">追加</button>
</div>
<ul id="todo-list" class="todo-list"></ul>
</div>

<script>
const input = document.getElementById('task-input');
const addBtn = document.getElementById('add-btn');
const list = document.getElementById('todo-list');
const editToggleBtn = document.getElementById('edit-toggle-btn');
const container = document.getElementById('main-container');

// ★ データの保存(LocalStorage)
function saveTodos() {
const todos = [];
// DOMから現在のリスト順序通りにテキストを取得
document.querySelectorAll('.todo-item .todo-text').forEach(item => {
todos.push(item.textContent);
});
localStorage.setItem('myTodoList', JSON.stringify(todos));
}

// ★ データの読み込み
function loadTodos() {
const saved = localStorage.getItem('myTodoList');
if (saved) {
const todos = JSON.parse(saved);
todos.forEach(text => addTodo(text, false)); // 読み込み時は保存処理をスキップ
} else {
// 初回起動時のサンプル
addTodo('「編集」ボタンを押すと操作メニューが出ます', false);
addTodo('並び替えや修正、削除が可能です', false);
saveTodos();
}
}

// SortableJSの初期化
let sortable = new Sortable(list, {
animation: 150,
ghostClass: 'sortable-ghost',
dragClass: 'sortable-drag',
handle: '.handle',
delay: 0,
disabled: true,
// ★ 並び替えが終わったら保存
onEnd: function() {
saveTodos();
}
});

// 編集モード切り替え
editToggleBtn.addEventListener('click', () => {
const isEditMode = container.classList.toggle('edit-mode');

if (isEditMode) {
editToggleBtn.textContent = '完了';
editToggleBtn.classList.add('active');
sortable.option('disabled', false);
} else {
editToggleBtn.textContent = '編集';
editToggleBtn.classList.remove('active');
sortable.option('disabled', true);
}
});

// 追加ボタン
addBtn.addEventListener('click', () => {
const text = input.value.trim();
if (text) {
addTodo(text);
input.value = '';
input.focus();
}
});

input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addBtn.click();
});

// saveフラグ: ロード時はfalseにして重複保存を防ぐ
function addTodo(text, save = true) {
const li = document.createElement('li');
li.classList.add('todo-item');
li.innerHTML = `
<span class="handle">≡</span>
<span class="todo-text">${text}</span>
<div class="btn-group">
<button class="modify-btn">修正</button>
<button class="delete-btn">削除</button>
</div>
`;

const textSpan = li.querySelector('.todo-text');

// ★ 修正ボタンの処理
li.querySelector('.modify-btn').addEventListener('click', (e) => {
// プロンプトで新しい名前を入力させる
const currentText = textSpan.textContent;
const newText = prompt("タスク内容を修正:", currentText);

if (newText !== null && newText.trim() !== "") {
textSpan.textContent = newText.trim();
saveTodos(); // 保存
}
});

// 削除ボタンの処理
li.querySelector('.delete-btn').addEventListener('click', (e) => {
e.stopPropagation();
if(confirm('削除しますか?')) {
li.style.transform = 'translateX(100%)';
li.style.transition = '0.3s';
setTimeout(() => {
li.remove();
saveTodos(); // ★ 削除後に保存
}, 300);
}
});

list.appendChild(li);

if (save) {
li.scrollIntoView({ behavior: 'smooth', block: 'end' });
saveTodos(); // ★ 追加時に保存
}
}

// ページ読み込み時にデータを復元
loadTodos();

</script>

</body>
</html>


使用変数

) { saveTodos -------( Function )
addBtn
addTodo -------( Function )
autocomplete
charset
class
container
content
currentText
editToggleBtn
id
innerHTML
input
isEditMode
item
key
lang
li
list
loadTodos -------( Function )
name
newText
placeholder
save
saved
saveTodos -------( Function )
scalable
scale
sortable
src
text
textContent
textSpan
todos
transform
transition
type
value
width