DEV Community

artydev
artydev

Posted on Edited on

TodoList with DML, one of the tiniest implementation 3K gzip

Here is the official demo of TodoList. You can run it live here :
TodoList.

Try it on your phone, and see how fast it is.
Features : multilines fields and localstorage.


<html lang="de">
 <head>
    <meta charset="utf-8">
    <title>title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://efpage.de/DML/DML_homepage/lib/DML-min.js"></script>

 </head>
 <body>
    <script>
let todos = []; // List of todos
const _w = "width: 100%; max-width: 450px;"
const _ba = _top+"min-height: 22px; width: 100%; border: none; resize: none; background-color: rgba(0,0,0,0); font-size: 20px; font-family: sans-serif;"

/*------------------------------------------------------
   count active todos 
  ------------------------------------------------------*/
function countTodos() {
  let _active = 0;
  todos.forEach(function (todo) {if (!todo.cb.checked) _active++;})
  activecount.textContent = "Active: " + _active;
  count.textContent = "Total: " + todos.length
}

/*------------------------------------------------------
   save data
  ------------------------------------------------------*/
function saveData() {
  localStorage.setItem("todocount", todos.length)
  todos.forEach(function (todo, i) {
    localStorage.setItem("todo" + i, todo.ta.value)
    localStorage.setItem("state" + i, todo.cb.checked)
  })
  countTodos()
}

/*------------------------------------------------------
   read old data 
  ------------------------------------------------------*/
function readData(area) {
  let n = localStorage.getItem("todocount");
  for (let i = 0; i < n; i++) {
    let content = localStorage.getItem("todo" + i);
    if (content)
      if (content.trim() != "") {
        let todo = new todoObj(area, content);
        todo.cb.checked = (localStorage.getItem("state" + i) != "false")
        todo.ta.style.height = todo.ta.scrollHeight + "px";
        todo.setState();
      }
  }
  countTodos()
}
/*------------------------------------------------------
  Make new todo Element and clear input 
  get workarea and textarea with content (will be cleared)
  ------------------------------------------------------*/
function mknew(workarea, ta) {
  if (ta.value != "") {
    new todoObj(workarea, ta.value);
    ta.value = '';
    ta.autosize()
  }
  saveData()
  countTodos()
}

/****************************************************************************************
  Class "todo" creates a new todo in area
****************************************************************************************/
class todoObj {
  constructor(area, content) {
    todos.push(this);    // Add Element to todo array

    selectBase(area);    // start inside area
    // Create a todo box
    this.todo = selectBase(div("", "display: flex; position: relative; border-top: solid;  border-width: 1px; "))
    this.cb = checkbox("", "flex: 0 0 30px;align-self: center;");
    this.ta = expandableTextarea(content, _ba); // Create input area

    // Invisible Delete-Button to remove element
    this.bt = button("x", "height: 28px; opacity: 0.0; transition: opacity 0.5s; "
      + " position: absolute; right: 10px; " + _top + _shadow);
    this.bt.onclick = this.delete.bind(this)

    // Set event functions
    this.ta.addEventListener("blur", saveData); // Save data on exit
    this.cb.onclick = () => { this.setState(); saveData(); } // Save data on state change
    this.todo.onmouseenter = () => { this.bt.style.opacity = 1 }
    this.todo.onmouseleave = () => { this.bt.style.opacity = 0 }

    // run once to show state
    this.setState();
    unselectBase(2)
  }
  // Class funcitons:
  /*------------------------------------------------------
     set state color of the input area
    ------------------------------------------------------*/
  setState() {
    this.ta.style.backgroundColor = (this.cb.checked ? "#f0f0e0" : "#ffe");
    this.ta.style.textDecoration = (this.cb.checked ? "line-through" : "none");
  }
  /*------------------------------------------------------
     Delete todo Object
    ------------------------------------------------------*/
  delete() {
    todos = todos.filter(todo => todo != this) // Delete this element from list
    this.todo.remove(); delete (this)
    saveData();
  }
} // end todo class

/****************************************************************************************
  Create page content
****************************************************************************************/

div(h1("todos", "Text-align: center; text-shadow: 2px 2px 4px #444;" + _red), _w);
selectBase(div("", _w + " background-color: #ffe; " + _border + _radius + _bigPadding + _shadow));

// Top Input field
let ta = expandableTextarea("", { "placeholder": "What to do ?", "style":_ba})

// Area for Todos
let workarea = div();     

// footer
hr("margin: 3px auto;")
selectBase(div("", "display: flex;  justify-content: space-between; font-size: 80%;"))
let count = span("count");
let activecount = span("activecount");
unselectBase(2)

/****************************************************************************************
  Events to create new todos
****************************************************************************************/

// Create new Todos on Return or exit
ta.onkeypress = (event) => {
  if (event.which == 13) {
    event.preventDefault();   // Remove CR from input
    mknew(workarea, ta)
  }
}
ta.onblur = () => { mknew(workarea, ta) }

readData(workarea)

/****************************************************************************************
  save Data before unload
****************************************************************************************/
window.onbeforeunload = saveData;
    </script> 
 </body>
</html>

Enter fullscreen mode Exit fullscreen mode

Top comments (0)