Manipulating a parent tag in JavaScript

Issue

I have a simple note taking web 5 application. Each time the user clicks on the ‘+’ button a new note is added. Once he clicks on the ‘⟳’ button all are excluded. I want to add five new item slots in the note that the user clicked. In order to do that I need to know which note he did so. This last bit is the one confusing me. How can I know which button the user clicked if all of the buttons are generated by the user?

HTML:

<html lang="en">
  <head>
    <title>To Do Lists</title>
    <link rel="stylesheet" href="styles.css" />
    <script src="script.js"></script>
  </head>
  <body>
    <div class="top_bar">
      <button id="plus">+</button>
      <button id="restart">⟳</button>
    </div>
    <div id="notes" class="notes">

    </div>
  </body>
</html>

CSS

    padding: 0px;
    margin: 0px;
}
body{
    display: flex;
    flex-direction: column;
}
.top_bar{
    width: 100%;
    height: 10vh;
    background-color: #95E29B;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
button{
    font-size: 35px;
    border: none;
    width: 15%;
    height: 10vh;
    background-color: #3BCE4B;
    cursor: pointer;
}
.notes{
    width: 100%;
    height: 90vh;
    overflow: auto;
    background-color: black;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
}

.note{
    margin-top: 20px;
    margin-bottom: 20px;
    margin-left: 20px;
    height: 40vh;
    width: 30%;
    border-radius: 10px;
    background-color: white;
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    align-items: flex-end;

}

.note_input{
    margin-top: 20px;
    margin-right: 5%;
    font-size: 30px;
    width: 90%;
    border-style: solid;
    border-top: none;
    border-left: none;
    border-right: none;
    border-color: black;
}

form{
    margin-top: 10px;
    margin-right: 15%;
    width: 80%;
    height: 49%;
    overflow-y: auto;
}

li{
    border: none;
    width: 70%;
    display: flex;
}

.li_input{
    border-style: solid;
    border-color: black;
    border-top: none;
    border-left: none;
    border-right: none;
    margin-left: 10px;
    font-size: 20px;
}

.more_items{
    width: 35px;
    height: 35px;
    margin-right: 2%;
    border-radius: 100%;
    font-size: 20px;
}

JavaScript

const add_note = () => {
  // Creates a new note and its props
  const new_note = document.createElement("div");
  const new_input = document.createElement("input");
  const new_form = document.createElement("form");
  const new_ol = document.createElement("ol");
  const new_button = document.createElement("button");

  //Populates the new note with inputs and checkboxes
  for (let i = 0; i < 5; i++) {
    let new_li = document.createElement("li");
    let new_checkbox = document.createElement("input");
    new_checkbox.setAttribute("type", "checkbox");
    let new_li_input = document.createElement("input");
    new_li_input.classList.add("li_input");
    new_ol.appendChild(new_li);
    new_li.appendChild(new_checkbox);
    new_li.appendChild(new_li_input);
  }

  //New note's settings
  new_note.classList.add("note");
  new_note.appendChild(new_input);
  new_input.classList.add("note_input");
  new_input.setAttribute("placeholder", "Note's title");
  new_note.appendChild(new_form);
  new_ol.classList.add("ols");
  new_form.appendChild(new_ol);
  new_note.appendChild(new_button);
  new_button.classList.add("more_items");

  //Inserts the new note and button
  const note_block = document.getElementById("notes");
  note_block.appendChild(new_note);
  new_button.addEventListener("click", add_more_items);
  new_button.innerHTML = "+";
};

//Adds more items
const add_more_items = () => {
  //console.log(new_button.parentElement.nodeName);
  //let new_ol = document.getElementsByClassName("ols")[];
  for (let i = 0; i < 5; i++) {
    let new_li = document.createElement("li");
    let new_checkbox = document.createElement("input");
    new_checkbox.setAttribute("type", "checkbox");
    let new_li_input = document.createElement("input");
    new_li_input.classList.add("li_input");
    new_ol.appendChild(new_li);
    new_li.appendChild(new_checkbox);
    new_li.appendChild(new_li_input);
  }
};

//Removes all notes
const remove_note = () => {
  let amount_of_notes = document.getElementsByClassName("note").length;
  console.log(amount_of_notes);
  while (amount_of_notes != 0) {
    amount_of_notes--;
    document.getElementsByClassName("note")[amount_of_notes].remove();
  }
  alert("All notes were removed.");
};

// Loads the buttons
const load_buttons = () => {
  document.getElementById("plus").addEventListener("click", add_note);
  document.getElementById("restart").addEventListener("click", remove_note);
};

// Main method
document.addEventListener("DOMContentLoaded", () => {
  load_buttons();
});

Solution

Given your html is fairly simple, you can do this in a rudimentary style by making use of parentNode.

Your current code is erroring because you’re trying to target new_ol to add the fresh <li> elements but it doesn’t exist in scope of the add_more_items function. And even if it did, it would be ambiguous – which <ol> should it refer to?

Instead, you can work out the parent <ol> from the clicked button, like so:

const add_more_items = (e) => {
  const new_ol = e.target.parentNode.querySelector('ol');
  // rest of your code
}

Here’s a full snippet putting all that together. I’ve put it in a codepen as the snippet editor here struggled with some parts of your layout: https://codepen.io/29b6/pen/qBxxXqG

Bear in mind that traversing the DOM like this isn’t ideal. The main problem with this approach is that if your HTML structure changes, you can end up chaining multiple parentNodes together which gets ugly fast. But it should help you understand the concept of selecting an element’s parent like you asked.

Answered By – Omid

Answer Checked By – Dawn Plyler (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.