CHROMATIC-SYSTEM v1.03_NESTED_TODO_LIST_DISPLAY_ENGINE

INFORMATION & CODE

Nested Todo List

What This code Does

The code creates a nested to-do list web application using HTML for the structure, CSS for the appearance, and JavaScript for the functionality. A user can create top-level categories, add individual nested to-do items inside a selected category, mark categories or nested items as completed, delete them, and have the data automatically saved in the browser using localStorage. Because the data is stored locally, the list remains available after refreshing or reopening the page in the same browser.

HTML

The HTML creates the basic structure of the application. The document starts with <!doctype html>, which tells the browser that the page uses modern HTML5. The <html lang="en/US"> element contains the entire page, while the <head> contains information and resources needed by the browser. The character encoding is set to UTF-8, and the viewport meta tag makes the page responsive on mobile devices. The page title is set to **"Nested Todo List"**.</head>

The page loads an external CSS file with <link rel="stylesheet" href="styles.css">. It also loads two Google Fonts, although the CSS shown does not currently apply either of these fonts. Font Awesome is loaded as well, presumably for icons, but the JavaScript and CSS provided do not actually use any Font Awesome icons.

The <body> contains a main <div> with the .todo-wrapper class element. This wrapper limits the width of the application and keeps it centered. Inside it is the header, identified by id="myDIV" and the .header class element. The header contains the application's title, a text input, and two buttons. The input with id="myInput" is where the user types a category or nested todo. The Category button calls the JavaScript function that creates a new category, while the Nested button creates a nested todo inside the currently selected category.

The <ol id="myOL"></ol> element is initially empty. JavaScript dynamically inserts the categories and nested items into this ordered list. The fact that it is an <ol> means the browser can provide ordered-list numbering, while CSS changes the numbering style for categories and nested items.

Finally, <script src="scripts.js"></script> loads the JavaScript after the HTML elements have been created. This is important because the JavaScript immediately looks for elements such as myOL, myInput, addBtn, and nestedBtn". Loading the script at the bottom of the <body>` means those elements already exist when the script runs.

CSS

The first rule uses box-sizing: border-box for every element. This makes width and height calculations easier because an element's padding and border are included within its declared dimensions rather than being added on top.

The body rule provides 50 pixels of space around the application and removes the browser's default margin. min-width: 250px prevents the page from becoming extremely narrow, while Arial is used as the default font. The .todo-wrapper then gives the application a maximum width of 700 pixels and uses margin: auto to center it horizontally.

The default margins and padding of ordered lists are removed with the ol rule. The .header rule creates the blue header area containing the title, input, and buttons. It uses white text, padding, centered text, and a blue border. The .header:after rule is a traditional clearfix technique. It ensures that the header contains its floated input and buttons correctly.

The input occupies 60% of the available width. The two buttons each occupy 20%, so together the input and buttons fill the available 100% width. They are floated to the left so they appear next to one another. The Nested button is orange, while the Category button is green. Their opacity changes when the mouse moves over them, creating a hover effect.

The selector #myOL > li targets only the direct children of the main ordered list. These are the categories. They are given a light background, padding, a pointer cursor, and list-style-type: upper-alpha. Consequently, categories are displayed using letters such as A, B, C rather than numbers.

The nested items are styled separately by #myOL ol li. These are the <li> elements inside a category's nested <ol>. They use decimal numbering, so nested items appear as numbered items. For example, a category could contain items named GROCERIES, HABERDASHERY, HARDWARE, etc.

The :nth-child(odd) rules create alternating background colors, commonly called zebra striping. This makes multiple categories or nested items easier to distinguish visually. The :hover rules change the background when the mouse is placed over an item.

The .selected class visually identifies the category that is currently selected. JavaScript adds this class to the selected category, and CSS gives it an orange outline. The selected category is important because pressing the Nested button adds the new todo to that category.

The .checked class represents a completed item. It changes the background to dark gray and applies text-decoration: line-through, causing the item's text to appear crossed out. The !important ensures that this background takes precedence over the normal alternating background colors.

The check mark itself is created entirely with CSS through .checked::after. Rather than inserting a checkmark character into the HTML, CSS creates a small element with borders and rotates it 45 degrees. This produces the familiar tick/check symbol.

The .close class styles the delete buttons. JavaScript creates these buttons dynamically using a <span> containing the × character. The button is positioned on the right side of its item. When the user hovers over it, its background becomes red and its text becomes white.

The .empty-message class is used when there are no categories. JavaScript creates a message saying "No categories yet.", and this CSS centers and styles that message.

Finally, the media query beginning with @media (max-width: 500px) adjusts the application for smaller screens. It reduces the body's padding and header padding and makes the buttons use a smaller font. This allows the interface to fit more comfortably on mobile devices.

JavaScript data structure

The JavaScript is responsible for practically all of the application's behavior. It begins with:

```javascript
            
const STORAGE_KEY = "nestedTodoList";

let todos = [];
let selectedCategoryId = null;
```
          

STORAGE_KEY is the name used when saving the application to localStorage. todos is an array containing all of the categories, while selectedCategoryId stores the ID of the currently selected category.

The data has a nested structure. Conceptually, it looks like this:


        ```text
  
        todos
         ├── Category A
         │    ├── Nested todo 1
         │    ├── Nested todo 2
         │    └── Nested todo 3
         │
         ├── Category B
         │    ├── Nested todo 1
         │    └── Nested todo 2
         │
         └── Category C
  
        ```

          

A category is represented by an object such as:


```javascript
{
  id: "unique-id",
  text: "Work",
  checked: false,
  children: []
}
  
```

          

The children array contains the nested todo objects. A nested todo does not need another children array because this application supports only two levels: category → nested todo.

Creating IDs

The createId() function generates an identifier for each category and nested todo:


```javascript
  
function createId() {
  return Date.now().toString(36) +
         Math.random().toString(36).substring(2);
}
  
```

          

It combines the current timestamp with a random value. The values are converted to base-36 strings, which produces relatively short identifiers containing numbers and letters. These IDs allow the program to distinguish between different categories and todos, even when they have the same text.

Saving and loading data

The loadTodos() function retrieves the saved list from localStorage. localStorage stores information inside the user's browser, meaning this application does not require a database or server.

The code first executes:


```javascript
  
const savedTodos = localStorage.getItem(STORAGE_KEY);
  
```
  
          

If there is nothing saved, the todos array remains empty. If data exists, JSON.parse() converts the stored JSON string back into a JavaScript array.

The try...catch block is important because corrupted or invalid stored data could otherwise cause the application to stop working. If parsing fails, the application prints an error to the browser console and resets the list.

The opposite process happens in saveTodos(). JSON.stringify(todos) converts the JavaScript array into a JSON string, which can then be stored using localStorage.setItem().

Therefore, the basic data flow is:

 
          ```text
  
          JavaScript objects
                 ↓
          JSON.stringify()
                 ↓
          localStorage
                 ↓
          JSON.parse()
                 ↓
          JavaScript objects
  
          ```

          

Creating a category

The newElement() function creates a new top-level category. It first obtains the input element and removes unnecessary whitespace using .trim().

If the input is empty, the function displays an alert and places the cursor back into the input field. Otherwise, it creates a category object containing a unique ID, the entered text, a checked state of false, and an empty children array.

The category is then added to todos using:


```javascript
  
todos.push(category);
  
```

          

The newly created category immediately becomes the selected category by assigning its ID to selectedCategoryId. This is convenient because the user can immediately type a nested todo and add it to the newly created category.

The input is cleared, the data is saved, and renderTodos() redraws the visible list.

Creating a nested todo

The newNestedElement() function works similarly, but instead of adding a new object to todos, it finds the currently selected category.

This line performs the search:


```javascript
  
const category = todos.find(
  todo => todo.id === selectedCategoryId
);
  
```

          

If no category has been selected, the user receives the message "Please select a category first."

If a category is found, the function creates a nested todo object and adds it to the category's children array:


```javascript
  
category.children.push(nestedTodo);
  
```

          

The data is then saved and the interface is rendered again.

This is the fundamental mechanism that makes the application nested: the nested todo is stored inside the children array belonging to a particular category.

Rendering the list

One of the most important functions is renderTodos(). Rather than modifying individual pieces of the page whenever something changes, the application takes a simple approach: it clears the displayed list and builds it again from the current todos data.

The first step is:


```javascript
  
list.innerHTML = "";
  
```

          

This removes the existing displayed categories.

If there are no categories, the function creates a <div> containing "No categories yet." and stops.

Otherwise, todos.forEach() loops through every category. For each category, JavaScript creates an `<li>` element and stores the category ID in a data-id attribute. The data-id is useful later when the user clicks the category because the program can determine exactly which object the HTML element represents.

The category's text is placed inside a <span>. A second `<span> containing × is created as its delete button.

If the category has nested items, the function creates an <ol> and loops through category.children. Each child becomes another <li>, with its own text and delete button. The nested <ol> is then placed inside its parent category <li>.

The final structure generated by JavaScript is therefore roughly equivalent to:


```html
  
<ol id="myOL">
  <li class="category" data-id="...">
    <span>Work</span>
    <span class="close">×</span>

    <ol>
      <li class="nested-item" data-id="...">
        <span>Finish report</span>
        <span class="close">×</span>
      </li>
    </ol>
  </li>
</ol>
  
```

          

The important point is that the HTML initially contains almost none of the todo-list content. JavaScript generates that content based on the data stored in todos.

Finding items

The findCategory() function searches the top-level todos array for a category with a particular ID.

The findNestedTodo() function is slightly more complicated because nested todos are inside individual categories. It loops through every category and searches that category's children array. When it finds the requested item, it returns both the parent category and the nested todo.

This is useful when deleting or checking a nested todo because the program needs to know not only which nested todo was found, but also which category contains it.

Handling clicks

The application uses event delegation rather than adding a separate click event listener to every category and every nested item.

The listener is attached to the main <ol>:


```javascript
  
document.getElementById("myOL").addEventListener("click", ...)
  
```

          

When anything inside that list is clicked, the event bubbles up to myOL. The program examines event.target to determine what was clicked.

This approach is particularly useful because the categories and todos are dynamically generated. They may not exist when the JavaScript first starts, so attaching listeners directly to them would be less convenient.

When the user clicks a .close element, the program finds its surrounding `<li>`. If it is a category, that category is removed from the todos array using filter(). If the deleted category was selected, the program selects the last remaining category, or sets selectedCategoryId to null if there are no categories left.

If the close button belongs to a nested todo, findNestedTodo() locates the item and filter() removes it from its parent's children array.

After deletion, the program saves the new data and renders the list again.

Checking and selecting items

Clicking a nested todo toggles its checked property:


```javascript
  
result.todo.checked =
  !result.todo.checked;
  
```

          

If it was false, it becomes true; if it was true, it becomes false. The CSS then displays the item with a gray background, crossed-out text, and checkmark.

Categories have slightly different behavior because they also serve as the mechanism for selecting where new nested todos will be added.

If the user clicks a category that is not currently selected, the program simply changes selectedCategoryId to that category's ID and renders the list. The orange outline then moves to the newly selected category.

If the user clicks the category that is already selected, the program interprets this as a request to mark the category as complete or incomplete. Its checked property is toggled.

Therefore, a category effectively has two possible interactions depending on its current state: clicking another category selects it, while clicking the already selected category toggles its completion state.

Keyboard support

The input field has a keydown event listener. When the user presses Enter, the default behavior is prevented.

A normal Enter key calls:


```javascript
  
newElement();
  
```

          

so it creates a category.

Shift + Enter calls:


```javascript
  
newNestedElement();
  
```

          

so it creates a nested todo in the selected category.

This means users don't necessarily have to click the buttons. They can type directly into the input and use the keyboard to add items.

Starting the application

At the very bottom, the application initializes itself:


```javascript
  
loadTodos();

if (todos.length > 0) {
  selectedCategoryId = todos[0].id;
}

renderTodos();
  
```

          

First, previously saved data is loaded from localStorage. If categories exist, the first category is selected automatically. Finally, renderTodos() creates the visible interface based on that data.

This initialization is what makes the application persistent between page loads. For example, if you create three categories, close the browser, and later open the page again, loadTodos() retrieves the saved information and renderTodos() reconstructs the list.

How to use the application

To use the application, place the three pieces into a project folder. The HTML should normally be saved as index.html, the CSS as styles.css, and the JavaScript as scripts.js. The HTML expects the CSS and JavaScript files to be in the same directory.

Open index.html in a browser. Type a category such as "Work" into the input and click Category. The new category appears in the list and becomes selected.

To add a nested todo, type something such as "Finish report" and click Nested. The item will appear inside the selected category. You can add additional nested todos in the same way.

To select a different category, click it once. Its orange outline will move to that category. You can then add nested todos to that category. Clicking the already selected category toggles its completed state.

You can click the × on any category or nested todo to delete it. Completed items are displayed with a line through their text. The application automatically saves additions, deletions, and completion changes to localStorage, so refreshing the page does not normally erase the list.

You can also use the keyboard: Enter adds a category, while Shift + Enter adds a nested todo to the selected category.

Overall operation

The application can be understood as four layers working together:

  • HTML provides the starting interface. It creates the input, buttons, header, and empty list container.
  • CSS controls presentation. It determines the colors, spacing, numbering, selected-category outline, completed-item appearance, delete buttons, hover effects, and mobile layout.
  • JavaScript manages the application state. The todos array represents the actual information, while functions such as newElement(), newNestedElement(), and the click handlers modify that information.
  • localStorage provides persistence. Every important change is converted into JSON and saved in the browser. When the page loads, that JSON is converted back into JavaScript objects.

The most important design principle is that todos is the source of truth. The visible HTML list is essentially a representation of that JavaScript data. Whenever something changes, the program updates todos, saves it, and calls renderTodos() to rebuild the displayed list. This makes the application relatively straightforward because the stored data and the displayed interface stay synchronized.

HTML

CSS

JAVASCRIPT

PREVIEW

Nested ToDo List