Event handler for dynamically added elements

Hi

html

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>Workout organizer app</title>
    <link rel="stylesheet" type="text/css" href="style.css">

</head>
<body>
    <div class="flex-container">
    <header>
        <input type="image" id="menu-icon" class="icons" alt="Menu" src="menu-icon.svg">
        <input type="text" name="add-exercise" id="add-exercise" placeholder="Exercise">
        <input type="text" name="add-group" id="add-group" placeholder="Group">
        <input type="image" id="plus-icon" class="icons" alt="Add" src="plus-icon.svg">
    </header>
    </div> 
    <div>
        <div class="exercise-type" id='back-exercise' >
          <p>BACK</p>
         
        </div>
        <ul class="exercise-ul" id="back-exercise-ul">
                <li class='exercise-li'><span class='list-text'>deadlift</span></li>
                <li class='exercise-li'><span class='list-text'>deadlift sumo</span></li>
                <li class='exercise-li'><span class='list-text'>deadlift bulgarian</span></li>
        </ul>
        
        <div class="exercise-type" id='chest-exercise' >
                <p>CHEST</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>Bench press</span></li>
                <li class='exercise-li'><span class='list-text'>dumbell floor press</span></li>
                <li class='exercise-li'><span class='list-text'>push ups</span></li>
        </ul>
        
        <div class="exercise-type" id='legs-exercise' >
                <p>LEGS</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>back squat</span></li>
                <li class='exercise-li'><span class='list-text'>front squat</span></li>
                <li class='exercise-li'><span class='list-text'>barbell lunges</span></li>
        </ul>
        <div class="exercise-type" id='shoulders-exercise' >
                <p>SHOULDERS</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>Military press</span></li>
                <li class='exercise-li'><span class='list-text'>dumbell floor press</span></li>
                <li class='exercise-li'><span class='list-text'>bla</span></li>
        </ul>
        <div class="exercise-type" id='stomach-exercise' >
                <p>STOMACH</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>123</span></li>
                <li class='exercise-li'><span class='list-text'>456f</span></li>
                <li class='exercise-li'><span class='list-text'>789</span></li>
        </ul>
        <div class="exercise-type" id='forearms-exercise' >
                <p>FOREARMS</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>123f</span></li>
                <li class='exercise-li'><span class='list-text'>456</span></li>
                <li class='exercise-li'><span class='list-text'>789</span></li>
        </ul>
        <div class="exercise-type" id='olympic-exercise' >
                <p>OLYMPIC WEIGHTLIFTING</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>snatch</span></li>
                <li class='exercise-li'><span class='list-text'>clean & jerk</span></li>
                <li class='exercise-li'><span class='list-text'>power snatch</span></li>
        </ul>
        <div class="exercise-type" id='stamina-exercise' >
                <p>STAMINA</p>
        </div>
        <ul class="exercise-ul">
                <li class='exercise-li'><span class='list-text'>bike</span></li>
                <li class='exercise-li'><span class='list-text'>running</span></li>
                <li class='exercise-li'><span class='list-text'>boat</span></li>
        </ul>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript" src="main.js"></script>
       
</body>

css

html, body {
    margin:0px;
    background-color: rgb(200,200,200);
    padding: 0px;
}

.flex-container {
    display: flex;
    justify-content: center;
    align-items: center;
    
    
}
header {
    width: 100%;
    height: 80px;
    background-color: rgb(40, 40, 40);
    display: inline-flex;
    margin-bottom: 10%;  
}
#add-exercise {
    width: 60%;
    height: 40px;
    border-radius: 10px;
    background-color: rgb(60, 60, 60);
    font-size: 20px;
    color: rgb(220, 220, 220);
    text-align: center;
    margin: auto 1%;
     
}

#add-group {
    width: 30%;
    height: 40px;
    border-radius: 10px;
    background-color: rgb(60, 60, 60);
    font-size: 20px;
    color: rgb(220, 220, 220);
    text-align: center;
    margin: auto 1%;
}





.icons {
     margin: auto 1%; 
   
}

.exercise-type {
background-color: rgb(40, 40, 40);
color: rgb(220, 220, 220);
border-radius: 10px;
font-size: 20px;
width: 60%;
height: 45px;
display: grid;
text-align: center;
margin: 1% auto;
border-style: solid;
border-color: rgb(255, 255, 255);
cursor: pointer;
}

.exercise-type p {
    margin: auto;
    justify-items: center;
    text-transform: uppercase;
}
ul {
    display: none;
   
    
    
}

.exercise-li {
    background-color: rgb(100, 200, 100);
    color: rgb(220, 220, 220);
    border-radius: 10px;
    font-size: 20px;
    width: 60%;
    height: 45px;
    display: grid;
    text-align: center;
    margin: 1% auto;
    border-style: solid;
    border-color: rgb(255, 255, 255);
    
    }

.exercise-li-highlighted {
    background-color: red;
    color: rgb(220, 220, 220);
    border-radius: 10px;
    font-size: 20px;
    width: 60%;
    height: 45px;
    display: grid;
    text-align: center;
    margin: 1% auto;
    border-style: solid;
    border-color: rgb(255, 255, 255);
        
    }

.list-text {
        margin: auto;
        justify-items: center;
        text-transform: uppercase;
     }

js

$(document).ready(function() {


// creates a group list and pushed to list all the paragrapstext in html    
let groupExerciseList = [];
for ( let i = 0; i < $('p').length; i++){
    groupExerciseList.push($('p').eq(i).text());
};

// creates a list of exercises from classes of li
let exerciseList = [];
for (let i = 0; i < $('.exercise-li').length; i++){
    exerciseList.push($('.exercise-li').eq(i).text());
}

// on click scroll exercise for each category
let exerciseScroller =  function(){

    let classOfSibling = $(event.currentTarget).next().attr('class');
    if(classOfSibling === 'exercise-ul'){
    $(event.currentTarget).next().slideToggle();
    
}}

$('.exercise-type').on('click', exerciseScroller);



let addExercise = function(){

    let exerciseInput = $('#add-exercise').val().toUpperCase();
    let groupInput = $('#add-group').val().toUpperCase();
    
    // this code finds the paragraph from group input and targets the list for it
    let groupFinder = $(`p:contains(${groupInput})`).parent().next();
    
for (let i = 0; i < groupExerciseList.length; i++ ) {
     // Check if input is not empty to not add empty li after clicking plus icon
    if (exerciseInput !== '' && groupInput === groupExerciseList[i]){
        // Adds the exercise from input into list
        groupFinder.append(`<li class='exercise-li'><span class='list-text'>${exerciseInput}</span></li>`);
       
        // Adds the exercise into js list})
        exerciseList.push(`${exerciseInput}`);
        // Empty the exercise and group input after adding
        $('#add-exercise').val('');
        $('#add-group').val('');      
    } 
}        
};


// function highlights the group input on red if u type a group that does not exist and put it back to normal if u type correctly
let validateGroup = function() {
    let groupInput = $('#add-group').val().toUpperCase();
    
    if (!groupExerciseList.includes(groupInput)) {
        $('#add-group').css('background-color', 'red');     
    } 
    else if (groupExerciseList.includes(groupInput)){
        $('#add-group').css('background-color', 'rgb(60, 60, 60)');   
    }
};

let exerciseRemover = function() {
    $(event.currentTarget).toggleClass('exercise-li-highlighted');
    

   if ($('li').hasClass('exercise-li-highlighted')) {
    $('#plus-icon').attr('src', 'minus-icon.svg'); 
   }
   else {
    $('#plus-icon').attr('src', 'plus-icon.svg');   
   }
}

let exerciseRemover2 = function() {

    if ($('#plus-icon').attr('src', 'minus-icon.svg')) {
        console.log(3)
}
}

$('#plus-icon').on('click', validateGroup);
$('#plus-icon').on('click', addExercise);
$('.exercise-ul').on('click', '.exercise-li',  exerciseRemover);
//$('#plus-icon').on('click', exerciseRemover2);


    });

On top of the website is a header with 2 inputs and a submit button. After you type exercise and group the exercise gonna be added to this group. Groups are made out of divs, exercises are li of ul that are siblings to those divs. I created an event handler with a function called exerciseRemove that is changing the background color of clicked exercise and changing the submit image from + into -. The event was working fine, but only for static li that were already on page but not on those added dynamically by js. I read that the way to do it with the others too i need to target the parent in jquery and add the actual target in a selector. So I changed the the code from

$(’.exercise-li’).on(‘click’, exerciseRemover);

into

$(’.exercise-ul’).on(‘click’, ‘.exercise-li’, exerciseRemover);

The event seems to be happening on all li now, however I have no idea what is actually going on. After click all the li from the ul seems to disappear and in front I see the first li from the ul with much smaller width and some red square behind it (the clicked li should change the background into red). With this event it happens on all li (those added dynamically and static that were already on page). To be clear with the first it works fine but just on those static.

I have also a second question but regarding to CSS. The groups made out of divs and exercises made out of li have exactly same styling except background-color. However the exercises have smaller width (both of them are set to 60%). Is it because the value is set by % not by px? Because height seems be same and was set by px. What is the way to solve this one? The exercise is a li that is a child of ul that is the sibling of divs that make the groups.

1 Like

The code is allowing all clicks to bubble up to the parent container, the UL, and is then delegated to the element that received the click (event target). The handler will know it element as this or $(this). Both represent the same object.

That is the preferred way to listen for events when several objects are contained. It only takes one listener which can be registered on an empty list. Newly added objects don’t need to register their listener as they already have one on the parent. That’s what makes them so powerful with dynamic content (but also valuable in static situations) since it never requires more memory.

1 Like

Ok I got it. I changed the code from

$(event.currentTarget).toggleClass(‘exercise-li-highlighted’);

into

$(this).toggleClass(‘exercise-li-highlighted’);

I tought that this and event.CurrentTarget is just a different syntax however there is a difference in functionality too. With this everything works fine.

2 Likes