todolist

94520 ワード

データ受信とレンダリング


創作のきっかけ
DOMと処理活動の方法を熟知するために、私は何度もコードを書いて、次回復習するために、整理したほうがいいです.また整理したほうがいいです.

DOMContentLoaded


DOM完全生成時にサーバからtodosデータが受信されたとする.
// State =======================================
let todos = [];

// State Function ==============================
const fetchTodos = () => {
  todos = [
    { id: 1, content: 'Javascript', completed: false },
    { id: 2, content: 'CSS', completed: true },
    { id: 3, content: 'HTML', completed: false }
  ];
};

window.addEventListener('DOMContentLoaded', fetchTodos);

レンダー(Render)


データを受信すると、ulに基本的なlistを表示させるためにrender関数を作成します.
HTML
section .todo-app
	header .header
	section .main
		input type="checkbox" id="toggle-all" class="toggle-all"
		label for="toggle-all"
		ul .todo-list
			<!-- 이곳에 list -->
		footer .footer
	footer .info
Javascript
// state =======================================
let todos = [];

// DOM Nodes ===================================
const $todoList = document.querySelector('.todo-list');

// State Function ==============================

const render = () => {
  $todoList.innerHTML = todos
    .map(
      ({ id, content, completed }) => 
    `<li data-id="${id}">
      <div class="view">
        <input type="checkbox" class="toggle" ${completed ? 'checked' : ''}/>
        <label>${content}</label>
        <button class="destroy"></button>
      </div>
      <input class="edit" value="${content}" />
    </li>`).join('');
};

const fetchTodos = () => {
  todos = [
    { id: 1, content: 'Javascript', completed: false },
    { id: 2, content: 'CSS', completed: true },
    { id: 3, content: 'HTML', completed: false }
  ];
  render();
};

window.addEventListener('DOMContentLoaded', fetchTodos);

Main


すべてを切り替え


すべての切り替えボタンを押すと、todosオブジェクトのcompleteをすべてtrueまたはfalseに設定します.
labelを押しますが、実際にはinputウィンドウで選択されています.このinputウィンドウで、ckecked propertyを使用してtrue、falseを検索し、todosの完了キーをすべてtrueに設定します.

先に実現してから配布します.
// DOM Nodes ===================================
const $toggleAll = document.querySelector('.toggle-all');
...

// State Function
...

// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$toggleAll.onchange = () => {
  todos = todos.map(todo => ({ ...todo, completed: $toggleAll.checked }));
  render();
};
State Function部分とEvent Binding部分に分かれています.
// DOM Nodes ===================================
const $toggleAll = document.querySelector('.toggle-all');
...

// State Function
...
const toggleAllTodosCompleted(completed => {
  // todos = todos.map(todo => ({...todo, completed: completed}));
  todos = todos.map(todo => ({...todo, completed}));
  render();
})

// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$toggleAll.onchange = () => {
  toggleAllTodosCompleted($toggleAll.checked);
};
しかし、todosに割り当てられた部分とrender関数を呼び出す部分を繰り返します.もう一つの関数を計算しましょう.それから整理するとずっときれいになります.
// State =======================================
let todos = [];

// DOM Nodes ===================================
const $toggleAll = document.querySelector('.toggle-all');
const $todoList = document.querySelector('.todo-list');

// State Function ==============================
const render = () => {
  ...
};

const setTodos = newTodo => {
  todos = newTodo;
  render();
};

const fetchTodos = () => {
  setTodos([
    { id: 1, content: 'Javascript', completed: false },
    { id: 2, content: 'CSS', completed: true },
    { id: 3, content: 'HTML', completed: false }
  ]);
};

const toggleAllTodosCompleted = completed => {
  setTodos(todos.map(todo => ({ ...todo, completed })));
};

// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$toggleAll.onchange = () => {
  toggleAllTodosCompleted($toggleAll.checked);
};

todoプロジェクトの追加


inputに追加された値をvaluepropertyとして受信しtodosに追加します.
// State =======================================
let todos = [];

// DOM Nodes ===================================
const $newTodo = document.querySelector('.new-todo');
...

// State Function ==============================
const render = () => {
  ...
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => Math.max(...todos.map(todo => todo.id)) + 1

const addTodos = content => {
  setTodos([{id: generateTodoId(), content, completed: false}, ...todos])
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
  if (e.key !== 'Enter') return;
  addTodos(e.target.value)
}  

$toggleAll.onchange = () => {
  ...
};
これにより、コンテンツが空の場合、todoが空白で追加されるという問題が発生し、todoを追加した後もinputウィンドウにvalueが保持されるという問題が発生します.修正してあげます.
$newTodo.onkeyup = e => {
  if (e.key !== 'Enter') return;
  const content = e.target.value;
  if(content) addTodos(content)

  e.target.value = '';
}  

カタログに何もないとき


hiddenクラスが追加されると、display: noneが処理されます.todoリスト数が0の場合、0ではなくhiddenクラスを追加する場合は、hiddenクラスを削除する方法で切り替えを使用します.
上を優先するのは良いのですが、あまりにもひどいので、このタイミングで仕事をします.
// State =======================================
let todos = [];

// DOM Nodes ===================================
...
const $main = document.querySelector('.main');
const $footer = document.querySelector('.footer');

// State Function ==============================
const render = () => {
  ...
  [$main, $footer].forEach($el => $el.classList.toggle('hidden', todos.length === 0));
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};

項目ごとに切り替える

// State =======================================
let todos = [];

// DOM Nodes ===================================
...

// State Function ==============================
const render = () => {
  ...
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

const toggleTodosCompleted = id => {
  setTodos(todos.map(todo => todo.id === +id ? {...todo, completed: !todo.completed} : todo));
}
  
// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};
  
$todoList.onchange = e => {
  if(!e.target.classList.contains('.toggle')) return;
  toggleTodosCompleted(e.target.closest('li').dataset.id);
}

アイテムの変更


試合はまだ慣れていないので,注意して見なければならない.
2つの機能を追加します.編集に入ったときとEnterを押したときに入力した値をダブルクリックして再レンダリングするようにしてください.
// State =======================================
let todos = [];

// DOM Nodes ===================================
...

// State Function ==============================
const render = () => {
  ...
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

const toggleTodosCompleted = id => {
  ...
}

const updateTodoContent = (id, content) => {
  setTodos(todo.map(todo => todo.id === +id ? {...todo, content} : todo));
}
// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};
  
$todoList.onchange = e => {
  ...
}
  
$todoList.dblclick = e => {
  if(!e.target.matches('.view > label')) return;
  e.target.closest('li').classList.add('.editing');
}

$todoList.onkeyup = e => {
  if(e.key !== 'Enter') return;
  updateTodoContent(e.target.closest('li').dataset.id, e.target.value)
}
e.target.valueではliveオブジェクトと非liveオブジェクトの違いが見られます.
ダブルクリックすると、Javascriptから123123にデータが移動します.修正後Enterをクリックし、下のコンソールを表示します.
$todoList.onkeyup = e => {
  if(e.key !== 'Enter') return;
  updateTodoContent(e.target.closest('li').dataset.id, e.target.value)
  console.log(e.target.value); // 123123
  console.log(e.target.getAttribute('value')); // Javascript
}

アイテムの削除

// State =======================================
let todos = [];

// DOM Nodes ===================================
...

// State Function ==============================
const render = () => {
  ...
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

const toggleTodosCompleted = id => {
  ...
}

const updateTodoContent = (id, content) => {
  ...
}
  
const removeTodo = id => {
  setTodos(todos.filter(todo => todo.id !== +id))
}
// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};
  
$todoList.onchange = e => {
  ...
}
  
$todoList.dblclick = e => {
  ...
}

$todoList.onkeyup = e => {
  ...
}
  
$todoList.onclick = e => {
  if(!e.target.classList.contains('destroy')) return;
  removeTodo(e.target.closest('li').dataset.id);
}

Footer


左下の未完了todoカウント
// State =======================================
let todos = [];

// DOM Nodes ===================================
...
const $todoCount = document.querySelctor('.todo-count')

// State Function ==============================
const render = () => {
  ...
  
  const activeTodos = todos.filter(todo => !todo.completed).length;
  $todoCount.textContent = `${activeTodos} ${activeTodos > 1? 'items' : 'item'} left`
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

const toggleTodosCompleted = id => {
  ...
}

const updateTodoContent = (id, content) => {
  ...
}
  
const removeTodo = id => {
  ...
}
// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};
  
$todoList.onchange = e => {
  ...
}
  
$todoList.dblclick = e => {
  ...
}

$todoList.onkeyup = e => {
  ...
}
  
$todoList.onclick = e => {
  ...
}

ALL、Active、Completedに分けて表示

// State =======================================
let todos = [];
let currentFilter = 'all';

// DOM Nodes ===================================
...
const $filters = document.querySelector('.filters');

// State Function ==============================
const render = () => {
  
  const _todos = todos.filter(todo => 
    currentFilter === 'completed'
    ? todo.completed
    : currentFilter === 'active'
    ? !todo.completed
    : true
    );
  
  todoList.innerHTML = _todos
    .map(
      ({ id, content, completed }) =>
        `<li data-id="${id}">
            <div class="view">
              <input type="checkbox" class="toggle" ${completed ? 'checked' : ''}/>
              <label>${content}</label>
              <button class="destroy"></button>
            </div>
            <input class="edit" value="${content}" />
          </li>`).join('');
  
  [$main, $footer].forEach($el => $el.classList.toggle('hidden', todos.length === 0));
  
  const activeTodos = _todos.filter(todo => !todo.completed).length;
  $todoCount.textContent = `${activeTodos} ${activeTodos > 1? 'items' : 'item'} left`
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const setFilter = newFilter => {
  currentFilter = newFilter;
  render();
}

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

const toggleTodosCompleted = id => {
  ...
}

const updateTodoContent = (id, content) => {
  ...
}
  
const removeTodo = id => {
  ...
}
// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};
  
$todoList.onchange = e => {
  ...
}
  
$todoList.dblclick = e => {
  ...
}

$todoList.onkeyup = e => {
  ...
}
  
$todoList.onclick = e => {
  ...
}
  
$filters.onclick = e => {
  if(!e.target.matches('filters')) return;
  [...filters.querySelectorAll('a')].forEach($a => $a.classList.toggle('selected', $a.id === e.target.id))
  setFilter(e.target.id);  
}

完了したtodoの削除

// State =======================================
let todos = [];
let currentFilter = 'all';

// DOM Nodes ===================================
...
const $clearCompleted = document.querySelector('.clear-completed');

// State Function ==============================
const render = () => {
  ...
};

const setTodos = newTodo => {
  ...
};

const fetchTodos = () => {
  ...
};

const generateTodoId = () => ...

const setFilter = newFilter => {
  ...
}

const addTodos = content => {
  ...
}
  
const toggleAllTodosCompleted = completed => {
  ...
};

const toggleTodosCompleted = id => {
  ...
}

const updateTodoContent = (id, content) => {
  ...
}
  
const removeTodo = id => {
  ...
}
  
const removeAllCompletedTodos = () => {
  setTodos(todos.filter(todo => !todo.completed))
}
// Event Binding ==============================
window.addEventListener('DOMContentLoaded', fetchTodos);

$newTodo.onkeyup = e => {
	...
}  

$toggleAll.onchange = () => {
  ...
};
  
$todoList.onchange = e => {
  ...
}
  
$todoList.dblclick = e => {
  ...
}

$todoList.onkeyup = e => {
  ...
}
  
$todoList.onclick = e => {
  ...
}
  
$filters.onclick = e => {
  ...
}
  
$clearCompleted.onclick = removeAllCompletedTodos
総括中の体得
html上で順番に仕事をすると、JSコードに順番に注意し、再配置するなど、面倒な仕事を減らすことができます.
単一のファイルではなくモジュールを使用することの重要性を理解したようです.
作業中、スクロールを最後まで移動すると、毒性も悪く、不便です.
それでも、何度も勉強して、たくさんの知識を得ました.単純に見たときに理解している部分を実際に見ると、全くそうではないことがわかります.何度も手で打ったので、手で覚えている部分もあるので、1~2週間かけてもう一度打って、それを完全に私のものにすることが大切です.