Search code examples
javascripthtmldomdom-manipulation

How can I can make this JavaScript code cleaner?


I'm new to JS and I did this exercise for the course I'm taking. It's basically done, but I had to repeat the code that creates the "delete" button and I'm not sure how I can make this cleaner.

var button = document.getElementById("add")
var input = document.getElementById("userinput")
var ul = document.querySelector("ul")
var li = document.querySelectorAll("li")

function inputLength() {
    return input.value.length;
}

function createListElement() {
    let li = document.createElement("li")
    li.appendChild(document.createTextNode(input.value))
    ul.appendChild(li)
    input.value = ""

    // Create a delete button and configure it:
    var btnDelete = document.createElement("button");
    btnDelete.classList.add("delete");
    btnDelete.textContent = "Delete";

    // Append the button to the list item
    li.appendChild(btnDelete);
}

// Create a delete button for each <li> already in the HTML file
li.forEach(function(item){
    var btnDelete = document.createElement("button");
    btnDelete.classList.add("delete");
    btnDelete.textContent = "Delete";
    item.appendChild(btnDelete);
 });

function addListAfterClick() {
    if(inputLength() > 0) {
        createListElement()
    }
}

function addListAfterKeypress(event) {
    if(inputLength() > 0 && event.keyCode === 13) {
        createListElement()
    }
}

button.addEventListener("click", addListAfterClick)

input.addEventListener("keypress", addListAfterKeypress)


function toggleClassDoneOnAndOff(event) {
    if (event.target.tagName === "LI") {
        event.target.classList.toggle("done");
    }
}

ul.addEventListener("click", toggleClassDoneOnAndOff);

function deleteAfterClick(event) {
    // Determine if it was a delete button that was clicked
    if(event.target.classList.contains("delete")){
      // Remove the closest li ancestor to the clicked element
      event.target.closest("li").remove();
    }
  
  }

// Handle all the clicks that originate from within the <ul> at
// the <ul> level when they bubble up to it instead of setting each
// button within the <ul> up with its own click event handler.
ul.addEventListener("click", deleteAfterClick);
.done {
    text-decoration: line-through;
}
<body>
    <h1>Shopping List</h1>
    <p id="first">Get it done today</p>
    <input id="userinput" type="text" placeholder="Add items">
    <button id="add">Add</button>
    <ul id="ul">
        <li>Notebook</li>
        <li>Jello</li>
        <li>Spinach</li>
        <li>Rice</li>
        <li>Birthday Cake</li>
        <li>Candles</li>
    </ul>
    <script type="text/javascript" src="script.js"></script>
</body>

My question is that simple, but since Stack Overflow is telling me to "add some more details"...

Basically, I tried to turn this part into a function:

var btnDelete = document.createElement("button");
btnDelete.classList.add("delete");
btnDelete.textContent = "Delete";

But I couldn't figure out how to make it work inside the other ones.


Solution

  • You'll just extract those 3 lines into their own function and have that function return the newly created element. Where the lines are now will get replaced by the function call.

    var button = document.getElementById("add")
    var input = document.getElementById("userinput")
    var ul = document.querySelector("ul")
    var li = document.querySelectorAll("li")
    
    function inputLength() {
        return input.value.length;
    }
    
    function createDelete(){
        // Create a delete button and configure it:
        var btnDelete = document.createElement("button");
        btnDelete.classList.add("delete");
        btnDelete.textContent = "Delete";
        return btnDelete; // <-- send the new element back to the function caller
    }
    
    function createListElement() {
        let li = document.createElement("li")
        li.appendChild(document.createTextNode(input.value))
        ul.appendChild(li)
        input.value = ""
    
        // Append the button to the list item
        // The createDelete() function will be called first
        // and its return value (the new button) is what will 
        // be appended.
        li.appendChild(createDelete());
    }
    
    // Create a delete button for each <li> already in the HTML file
    li.forEach(function(item){
        // The createDelete() function will be called first
        // and its return value (the new button) is what will 
        // be appended.
        item.appendChild(createDelete());
    });
    
    function addListAfterClick() {
        if(inputLength() > 0) {
            createListElement()
        }
    }
    
    function addListAfterKeypress(event) {
        if(inputLength() > 0 && event.keyCode === 13) {
            createListElement()
        }
    }
    
    button.addEventListener("click", addListAfterClick)
    
    input.addEventListener("keypress", addListAfterKeypress)
    
    
    function toggleClassDoneOnAndOff(event) {
        if (event.target.tagName === "LI") {
            event.target.classList.toggle("done");
        }
    }
    
    ul.addEventListener("click", toggleClassDoneOnAndOff);
    
    function deleteAfterClick(event) {
        // Determine if it was a delete button that was clicked
        if(event.target.classList.contains("delete")){
          // Remove the closest li ancestor to the clicked element
          event.target.closest("li").remove();
        }
      
      }
    
    // Handle all the clicks that originate from within the <ul> at
    // the <ul> level when they bubble up to it instead of setting each
    // button within the <ul> up with its own click event handler.
    ul.addEventListener("click", deleteAfterClick);
    .done {
        text-decoration: line-through;
    }
    <body>
        <h1>Shopping List</h1>
        <p id="first">Get it done today</p>
        <input id="userinput" type="text" placeholder="Add items">
        <button id="add">Add</button>
        <ul id="ul">
            <li>Notebook</li>
            <li>Jello</li>
            <li>Spinach</li>
            <li>Rice</li>
            <li>Birthday Cake</li>
            <li>Candles</li>
        </ul>
        <script type="text/javascript" src="script.js"></script>
    </body>