简体   繁体   中英

Get specific cell value from specific row in dynamically populated table JavaScript

My web application gets values from a JSON object, and populates a table with its values along with an edit and delete button on each row. I want my script to be able to know which rows edit button was pressed, along with that rows cell values.

While formatting my question, I stumbled (I searched for an hour before deciding to ask) upon this exact same question, but it did not contain any solution that worked for me: Get Value of HTML cells in row clicked with Javascript

HTML:

<div class="container">     
    <h4>Persons</h4>
    <div class="table-responsive">
        <table class="table table-striped" id="persons_table">
            <thead class="thead-dark">
                <tr>
                    <th scope="col">ID</th>
                    <th scope="col">Name</th>
                    <th scope="col">Email</th>
                    <th scope="col">Phone</th>
                    <th scope="col">Edit</th>
                    <th scope="col">Delete</th>
                </tr>
            </thead>
            <tbody>
                    //dynamically generated rows goes here
            </tbody>
        </table>
    </div>
    <button class="btn btn-sm btn-primary" id="addBtn">New person</button>
</div>

Function for populating the table:

function handlePersons(data){ //data is a response from an API (JSON)
    var table = document.getElementById('persons_table');
    data.forEach(function(object) {
        var tr = document.createElement('tr');
        tr.innerHTML = 
        '<td>' + object.ID + '</td>' +
        '<td>' + object.Info.Name + '</td>' +
        '<td>' + object.Info.Email + '</td>' +
        '<td>' + object.Info.PhoneNumber + '</td>' +
        '<td><p data-placement="top" data-toggle="tooltip" title="Edit">
        <button class="btn btn-primary btn-xs" id="editBtn" data-title="Edit" data-toggle="modal" data-target="#edit">
        <span class="fa fa-pencil"></span></button></p></td>' +
        '<td><p data-placement="top" data-toggle="tooltip" title="Delete">
        <button class="btn btn-danger btn-xs" data-title="Delete" data-toggle="modal" data-target="#delete">
        <span class="fa fa-trash"></span></button></p></td>';
        table.appendChild(tr);
        });
    }

Just in case you need it, this is my ajax call to fetch JSON data:

function getPersons(){
    var settings = {
            "async" : true,
            "crossDomain" : true,
            "url" : "...",
            "method" : "GET",
            "headers" : {
                "Content-Type" : "application/json",
                "Authorization": "...",
                "cache-control" : "no-cache"
            },
            "processData" : false,
            "data" : ""
        }
        return $.ajax(settings);
}

Function call:

getPersons().done(handlePersons)

In practice it looks like this: 在此输入图像描述

My question is, when I press the edit button on the first row, how can I let my script know it was the edit button on that specific row that was pressed, and how can I for example console.log that rows name (John)? This is what I've tried:

$('#editBtn').on('click', () => {

    var par = $(this).parent().parent(); //to choose table row, also tried one 
                                         //and three .parent() because I don't 
                                         //quite understand this

    var namevalue = par.children("td:nth-child(2)").val(); //also tried with .text()
    console.log(namevalue); //.val = undefined and .text = empty string

});
$('#editBtn').on('click', () => { //also tried with onclick="function" 
                                  //on the button instead of
                                  //jQuery just in case

    var tr = $(this).closest('tr')
    var tdID = $(tr).find('td').eq(0).text();
    var tdName = $(tr).find('td').eq(1).text();
    var tdEmail = $(tr).find('td').eq(2).text();
    var tdPhone = $(tr).find('td').eq(3).text();

    console.log(tdName); //.text logs empty string, .val logs undefined again

});

JSON data after request from @Cybernetic. This is edited a little bit to not reveal any sensitive information, but should still produce the same result in my code.

[
    {
        "CustomValues": {},
        "Comment": "Here is John",
        "Deleted": false,
        "ID": 1,
        "InfoID": 4,
        "Role": null,
        "StatusCode": null,
        "UpdatedAt": null,
        "UpdatedBy": null,
        "Info": {            
            "DefaultEmailID": 1,
            "DefaultPhoneID": 2,
            "Deleted": false,
            "ID": 3,
            "Name": "John",
            "PhoneNumber": "123-123-123",
            "Email": "john@mail.com"                      
        }
    },
    {
        "CustomValues": {},
        "Comment": "Here is Mike",
        "Deleted": false,
        "ID": 2,
        "InfoID": 6,
        "Role": null,
        "StatusCode": null,
        "UpdatedAt": null,
        "UpdatedBy": null,
        "Info": {            
            "DefaultEmailID": 3,
            "DefaultPhoneID": 4,
            "Deleted": false,
            "ID": 6,
            "Name": "Mike",
            "PhoneNumber": "321-321-321",
            "Email": "mike@mail.com"                      
        }
    }   
]

First of all, you give the same id #editBtn to multiple elements. That is incorrect, an id must be unique in the scope of an HTML document.

Secondly i assume that you place the click event of the editBtn only on the page load. But since you generate these elements with AJAX, the click event won't bind on them. You need to call the $('#editBtn').on('click', function()) event, on the success/done of the AJAX data.

Thirdly, this:

var par = $(this).parent().parent();

will not work. The editBtn is inside ap, so the $('#editBtn').parent().parent() is the td, not the tr. You need to add one more parent, although trying to retrieve data, traversing the DOM this way is a bad practice and can lead to code breaking and more difficult maintenance. It is better to use specific ids or data attributes.

Thanks for the data.

Simply add data to each dynamically generated row (tr element):

function handlePersons(data){ 
    var table = document.getElementById('persons_table');
    data.forEach(function(object) {
        var tr = document.createElement('tr');
        tr.setAttribute('data-id', object.ID) // adding data
        tr.setAttribute('data-name', object.Info.Name) // adding data
        tr.setAttribute('data-email', object.Info.Email) // adding data
        tr.setAttribute('data-phone', object.Info.PhoneNumber) // adding data
        tr.innerHTML = 
        '<td>' + object.ID + '</td>' +
        '<td>' + object.Info.Name + '</td>' +
        '<td>' + object.Info.Email + '</td>' +
        '<td>' + object.Info.PhoneNumber + '</td>' +
        `<td><p data-placement="top" data-toggle="tooltip" title="Edit">
        <button class="btn btn-primary btn-xs" id="editBtn" data-title="Edit" data-toggle="modal" data-target="#edit">
        <span class="fa fa-pencil"></span></button></p></td>' +
        '<td><p data-placement="top" data-toggle="tooltip" title="Delete">
        <button class="btn btn-danger btn-xs" data-title="Delete" data-toggle="modal" data-target="#delete">
        <span class="fa fa-trash"></span></button></p></td>`;
        table.appendChild(tr);
        });
    }

...then use the edit button's position within the table to locate its parent row and read its data as needed:

$('.btn-xs').click(function() {
    $(this).parents('tr').css('background', 'pink')
    id = $(this).parents('tr').data('id')
    name = $(this).parents('tr').data('name')
    email = $(this).parents('tr').data('email')
    phone = $(this).parents('tr').data('phone')
    alert(id + '\n' + name + '\n' + email + '\n' + phone)
})

Result :

在此输入图像描述

Here is the Fiddle

Since jQuery is used, there are two undocumented rules you should follow:

  1. Do not use id unless required (such as Bootstrap data- attributes), use class. Note: The demo below demonstrates how we can use class exclusively and no longer have any dependency on ids.

  2. Do not use on-event attributes <button onclick="fnc()" >

The first rule is applied to demo and the second rule isn't relevant just added for completeness. Also, ids must be unique -- the OP code provided was generating duplicate button#editBtn .

Dynamically added elements cannot be bound to events so any attempts to add 'click' events to the edit/delete buttons would fail. Only elements that existed at load time can be registered to events, so any ancestor element from <tbody> to document can be registered to the click event and then the event can be delegated to the edit/delete buttons. In general terms, delegating events involves programming pattern used to specifically determine which elements react to events and which elements ignore such events.

jQuery event methods make event delegation easy by providing the first (second for .on() ) parameter called eventData . eventData is an optional parameter in the format of a selector string (same as what is commonly in $( ... ) ). That will become the context of the function -- $(this) or $(event.target) -- essentially the clicked button.
Ancestor eventData
⇩ ⇩

 $(document).on('click', '.edit', function(e) {... 

Details commented in demo

 /* A: jQuery .append() is not destructive like .innerHTML Template literal is easier and cleaner than literal strings. */ function handleContacts(data) { data.forEach(function(object) { $('.contacts').append(`<tr> <td class='content'>${object.ID}</td> <td class='content'>${object.Info.Name}</td> <td class='content'>${object.Info.Email}</td> <td class='content'>${object.Info.PhoneNumber}</td> <td><button class="btn btn-primary btn-xs edit"><i class="fas fa-pen"></i></button></p></td> <td><button class="btn btn-danger btn-xs delete"><i class="fas fa-trash"></i></button></td></tr>`); //A }); } var data = [{ "CustomValues": {}, "Comment": "Here is John", "Deleted": false, "ID": 1, "InfoID": 4, "Role": null, "StatusCode": null, "UpdatedAt": null, "UpdatedBy": null, "Info": { "DefaultEmailID": 1, "DefaultPhoneID": 2, "Deleted": false, "ID": 3, "Name": "John", "PhoneNumber": "123-123-1231", "Email": "john@mail.com" } }, { "CustomValues": {}, "Comment": "Here is Mike", "Deleted": false, "ID": 2, "InfoID": 6, "Role": null, "StatusCode": null, "UpdatedAt": null, "UpdatedBy": null, "Info": { "DefaultEmailID": 3, "DefaultPhoneID": 4, "Deleted": false, "ID": 6, "Name": "Mike", "PhoneNumber": "321-321-3213", "Email": "mike@mail.com" } } ]; // button.add is clicked -- handleContacts() is called $('.add').on('click', function(e) { handleContacts(data); }); /* Delegate click event to document $(this) or $(e.target) is button.delete Get the .closest() <tr> and remove() it. */ $(document).on('click', '.delete', function(e) { $(e.target).closest('tr').remove(); }); /* Delegate click event to document Note: Any dynamically added elements cannot be bound to events. Events must be bound to an element that existed at load time. $(this) or $(e.target) is button.edit */ /* B: Get the .closest() <tr> C: In this <tr> find() .each() td.content and toggle the [contenteditable] attribute true/false D: In this <tr> find() td[contenteditable] and .focus() on the first one .eq(0). Note: $(this)[0] is a dereferenced "this" because jQuery will not recognize the JavaScript method .toggleAttribute() */ $(document).on('click', '.edit', function(e) { var row = $(e.target).closest('tr'); //B row.find('.content').each(function() { $(this)[0].toggleAttribute('contenteditable'); }); //C row.find('[contenteditable]').eq(0).focus(); //D }); 
 <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"> <link href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" rel="stylesheet" crossorigin="anonymous"> <div class="container"> <div class="table-responsive"> <table class="table table-striped contacts"> <caption style='caption-side:top'><h4>Contacts</h4></caption> <thead class="thead-dark"> <tr> <th scope="col">ID</th> <th scope="col">Name</th> <th scope="col">Email</th> <th scope="col">Phone</th> <th scope="col">Edit</th> <th scope="col">Delete</th> </tr> </thead> <tbody> </tbody> </table> </div> <button class="btn btn-sm btn-primary add">Add Contacts</button> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM