[Javascript] HTML5 DOM project

import ‘./assets/css/style.css‘;

const app = document.getElementById(‘app‘);

app.innerHTML = `
  <div class="todos">
    <div class="todos-header">
      <h3 class="todos-title">Todo List</h3>
      <div>
        <p>You have <span class="todos-count"></span> items</p>
        <button type="button" class="todos-clear" style="display: none;">
          Clear Completed
        </button>
      </div>
    </div>
    <form class="todos-form" name="todos">
      <input type="text" placeholder="What‘s next?" name="todo">
    </form>
    <ul class="todos-list"></ul>
  </div>
`;

// state
let todos = JSON.parse(localStorage.getItem(‘todos‘)) || [];

// selectors
const root = document.querySelector(‘.todos‘);
const list = root.querySelector(‘.todos-list‘);
const count = root.querySelector(‘.todos-count‘);
const clear = root.querySelector(‘.todos-clear‘);
const form = document.forms.todos;
const input = form.elements.todo;

// functions
function saveToStorage(todos) {
  localStorage.setItem(‘todos‘, JSON.stringify(todos));
}

function renderTodos(todos) {
  let todoString = ‘‘;
  todos.forEach((todo, index) => {
    todoString += `
      <li data-id="${index}"${todo.complete ? ‘ class="todos-complete"‘ : ‘‘}>
        <input type="checkbox"${todo.complete ? ‘ checked‘ : ‘‘}>
        <span>${todo.label}</span>
        <button type="button"></button>
      </li>
    `;
  });
  list.innerHTML = todoString;
  count.innerText = todos.filter(todo => !todo.complete).length;
  clear.style.display = todos.filter(todo => todo.complete).length
    ? ‘block‘
    : ‘none‘;
}

function addTodo(event) {
  event.preventDefault();
  const label = input.value.trim();
  const complete = false;
  todos = [
    ...todos,
    {
      label,
      complete,
    },
  ];
  renderTodos(todos);
  saveToStorage(todos);
  input.value = ‘‘;
}

function updateTodo(event) {
  // find span‘s parnet node‘s data-id attribute
  const id = parseInt(event.target.parentNode.getAttribute(‘data-id‘), 10);
  const complete = event.target.checked;
  todos = todos.map((todo, index) => {
    if (index === id) {
      return {
        ...todo,
        complete,
      };
    }
    return todo;
  });
  renderTodos(todos);
  saveToStorage(todos);
}

function editTodo(event) {
  // get current node tag name
  if (event.target.nodeName.toLowerCase() !== ‘span‘) {
    return;
  }
  const id = parseInt(event.target.parentNode.getAttribute(‘data-id‘), 10);
  const todoLabel = todos[id].label;

  const input = document.createElement(‘input‘);
  input.type = ‘text‘;
  input.value = todoLabel;

  function handleEdit(event) {
    // because list also has ‘change‘ event, so stop propgation
    event.stopPropagation();
    // this point to input
    const label = this.value;
    if (label !== todoLabel) {
      todos = todos.map((todo, index) => {
        if (index === id) {
          return {
            ...todo,
            label,
          };
        }
        return todo;
      });
      renderTodos(todos);
      saveToStorage(todos);
    }
    // clean up
    event.target.style.display = ‘‘;
    // this point to input element ref
    this.removeEventListener(‘change‘, handleEdit);
    // remove input element
    this.remove();
  }

  event.target.style.display = ‘none‘;
  event.target.parentNode.append(input);
  input.addEventListener(‘change‘, handleEdit);
  // focus has to be called in the last of function.
  // otherwise focus() has no effect
  input.focus();
}

function deleteTodo(event) {
  if (event.target.nodeName.toLowerCase() !== ‘button‘) {
    return;
  }
  const id = parseInt(event.target.parentNode.getAttribute(‘data-id‘), 10);
  // get button‘s sibling element span
  const label = event.target.previousElementSibling.innerText;
  if (window.confirm(`Delete ${label}?`)) {
    todos = todos.filter((todo, index) => index !== id);
    renderTodos(todos);
    saveToStorage(todos);
  }
}

function clearCompleteTodos() {
  const count = todos.filter(todo => todo.complete).length;
  if (count === 0) {
    return;
  }
  if (window.confirm(`Delete ${count} todos?`)) {
    todos = todos.filter(todo => !todo.complete);
    renderTodos(todos);
    saveToStorage(todos);
  }
}

// init
function init() {
  renderTodos(todos);
  // Add Todo
  form.addEventListener(‘submit‘, addTodo);
  // Update Todo
  list.addEventListener(‘change‘, updateTodo);
  // Edit Todo
  list.addEventListener(‘dblclick‘, editTodo);
  // Delete Todo
  list.addEventListener(‘click‘, deleteTodo);
  // Complete All Todos
  clear.addEventListener(‘click‘, clearCompleteTodos);
}

init();

相关推荐