0

I am pretty new to js. I am trying to complete a todo sort of app. I have been able to create, render and delete an array item, but I am having trouble editing.

All these operations are done with the uuidv4 library to generate an id for each array item created.

With an individual id selected for an array item, I am generating dynamic buttons, one for deleting the array item, the other one for editing.

Upon clicking edit, I want to open up a modal that contains the content of the selected array item. After making the changes, the edit button inside the modal should call upon an edit function to update the changes and then rerender the array.

My issue is that I can't open up the modal dialog box when clicking the edit button.

This is the code to create the necessary structure, the code for creating, rendering and deleting is not included as they are working properly.

// Generate the DOM structure for a todo

const generateTodoDOM = function(todo) {
  const todoEl = document.createElement("div");
  const checkbox = document.createElement("input");
  const label = document.createElement("label");
  checkbox.appendChild(label);
  todoEl.setAttribute("id", "myTodos");
  const textEl = document.createElement("p");
  const editButton = document.createElement("button");
  editButton.setAttribute("id", "modal-btn");
  const removeButton = document.createElement("button");
  const createDate = document.createElement("p");
  createDate.textContent = `Created: ${dateCreated}`;
  createDate.style.color = "#956E93";

  // Setup the todo text
  textEl.textContent = todo.text;
  todoEl.appendChild(textEl);

  // Setup the remove button
  removeButton.textContent = "x";
  todoEl.appendChild(removeButton);
  removeButton.addEventListener("click", function() {
    removeTodo(todo.id);
    saveTodos(todos);
    renderTodos(todos, filters);
  });

  // TODO: Setup the edit note button
  editButton.textContent = "Edit Todo";
  todoEl.appendChild(editButton);
  editButton.addEventListener("click", function() {

    //Launch the modal
    editModal(todo.id);
  });
  // Setup todo checkbox
  checkbox.setAttribute("type", "checkbox");
  checkbox.checked = todo.completed;
  todoEl.appendChild(checkbox);
  checkbox.addEventListener("change", function() {
    toggleTodo(todo.id);
    saveTodos(todos);
    renderTodos(todos, filters);
  });

  todoEl.appendChild(createDate);

  return todoEl;
};

The code for the modal is the following:

//Edit modal todo by id
const editModal = function(id) {
  const todoIndex = todos.findIndex(function(todo) {
    return todo.id === id;
  });
  if (todoIndex > -1) {
  const modal = document.querySelector("#my-modal");
  const modalBtn = document.querySelector("#modal-btn");
  const editTodoContentBtn = document.querySelector("#submitEditTodo")
  const closeBtn = document.querySelector(".close");

  // Events
  modalBtn.addEventListener("click", openModal);
  closeBtn.addEventListener("click", closeModal);
  editTodoContentBtn.addEventListener("click", editTodo)
  window.addEventListener("click", outsideClick);

  // Open
  function openModal() {
    modal.style.display = "block";
  }

  // Close
  function closeModal() {
    modal.style.display = "none";
  }

  // Close If Outside Click
  function outsideClick(e) {
    if (e.target == modal) {
      modal.style.display = "none";
    }
  }

  //Edit the content of the textarea
  function editTodo(e) {
    editTodo(id)
  }
  }
};

When clicking the submitEditTodo button the following edit function should be fired:

//Edit todo by id
const editTodo = function(id) {
  const editTodoContent = document.querySelector('#editTodo')
  const todoIndex = todos.findIndex(function(todo) {
    return todo.id === id;
  });

  if (todoIndex > -1) {
    editTodoContent.value = todos.text
    saveTodos(todos)
    renderTodos(todos, filters);
  }
};

The saveTodos and renderTodos are functioning properly with other functions for creating, rendering and deleting.

This is the HTML code:

<!-- Edit modal -->
  <div id="my-modal" class="modal">
    <div class="modal-content">
      <div class="modal-header">
        <span class="close">&times;</span>
        <h2>Edit Todo</h2>
      </div>
      <div class="modal-body">
        <textarea name="" class="editTextArea" id="editTodo"  rows="10"></textarea>
        <button class="button" id="submitEditTodo">Edit Todo</button>
      </div>
      <div class="modal-footer">
        <!-- <h3>Modal Footer</h3> -->
      </div>
    </div>
   <!-- End modal --> 

and this is the CSS for the modal:

/*
Edit todo modal start
*/

:root {
  --modal-duration: 1s;
  --modal-color: #BB8AB8;
}

.modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.5);
}

.modal-content {
  margin: 10% auto;
  width: 35%;
  box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
  animation-name: modalopen;
  animation-duration: var(--modal-duration);
}

.editTextArea{
  width:100%
}

.modal-header h2,
.modal-footer h3 {
  margin: 0;
}

.modal-header {
  background: var(--modal-color);
  padding: 15px;
  color: #fff;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
}

.modal-body {
  padding: 10px 20px;
  background: #fff;
}

.modal-footer {
  background: var(--modal-color);
  padding: 10px;
  color: #fff;
  text-align: center;
  border-bottom-left-radius: 5px;
  border-bottom-right-radius: 5px;
}

.close {
  color: #ccc;
  float: right;
  font-size: 30px;
  color: #fff;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

@keyframes modalopen {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/*
Edit todo modal end
*/

Thanks

2
  • "My issue is that I can't open up the modal dialog box when clicking the edit button." is a little vague for describing your issue. Could you explain what you expect to happen and what actually happens when clicking the edit button? Commented Mar 4, 2019 at 9:53
  • Yes, that's what I am not able to initiate, I need to understand how to open up the modal function editModal from generateTodoDOM function and with the button with css id of modal-btn Commented Mar 4, 2019 at 10:01

1 Answer 1

1

Below are a few pointers to where you might need adjustments to achieve what you want.

  1. Currently you are adding new listeners to the modal every time you click an edit button for a todo. This should probably only be set once. Alternatively you should remove the listeners when the modal is closed.

  2. Your function editModal does not actually open the modal. What it does is add a listener to the #modal-btn button which will then open the modal the next time you click the button.

  3. You are setting id's for both the outer div and the edit button, but the id's are not based on anything related to the todo element you are creating. So effectively all those elements end up with the same id. An id (short for identifier) is usually meant to be unique. For grouping of multiple elements you should instead use the class attribute.

  4. Your function "editTodo" calls itself. Recursing indefinitely. Beware of reusing function names.

With that said the below code is a crude way to do what I think you want to do based on the snippets you provided:

// Open
const openModal = function() {
  document.querySelector("#my-modal").style.display = "block";
}


  // Close
const closeModal = function() {
  document.querySelector("#my-modal").style.display = "none";
}

function initModal() {
  const modal = document.querySelector("#my-modal");
  const closeBtn = document.querySelector(".close");

  // Events
  closeBtn.addEventListener("click", closeModal);
  window.addEventListener("click", outsideClick);


  // Close If Outside Click
  function outsideClick(e) {
    if (e.target == modal) {
      modal.style.display = "none";
    }
  }
}

const filters = []; // dummy variable

// Generate the DOM structure for a todo
var todos = []
function generateTodoDOM(todo) {
  todos.push(todo);
  const todoEl = document.createElement("div");
  const checkbox = document.createElement("input");
  const label = document.createElement("label");
  checkbox.appendChild(label);
  todoEl.setAttribute("id", "my-todos-" + todo.id);
  const textEl = document.createElement("p");
  const editButton = document.createElement("button");
  editButton.setAttribute("id", "modal-btn-" + todo.id);
  const removeButton = document.createElement("button");
  const createDate = document.createElement("p");
  createDate.textContent = 'Created: ' + new Date();
  createDate.style.color = "#956E93";

  // Setup the todo text
  textEl.textContent = todo.text;
  todoEl.appendChild(textEl);

  // Setup the remove button
  removeButton.textContent = "x";
  todoEl.appendChild(removeButton);
  removeButton.addEventListener("click", function() {
    removeTodo(todo.id);
    saveTodos(todos);
    renderTodos(todos, filters);
  });

  // TODO: Setup the edit note button
  editButton.textContent = "Edit Todo";
  todoEl.appendChild(editButton);
  editButton.addEventListener("click", function() {
    //Launch the modal
    editModal(todo.id);
    openModal();
  });
  // Setup todo checkbox
  checkbox.setAttribute("type", "checkbox");
  checkbox.checked = todo.completed;
  todoEl.appendChild(checkbox);
  checkbox.addEventListener("change", function() {
    toggleTodo(todo.id);
    saveTodos(todos);
    renderTodos(todos, filters);
  });

  todoEl.appendChild(createDate);

  return todoEl;
};


var editFn
//Edit modal todo by id
const editModal = function(id) {
  const todoIndex = todos.findIndex(function(todo) {
    return todo.id === id;
  });
  if (todoIndex > -1) {
    const modal = document.querySelector("#my-modal");
    const editElm = document.querySelector("#editTodo");
    const editTodoContentBtn = document.querySelector("#submitEditTodo")

    editElm.value = todos[todoIndex].text;

    // Events
    editTodoContentBtn.removeEventListener("click", editFn)

    //Edit the content of the textarea
    editFn = function(e) {
      editTodo(id)
      closeModal()
    }

    editTodoContentBtn.addEventListener("click", editFn)

  }
};


//Edit todo by id
const editTodo = function(id) {
  const editTodoContent = document.querySelector('#editTodo')
  const todoIndex = todos.findIndex(function(todo) {
    return todo.id === id;
  });

  if (todoIndex > -1) {
    todos[todoIndex].text = editTodoContent.value;
    saveTodos(todos)
    renderTodos(todos, filters);
  }
};

const saveTodos = function(todos) {
  // dummy method, we're keeping it in memory for this example
}

const renderTodos = function(todosToRender) {
  todos = []; // clear current in-memory array
  var todoList = document.getElementById("todo-container");
  while (todoList.firstChild) {
      todoList.removeChild(todoList.firstChild);
  }
  for(var i = 0; i < todosToRender.length; i++) {
    todoList.appendChild(generateTodoDOM(todosToRender[i]));
  }
};


initModal();
const container = document.getElementById("todo-container");
var generatedTodos = [];
for(var i = 0; i < 10; i++) {
  var todo = { text: "Todo " + (i+1), id: "todo-" + i, completed: false};
  generatedTodos.push(todo);
}
renderTodos(generatedTodos);
/*
Edit todo modal start
*/

:root {
  --modal-duration: 1s;
  --modal-color: #BB8AB8;
}

.modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.5);
}

.modal-content {
  margin: 10% auto;
  width: 35%;
  box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
  animation-name: modalopen;
  animation-duration: var(--modal-duration);
}

.editTextArea{
  width:100%
}

.modal-header h2,
.modal-footer h3 {
  margin: 0;
}

.modal-header {
  background: var(--modal-color);
  padding: 15px;
  color: #fff;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
}

.modal-body {
  padding: 10px 20px;
  background: #fff;
}

.modal-footer {
  background: var(--modal-color);
  padding: 10px;
  color: #fff;
  text-align: center;
  border-bottom-left-radius: 5px;
  border-bottom-right-radius: 5px;
}

.close {
  color: #ccc;
  float: right;
  font-size: 30px;
  color: #fff;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

@keyframes modalopen {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/*
Edit todo modal end
*/
<!DOCTYPE html>
<html>
<head>
	<link rel="stylesheet" href="style.css">
</head>
<body>
	<div id="todo-container">

	</div>
	<!-- Edit modal -->
  <div id="my-modal" class="modal">
    <div class="modal-content">
      <div class="modal-header">
        <span class="close">&times;</span>
        <h2>Edit Todo</h2>
      </div>
      <div class="modal-body">
        <textarea name="" class="editTextArea" id="editTodo"  rows="10"></textarea>
        <button class="button" id="submitEditTodo">Edit Todo</button>
      </div>
      <div class="modal-footer">
        <!-- <h3>Modal Footer</h3> -->
      </div>
    </div>
   </div>
   <!-- End modal --> 
</body>
</html>

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, i will immediately look into it, and get back :)
Thanks for your great input, When I went through your code, I immediately understood the wrong thing in my code. I do know the difference use cases for id:s and classes, but didn't understand that in this instance I was making it totally confusing :) Thanks again

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.