Make Delete and Edit Buttons Work

Please help me to finish my To Do App:

Add click event on button “Remove” - So that by click on button “REMOVE” created item would be removed from localstorage as well.
Add click event on button “Edit” - So that created item could be edited in localstorage as well.
Thank you a lot for your time and effort.
See my codepen

I dont know how to do it. Its not an assignment its just my pet project for myself. I started to learn js 2 month ago and its really tough for me. I was thinking that in this forum someone could help me by showing how i can make them buttons work.

I’m sure someone will be able to help you @dimerdelit but you only posted an hour ago.

Everyone on the forums is a volunteer, helping others in their spare time, so please be patient. :slight_smile:

1 Like

I am having a look at this. WIll more than likely be back to it in the morning my time as it is a bit late here.

For your information, you need a unique id for each todo. That way when you click on say ‘delete’ it knows which item to remove from the todoList.

For delete you can use Array.filter to achieve that e.g.

// will filter out the chosen item based on id
const updatedTodos = todoList.filter((todo) => todo.id !== idToDelete);
1 Like

Okay I will try it and will let you know if i made it or not

Just some ideas @dimerdelit,

I was having a play last night, and it seems to me that making your add todo section into a form might be a better way to go.

Here is just a rough

<header>
    <div class='add-todo'>
        <h1>ToDo list</h1>
        <form class='create_new_todo'>
            <input 
                type='text' 
                id='input-task' 
                name='input-task' 
                placeholder='Next task to do' 
                required
            >
            <button type='submit'>Add</button>
        </form>
    </div>
</header>

<main>
    <section class='todo-list-container'>
        <ul class='todo-list'>
            <li id='task-abcd1234'>
                <input
                    type='text' 
                    class='task' 
                    name='task' 
                    value='dummy task 2' 
                    disabled
                >
                <button class='edit'>Edit</button>
                <button class='delete'>Delete</button>
            </li>
            <li id='task-dcbf3251'>
                <input
                    type='text' 
                    class='task' 
                    name='task' 
                    value='dummy task 2' 
                    disabled
                >
                <button class='edit'>Edit</button>
                <button class='delete'>Delete</button>
            </li>
        </ul>
    </section>
</main>

You can see that each task is created as an input that has been set to disabled. I’m thinking clicking on edit would remove that disabled property letting you edit the existing task.

There is also an id on the parent list element, so that you can edit or delete the correct task from storage.

As I say just ideas at this point.

1 Like

Okay, but i am more worried about js :smiley:
I updated my code by the way.
Now I am able to delete element, but not from localstorage and I add random id.
Thats all what i can. Other ways i just dont understand.

The html is key to how you implement the JS. If you get the html side of things right, it will make the javascript that much simpler.

For instance in your code you have

addMessage.value = '';
addName.value = '';

Had those inputs been inside a form element, you could just reset the form.

addTodoForm.reset()

It’s your project though, so just food for thought :slight_smile:

1 Like

yes, I understand and its true.
I just want to finish it in a way I started.
Could you please help me to delete element from localstorage using my randon id number?

1 Like

Do you have an up to date example of your code?

1 Like

Click Here

Hi @dimerdelit, well I have tried to stick roughly to your code.

const addMessage = document.querySelector('.message');
const addName = document.querySelector('.name');
const addButton = document.querySelector('.add');
const todo = document.querySelector('.todo');
const remove = document.querySelector('.remove');

let todoList = [];

function uniqueId() {
  return Math.random().toString(16).slice(2);
}

// a function to update the storage
// and update the displayed list
function updateTodoList (todoList) {
  localStorage.setItem('todo-list', JSON.stringify(todoList));
  displayMessages(todoList);
}

if (localStorage.getItem('todo-list')) {
   todoList = JSON.parse(localStorage.getItem('todo-list'));
   displayMessages(todoList);
}

addButton.addEventListener('click', function () {
  // use or '||' instead of '+'
  if (!addMessage.value || !addName.value) return;

  const newTodo = {
    id: uniqueId(),
    name: addName.value,
    message: addMessage.value,
    checked: false,
    important: false
  };

  addMessage.value = '';
  addName.value = '';

  todoList.push(newTodo);
  updateTodoList(todoList);
});

// onClick="removeMessage('${todo.id}')"
// passes the id to removeMessage
function removeMessage (id) {
  // iterate through the existing todoList
  // and using filter remove the todo that has an id
  // matching the todo we clicked delete on
  todoList = todoList.filter(
    function(todo) {
      // only return the ones that don't match
      return todo.id !== id;
    }
  )
  
  updateTodoList(todoList);
}

function displayMessages(todoList = []) {

  // Iterate through each todo with map.
  // For each todo return the list item HTML
  const messages = todoList.map(
    function(todo) {
      return `
      <li id="${todo.id}">
        Name:${todo.name}
        Message:${todo.message}
        <button class="remove" onClick="removeMessage('${todo.id}')">DELETE</button>
        <button class="edit">EDIT</button>
      </li>
      `;
    }
  );
  
  // join the array items into one big string
  // using a newline as a separator.
  todo.innerHTML = messages.join('\n');
}

Here is a codepen. You will obviously need to work on your css/html.

Functions that I have used, which are worth looking at.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join

Note: Personally I would make further changes to this, but didn’t want to veer too far off from your original code.

Edit: Just made a change to your inline onClick handler. Now passes the id to removeMessage instead. I would personally opt for addEventListener, but this is a simpler solution to passing the button element.

1 Like

omg big thank you! Also thank you for your comments in a code. You made my day! I am now looking and I see much clearer then before.

1 Like

only thing left is Edit button option :smiley:

I tried to make edit function work, but i am an amateur I guess :smiley:
Look here

Hi again @dimerdelit,

I don’t think you were too far off.

I have gone for a slightly different approach. For starters I have changed the todos template to the following format.

<li id="${todo.id}">
  <fieldset disabled>
    <label>Name: 
      <input type="text" name="name" value="${todo.name}">
    </label>
    <label>Message: 
      <input type="text" name="message" value="${todo.message}">
    </label>
  </fieldset>
  <button class="remove" onClick="removeTodo('${todo.id}')">DELETE</button>
  <button class="edit" onClick="editTodo(this)">EDIT</button>
</li>

fieldset are quite handy. We can disable or enable all inputs and they come with an elements array that enables us to select the inputs by name e.g.

const name = myFieldSet.elements.name;
const message = myFieldSet.elements.message;

I have amended the code refactoring it into a few more shorter functions. For instance I have a function that creates the html template. Personally I quite like to have these template functions in their own module.

Anyway here is the code. It is rough around the edges and can be improved on.

// A template function to create the list item HTML
function createTodoHTML (todo) {
  return `
  <li id="${todo.id}">
    <fieldset disabled>
      <label>Name: 
        <input type="text" name="name" value="${todo.name}">
      </label>
      <label>Message: 
        <input type="text" name="message" value="${todo.message}">
      </label>
    </fieldset>
    <button class="remove" onClick="removeTodo('${todo.id}')">DELETE</button>
    <button class="edit" onClick="editTodo(this)">EDIT</button>
  </li>
  `
}

function uniqueId() {
  return Math.random().toString(16).slice(2);
}

// A factory function to create a new todo object
function createTodo (name, message) {
  return {
    id: uniqueId(),
    name: name,
    message: message,
    checked: false,
    important: false
  };
}

// a function to update the storage
// and update the displayed list
function updateTodoList (todoList) {
  localStorage.setItem('todo-list', JSON.stringify(todoList));
  displayTodos(todoList);
}

// as we have editTodo and removeTodo it makes
// sense to have addTodo
function addTodo (name, message) {
  const newTodo = createTodo(name, message);
  
  todoList.push(newTodo);
  updateTodoList(todoList);
}

function removeTodo (id) {
  todoList = todoList.filter(
    function(todo) {
      return todo.id !== id;
    }
  )
  
  updateTodoList(todoList);
}

function editTodo (editButton) {
  const listItem = editButton.parentElement;
  const fieldset = listItem.querySelector('fieldset');
  const editing = editButton.textContent === 'EDIT';
  
  // fieldsets have an elements array that allow
  // you to access the inputs by input name
  const name = fieldset.elements.name;
  const message = fieldset.elements.message;
  
  if (editing) {
    editButton.textContent = 'SAVE';
    fieldset.disabled = false; // now we can edit the inputs
    name.focus(); // move cursor to name
    return; // exit here
  }
  
  // otherwise the button clicked is 'SAVE'
  editButton.textContent = 'EDIT';
  fieldset.disabled = true;
  
  // Iterate through the todos
  todoList = todoList.map(function(todo) {
    // if the todo id matches the list item id then ammend
    if (todo.id === listItem.id) {
      todo.name = name.value;
      todo.message = message.value;
    }
    
    return todo;
  })
  
  updateTodoList(todoList);
}

function displayTodos (todoList = []) {
  const todos = document.querySelector('.todos');
  const messages = todoList.map(function(todo) {
    // return a new list item template for each todo
    return createTodoHTML(todo);
  });

  todos.innerHTML = messages.join('\n');
}

// A generic function to reset fields
// Using the rest operator e.g ...fields 
// to convert all arguments to an array
// e.g. resetFields(name, message) → [name, message]
function resetFields (...fields) {
  fields.forEach(function(field) {
    field.value = "";
  })
}

// Moved addButton here. Saves having to scroll to the top of the code
// to find out what addButton is
const addButton = document.querySelector('.add');

addButton.addEventListener('click', function(event) {
  // event.target is the button, parent is the wrapping div
  const parent = event.target.parentElement;
  // parent can be used as the root to search from
  const message = parent.querySelector('.message');
  const name = parent.querySelector('.name');
  
  if (!name.value || !message.value) return;
  
  addTodo(name.value, message.value);
  resetFields(name, message);
});

let todoList = [];

if (localStorage.getItem('todo-list')) {
   todoList = JSON.parse(localStorage.getItem('todo-list'));
   displayTodos(todoList);
}

I went for fieldsets and inputs, because that seemed more straight forward. They also allowed me to use focus to focus on the name box when editing. If you want to stick with the direction you are going in I am sure you could amend my code to work with editable content instead.

1 Like

Very interesting my friend I am really appreciated for your help, time and effort. I will analyse the code on weekend. fieldset is something new to me, but i will definitely use it from now. Thank you for your really useful comments in a code. Hope to meet you next time I get stuck :smiley: .Big thank you again for your support and take care :wink:

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.