0

I've made a budgeting app that has expenses and Income tabs. Every time you add an expense or income, the app pushes the information inside of an array of objects and dynamically renders an <li> component and places it inside of a <ul>. I'm having trouble with the edit and delete features. Each individual <li> comes with a delete and edit button. The <li>, delete button, and edit button all have the same id of Date.now(). Date.now() is used because it produces a number based on milliseconds and won't produce the same id twice unless someone types an expense or income twice within one millisecond I want to click on the delete button inside of the <li> and have my app remove that individual object from my entry_list[] array and also remove the <li> from the DOM.

'use strict'


const balanceElement = document.querySelector(".balance .value");
const totalIncome = document.querySelector(".income-total");
const totalOutcome = document.querySelector(".outcome-total");

const incomeElement = document.querySelector(".income-tab");
const expense = document.querySelector(".expense-tab");
const all = document.querySelector(".all-tab");

const incomeList = document.querySelector(".income-tab .list");
const expenseList = document.querySelector(".expense-tab .list");
const allList = document.querySelector(".all-tab .list");

const expensesButton = document.querySelector(".tab1");
const incomeButton = document.querySelector(".tab2");
const allButton = document.querySelector(".tab3");

const addExpense = document.querySelector(".add-expense")
const expenseTitle = document.querySelector(".expense-title-input")
const expenseAmount = document.querySelector(".expense-amount-input")

const addIncome = document.querySelector(".add-income")
const incomeTitle = document.querySelector(".income-title-input")
const incomeAmount = document.querySelector(".income-amount-input")

const list = document.querySelector('.list')








//SWITCHING BETWEEN TABS
expensesButton.addEventListener('click', () => {
    expense.classList.remove('hidden');
    incomeElement.classList.add('hidden');
    expensesButton.classList.add('clicked');
    incomeButton.classList.remove('clicked');
    
})


incomeButton.addEventListener('click', () => {
    incomeElement.classList.remove('hidden');
    expense.classList.add('hidden');
    expensesButton.classList.remove('clicked');
    incomeButton.classList.add('clicked');
    
})


incomeList.addEventListener('click', deleteOrEdit)
expenseList.addEventListener('click', deleteOrEdit)

let entry_list = []


addExpense.addEventListener('click', () =>{
    if(expenseTitle.value == '' || expenseAmount.value == ''){
                return;   
            }
            let expense = {
                type: 'expense',
                title: expenseTitle.value,
               amount: expenseAmount.value,
               id: Date.now()
            }
            entry_list.push(expense)

            clearExpense()
            changeLists()                             
})


addIncome.addEventListener('click', () =>{
    if(incomeTitle.value == '' || incomeAmount.value == ''){
        return;   
    }
    let income = {
        type: 'income',
        title: incomeTitle.value,
       amount: incomeAmount.value,
       id: Date.now()
    }
    entry_list.push(income)
    clearIncome()
    changeLists() 
 
})

const clearExpense = () =>{
    expenseTitle.value = '';
    expenseAmount.value = '';
}
const clearIncome = () =>{
    incomeTitle.value = ''
    incomeAmount.value = ''
}


const changeLists = () =>{
    expenseList.innerHTML = ''
    incomeList.innerHTML = ''
   entry_list.map((entry) =>{
        if(entry.type == 'expense'){
            return expenseList.innerHTML += `<li id = "${entry.id}" class= "${entry.type}">
            <div class = "entry">${entry.title}: $${entry.amount}</div>
            <div class="icon-container">
            <div class = "edit" id="${entry.id}"></div>
            <div class ="delete" id="${entry.id}"></div>
            </div>
            </li>`
          
        }
        else if(entry.type == 'income'){
            return incomeList.innerHTML += `<li id = "${entry.id}" class= "${entry.type}">
            <div class = "entry">${entry.title}: $${entry.amount}</div>
            <div class="icon-container">
            <div class = "edit" id="${entry.id}"></div>
            <div class ="delete" id="${entry.id}"></div>
            </div>
            </li>`  
        }
             
    })
    addIncomes()     
}


const addIncomes = () =>{
    let sum = 0;
    let income = 0;
    let outcome = 0;
    balanceElement.innerHTML = 0
    totalIncome.innerHTML = 0
    totalOutcome.innerHTML = 0
entry_list.forEach(list =>{
    if(list.type == 'expense'){
        sum -= list.amount
        outcome -= list.amount
    }else if(list.type == 'income'){
        sum += Number(list.amount)
        income += Number(list.amount)
    }
    balanceElement.innerHTML = '$' + sum
    totalIncome.innerHTML = '$' + income
    totalOutcome.innerHTML = '$' + outcome
})
} 


// // DETERMINE IF BUTTON IS EDIT OR DELETE
function deleteOrEdit(e){
const targetButton = e.target;
const entry = targetButton.parentNode.parentNode;
if(targetButton.classList == ('delete')){ 
    deleteEntry(entry)
}else if(targetButton.classList == ('edit')){
    editEntry(entry); 
}
}

// //DELETE FUNCTION
const deleteEntry = (entry) =>{
console.log(entry.id)

     entry_list.splice(entry.id, 1)
    //  entry.innerHTML = ''
     console.log(entry.id)
     addIncomes()
     
}


// EDIT FUNCTION
const editEntry = (entry) =>{
let Entry = entry_list[entry.id]
if(entry.type == "income"){
    incomeAmount.value = Entry.amount;
    incomeTitle.value = Entry.title;
}else if(entry.type == "expense"){
    expenseAmount.value = Entry.amount;
    expenseTitle.value = Entry.title;
}
deleteEntry(entry);
}
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&family=Raleway:wght@400;700&display=swap');




*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Open Sans', sans-serif;
}

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}


.budget-container{
    display: flex;
    align-items: center;  
    justify-content: center;  
   
    height: 100vh;
    width: 100%;
    background-color: #4F98CA;
}


.balance-container{
    width: 360px;
    height: 470px;
    background-color: #50D890;
    border-radius: 30px;
    margin-right: 100px;
    

}


.app-title{
    color: white;
    margin-top: 1rem;
    margin-left: 1rem;
    
}

.month{
    color: white;
    margin-top: 1rem;
    text-align: center;
    
}


.budget-header{

    display: flex;
    flex-direction:column;
    justify-content: center;
}


.balance{
    margin-top: 1rem;
    margin-left: 1rem;
   
}



.title{
    color: white;
    font-size: 1.25rem;
    opacity: .75;
}

.value{
    font-size: 1.75rem;
    color: white;
    font-weight: bold;
    margin-left: 1rem;
    
}

.account{
    margin-top: 2.5rem;
    margin: 2.5rem 1.5rem 2.5rem 1.5rem;
    display: flex;
    justify-content: space-between

}

.income-total{
    color: white;
    text-align: center;
    font-size: 1.5rem;
}

.outcome-total{
    color: #4F98CA;
    text-align: center;
    font-size: 1.5rem;
}



/* DASHBOARD */


.budget-dashboard{
    display: block;
    width: 360px;
    height: 470px;
    position: relative;
    border-radius: 30px;
    background-color: white;
}

.dash-title{
    margin-top: 2rem;
    margin-left: 1rem;
    font-size: 1.5rem;
    
}

.toggle{
    margin: 1rem;
    display: flex;
    cursor: pointer;
}
.toggle .tab2, .tab3{
    margin-left: 1rem;
    cursor: pointer;
}

.clicked{
    font-weight: bold !important;
}

.hidden{
    display: none !important;
}


/* EXPENSES TAB */


.expense-tab{
    display: flex;
    justify-content: center;
}
.expense-input-container{
    position: absolute;
    top: 400px;
    border-top: solid 1px gray;  
    width: 100%;
    
}

.expense-amount-input{
    width: 125px;
    border: none;
    outline: none;
    font-size: 1.25rem;
}


.expense-title-input{
    width: 125px;
    border: none;
    outline: none;
    font-size: 1.25rem;
}

.add-expense{
    color: none;
    background-color: none;
    border: none;
    outline: none;  
    color: inherit;
}

/* INCOME TAB */

.income-tab{
    display: flex;
    justify-content: center;
}

.income-input-container{
    position: absolute;
    top: 400px;
    border-top: solid 1px gray;  
    width: 100%;
    
}

.input{
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin: 1rem;
    
}
.income-amount-input{
    width: 125px;
    border: none;
    outline: none;
    font-size: 1.25rem;
} 

.income-title-input{
    width: 125px;
    border: none;
    outline: none;
    font-size: 1.25rem;
}


.add-income{
    color: none;
    background-color: none;
    border: none;
    outline: none;
}

.plus-img{
    width: 40px;
}


/* li */

ul{
    width: 360px;
    height: 255px;
    list-style: none;
    margin-top:20px;
    overflow-x: auto;
    
}


/* BUTTON ICONS */

.edit{
    background-image: url('media/Icons/icons8-edit-48.png');
    background-size: contain;
    width: 25px;
    height: 25px;
    background-repeat: no-repeat;
    margin-right: 10px;
}

.delete{
    background-image: url('media/Icons/icons8-trash-can-48 (2).png');
    background-size: contain;
    width:25px;
    height: 25px;
    background-repeat: no-repeat;
}




.income{
    width:250px;
    height: auto;
    padding-left: 20px;
    margin-bottom: 10px;;
    word-wrap: break-word;
    color: black
}

.expense{
    width:250px;
    height: auto;
    padding-left: 20px;
    margin-bottom: 10px;;
    word-wrap: break-word;
    font-family: 'Gilroy Bold';
    color: #4F98CA;
}

li{
    display: flex;
    justify-content: space-between;
    width: 100% !important;
    padding-right: 20px;
}

.icon-container{
    display: flex;
}


























@media (max-width:900px){

   
    .budget-container{
    display: inline-block;
    position: relative
    }

    .balance-container{
    position: absolute;
    top: 10%;
    left: 25%;

    }

    .budget-dashboard{
    position: absolute;
    left: 25%;
    top: 40%;
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Budgetrr</title>
</head>
<body>
    <main class="budget-container">

        <section class="balance-container">


        
        <div class="app-title">
            <p>Budgetrr</p>
        </div>

        <h1 class="month">OCTOBER</h1>

        <section class="budget-header">
            <div class="balance">
                <div class="title">
                    Balance
                </div>
                <div class="value">
                    <small>$</small>0
                </div>
            </div>


            <div class="account">
                <div class="budget-income">
                    <div class="title">
                        Income
                    </div>
                    <div class="income-total">
                        <small>$</small>0
                    </div>
                </div>
                <div class="chart"></div>
                <div class="budgetoutcome">
                    <div class="title">
                        Expenses
                    </div>
                    <div class="outcome-total">
                        <small>$</small>0
                    </div>
                </div>
            </div>

        </section>
    </section>


        <section class="budget-dashboard">
            <div class="dash-title">Dashboard</div>
            <div class="toggle">
                <div class="tab1 clicked">Expenses</div>
                <div class="tab2">Income</div>
                <!-- <div class="tab3  clicked">All</div> -->
            </div>




            <div class="income-tab hidden">
                <ul class="list"></ul>

                <div class="income-input-container">
                <form class="input">
                    <input type="text" class="income-title-input" name="title" placeholder="Title">

                    <input type="number" class="income-amount-input" name="amount" placeholder="$0">

                    <button type = "button" class="add-income"><img class= "plus-img"src="media/Icons/icons8-add-new-48.png" alt=""></button>
                </form>
            </div>
            </div>



             <div class = "expense-tab">
                <ul class="list"></ul>

                <div class="expense-input-container">

                <div class="input">
                    <input type="text" class="expense-title-input" name="title" placeholder="Title">

                    <input type="number" class="expense-amount-input" name="amount" placeholder="$0">
                    <button type="button" class="add-expense"><img class= "plus-img" src="media/Icons/icons8-add-new-48.png" alt=""></button>
                    
                </div>
                </div> 
            </div>




           
        </section>
    </main>
    <script src="JavaScript/budget.js"></script>
</body>
</html>

I've tried to use .splice() but I can't seem to get it to work.

Ehrlack3r
  • 1
  • 1
  • Hello friend, something about the formatting of the answer went wrong, and your question is unreadable, therefore un-answerable. Could you please have another look and edit it? – Tal Kohavy Nov 06 '22 at 21:23
  • An `id` must be unique. This: "all have the same `id` of `Date.now()`" won't work. – Tibrogargan Nov 06 '22 at 21:25
  • Would you please include your basic HTML in this question. In order to test a fix, a reader needs to reverse engineer your HTML from your code, which is more effort than most people want to go to. This is why StackOverflow asks for an [mre] (which you have not provided) – Tibrogargan Nov 06 '22 at 21:31
  • In the meantime, you could probably use [a GUID/UUID](https://stackoverflow.com/a/2117523/2487517) instead of the date for your IDs and fix this yourself. (i.e. replace `Date.now()` with `crypto.randomUUID()`) – Tibrogargan Nov 06 '22 at 21:32
  • The post has been updated. – Ehrlack3r Nov 07 '22 at 15:17

1 Answers1

0

Your entry is an object. And entry has an id property with Date type.

Your delete function calls this:

entry_list.splice(entry.id, 1)

Javascript splice function function takes number as argument(s).

You should find the id of element you want to delete and get its index. After that you can delete the element with splice method.


Here is how to delete:

// Find the index of object at the given list
const index = entry_list.findIndex(x => x.id === entry.id);

// Starting from index of element to delete, remove 1 element.
entry_list.splice(index, 1);
SeLeCtRa
  • 600
  • 1
  • 6
  • 14
  • Did you test this with the OP's code? That's doubtful, since it's hard to run without the HTML. Seem like this is more of a comment/guess than an answer (also, it won't work) – Tibrogargan Nov 06 '22 at 21:36
  • The structure of the OP's HTML is always going to have problems because the OP is duplicating the `id`. Basing any code on the `id` will be non-deterministic and the behavior will be browser dependent. If you had restructured the `entry_list` code to work around that issue it would probably work, but you didn't. – Tibrogargan Nov 06 '22 at 21:43
  • Question is not related to id. I answered only what OP asked. If you want to suggest this write it in a comment. Not here. – SeLeCtRa Nov 06 '22 at 21:47
  • I doubt there will be any duplicate id's when setting id to Date.now() in a click event. It would require a user to click twice within a millisecond. – Bqardi Nov 06 '22 at 21:50
  • @Bqardi I doubt it too, but why depend on that risk not happening when you don't have to? – Tibrogargan Nov 06 '22 at 23:19
  • Thank you @SeLeCtRa for answering, this is exactly what i'm trying to do. The problem is that I don't know how to do it. – Ehrlack3r Nov 07 '22 at 15:19
  • @Ehrlack3r I updated my answer. This should work. If you have problem please let me know – SeLeCtRa Nov 07 '22 at 21:43