2
votes

I recently started to learn Javascript. I'm currently working on some project and I ran into a problem. I'm making contact manager page, the idea is that the user enters name, email and a phone number which will be displayed in the dynamically created HTML table below. There are three buttons. The first button save's information, second clears input fields for new user and third deleteds localStorage information. When you click on a row in the table, information from that row are to be displayed in the input fields above. When you click on a row in HTML table everything is working fine, but when I click on the row in the table created using JavaScript it doesn't work. Both tables have the same structure, id's and class, but the other one doesn't work. Information form Javascript are displayed in dvcontainer div.

Here's the code

(function (){ 

var Person = { 
    Name: "", 
    Email: "", 
    MobileNo: ""
};

var applogic = {
    clearuielements: function () { 
        var inputs = document.getElementsByClassName("c1"); 
        for (i = 0; i < inputs.length; i++) { 
            inputs[i].value = ""; 
        } 
    },
    
    saveitem: function () { 
        var lscount = localStorage.length;
        var inputs = document.getElementsByClassName("c1"); 
            Person.Name = inputs[0].value; 
            Person.Email = inputs[1].value; 
            Person.MobileNo = inputs[2].value; 
            localStorage.setItem("Person_" + lscount, JSON.stringify(Person)); 
            location.reload(); 
    },
    
    loaddata: function () { 
        var datacount = localStorage.length; 
        if (datacount > 0) { 
            var render = "<table id='t_id' border='1'>"; 
            render += "<tr><th>Name</th><th>Email</th><th>Phone</th></tr>"; 
            for (i = 0; i < datacount; i++) { 
                var key = localStorage.key(i);
                var person = localStorage.getItem(key);
                var data = JSON.parse(person);

                render += "<tr><td class='row_s edit_row'>" + data.Name + "</td>"; 
                render += "<td class='row_t'>" + data.Email + "</td>"; 
                render += "<td class='row_d'>" + data.MobileNo + "</td></tr>"; 
            } 
            render+="</table>"; 
            dvcontainer.innerHTML = render; 
        } 
    },
    
    clearstorage: function () { 
        var storagecount = localStorage.length;
        if (storagecount > 0) { 
            for (i = 0; i < storagecount; i++) { 
                localStorage.clear(); 
            } 
        } 
        window.location.reload(); 
    }
};
    
    var btnsave = document.getElementById('btnsave'); 
    btnsave.addEventListener('click', applogic.saveitem, false); 

    var btnclear = document.getElementById('btnclear'); 
    btnclear.addEventListener('click', applogic.clearuielements, false); 

    var btnclearstorage = document.getElementById('btnclearstorage'); 
    btnclearstorage.addEventListener('click', applogic.clearstorage, false); 

    window.onload = function () { 
    applogic.loaddata(); 
    }; 
    
})();

var edit_row = document.querySelectorAll('#t_id .edit_row');
for(var i=0; i<edit_row.length; i++) {
    edit_row[i].addEventListener('click', function(e){
        var tr_parent = this.parentNode;
        document.getElementById('e_site').value = tr_parent.querySelector('.row_s').innerHTML;
        document.getElementById('e_title').value = tr_parent.querySelector('.row_t').innerHTML;
        document.getElementById('e_desc').value = tr_parent.querySelector('.row_d').innerHTML;
    }, false);
}
<!DOCTYPE html>
<html>
    <head>
        <title>nesto</title>
    </head>
    <body>

    <form action="#" method="post" id="edit_form">
        Person Name:<input name="e_site" id="e_site" type="text" class="c1" /><br/>
        Email:<input name="e_title" id="e_title" type="text" class="c1" /><br/>
        Mobile No:<input name="e_desc" id="e_desc" type="text" class="c1"/><br/>
    </form>

    <td><input id="btnsave" type="button" value="Save" /></td> 
    <td><input id="btnclear" type="button" value="Clear" /></td> 
    <td><input id="btnclearstorage" type="button" value="Clear Storage" /></td> 

    <div id="dvcontainer"></div>
     
    
        <!--
        <table id="t_id" border="1">
            <tr>
                <td class="row_s edit_row">yyyy</td>
                <td class="row_t">yyyy</td>
                <td class="row_d">yyyyy</td>
            </tr>
        </table>
        -->
    
        
    <script type="text/javascript" src="funkcije.js"></script>

    </body>
</html>
3
I don't see any table, when i run the snippet. - John
You've gotta add the bindings dynamically when you load the data. - Adam Konieska
What most likely seems to be happening is you are adding your event listeners to the table before the other table has been loaded. So you would need to add the event listener to the dynamic table after it is created - Adjit
var dvcontainer = document.getElementById("dvcontainer"); - DIEGO CARRASCAL
@John Yeah, i see that now..don't know why, but it works well on my pc. - Đorđe Stanivuković

3 Answers

0
votes

You need to add the bindings when you render the table. One way to do this is by moving the binding loop you have at the end of your code into the loaddata function.

Something like this would work:

loaddata: function () { 
        var datacount = localStorage.length; 
        if (datacount > 0) { 
            var render = "<table id='t_id' border='1'>"; 
            render += "<tr><th>Name</th><th>Email</th><th>Phone</th></tr>"; 
            for (i = 0; i < datacount; i++) { 
                var key = localStorage.key(i);
                var person = localStorage.getItem(key);
                var data = JSON.parse(person);

                render += "<tr><td class='row_s edit_row'>" + data.Name + "</td>"; 
                render += "<td class='row_t'>" + data.Email + "</td>"; 
                render += "<td class='row_d'>" + data.MobileNo + "</td></tr>"; 
            } 
            render+="</table>"; 
            dvcontainer.innerHTML = render; 


            //add bindings
            var edit_row = document.querySelectorAll('#t_id .edit_row');
            for(var i=0; i<edit_row.length; i++) {
                edit_row[i].addEventListener('click', function(e){
                    var tr_parent = this.parentNode;
                    document.getElementById('e_site').value = tr_parent.querySelector('.row_s').innerHTML;
                    document.getElementById('e_title').value = tr_parent.querySelector('.row_t').innerHTML;
                    document.getElementById('e_desc').value = tr_parent.querySelector('.row_d').innerHTML;
                }, false);
            }

        } 

See a full example in this JS Fiddle: https://jsfiddle.net/9n8ecmj8/

Hope that helps!

0
votes

If you add new elements to the DOM and want to them to have an event listener, you need to add the listener to the elements after you add them.

Looks like you're adding the click event listeners to elements that exist when the script loads, but are not adding any to the elements created dynamically. The code where you set the click listeners gets run before the loaddata function bound to window.onload.

0
votes

The for loop you wrote to add listeners gets executed when your JavaScript is first loaded, long before you've created any new table cells. To register an event listener on a DOM element, the element must exist in the first place, right? :)

What you need to do is something called event delegation. Basically, you wire up an event listener on a DOM element that exists on your webpage before your JavaScript runs. This DOM element must be a parent to your target element(s).

In your case, the div with ID "dvcontainer" will do just fine, since you render the table within it. First, we will select it:

var myContainer = document.querySelector('#dvcontainer');

myContainer.addEventListener('click', function(e) {
  //...
};

If we add an event listener to myContainer, it will invoke the click handler function on any click to the entire div. We know e.target selects the clicked element, but there is a useful method on elements called matches that will tell us if the selected element matches a selector string (like "#container", ".username", or even "h2").

What would be a good selector for your table? To keep things simple, let's use "td" to handle any table cell < td > that gets a click. Here's a working solution to your problem (replace your for loop with this):

var myContainer = document.querySelector('#dvcontainer');

// adds event listener on myContainer
myContainer.addEventListener('click', function(e) {

  // if the clicked element exists and is a TD element
  if (e.target && e.target.matches('td')) {

    // your code from your original for loop
    var tr_parent = e.target.parentNode;
    document.getElementById('e_site').value = tr_parent.querySelector('.row_s').innerHTML;
    document.getElementById('e_title').value = tr_parent.querySelector('.row_t').innerHTML;
    document.getElementById('e_desc').value = tr_parent.querySelector('.row_d').innerHTML;
  }
}, false);

The great thing about this approach is you only have to set the event listener one time. Whether you use JS to make one, two, or a thousand new DOM elements dynamically, they will all "inherit" the same listener and handler without registering every single element on the fly.

Hope this was helpful to you!

More on:

Vanilla JS event delegation: https://davidwalsh.name/event-delegate

element.matches(): https://developer.mozilla.org/en-US/docs/Web/API/Element/matches