【问题标题】:Get specific cell value from specific row in dynamically populated table JavaScript从动态填充的表格 JavaScript 中的特定行获取特定的单元格值
【发布时间】:2019-04-21 20:42:49
【问题描述】:

我的 Web 应用程序从 JSON 对象获取值,并使用其值以及每行上的编辑和删除按钮填充表。我希望我的脚本能够知道按下了哪些行编辑按钮,以及该行的单元格值。

在格式化我的问题时,我偶然发现了这个完全相同的问题(我搜索了一个小时才决定提问),但它没有包含任何对我有用的解决方案: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 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);
        });
    }

以防万一,这是我获取 JSON 数据的 ajax 调用:

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);
}

函数调用:

getPersons().done(handlePersons)

实际上它看起来像这样:

我的问题是,当我按下第一行上的编辑按钮时,我怎样才能让我的脚本知道它是按下的特定行上的编辑按钮,以及我怎样才能例如 console.log 该行名称(约翰)? 这是我尝试过的:

$('#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

});

@Cyber​​netic 请求后的 JSON 数据。对此进行了一些编辑,以不泄露任何敏感信息,但仍应在我的代码中产生相同的结果。

[
    {
        "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"                      
        }
    }   
]

【问题讨论】:

  • 您能否提供一个示例 JSON 数据的 sn-p,以便我们重新创建表并制定解决方案?
  • @Cyber​​netic 当然,请参阅原帖底部的编辑。

标签: javascript jquery html


【解决方案1】:

由于使用了 jQuery,您应该遵循两个未记录的规则:

  1. 除非需要,否则不要使用id(如Bootstrapdata-属性),使用class。注意:下面的demo演示了我们如何可以独占使用class而不再依赖ids。

  2. 不要使用事件属性&lt;button onclick="fnc()" &gt;

第一条规则适用于演示,第二条规则不相关,只是为了完整性而添加。此外,ID 必须是唯一的——提供的 OP 代码会生成重复的 button#editBtn

动态添加的元素不能绑定到事件,因此任何将“点击”事件添加到编辑/删除按钮的尝试都会失败。只有在加载时存在的元素才能注册到事件中,因此从&lt;tbody&gt;document 的任何祖先元素都可以注册到点击事件,然后可以将事件委托给编辑/删除按钮。一般而言,委派事件涉及用于具体确定哪些元素对事件做出反应以及哪些元素忽略此类事件的编程模式。

jQuery 事件方法通过提供称为eventData 的第一个(.on() 的第二个)参数使事件委托变得容易。 eventData 是选择器字符串格式的可选参数(与$(...) 中的常用参数相同)。这将成为函数的上下文 - $(this)$(event.target) - 本质上是点击按钮。
Ancestor                  eventData
⇩                                 ⇩

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

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>

【讨论】:

  • 我很惊讶,我觉得我从这个答案中学到了很多东西。非常感谢您写出如此详细的回复、解决方案以及您提供的所有额外知识。这对我找到过去一周一直在努力的最后一块拼图有很大帮助。现在一切正常! :)
  • 太棒了,我很高兴能帮上忙。快乐编码?
【解决方案2】:

首先,您将相同的 id #editBtn 赋予多个元素。这是不正确的,一个 id 在 HTML 文档的范围内必须是唯一的。

其次,我假设您仅将 editBtn 的单击事件放在页面加载上。但是由于您使用 AJAX 生成这些元素,因此 click 事件不会绑定到它们上。您需要在 AJAX 数据成功/完成时调用 $('#editBtn').on('click', function()) 事件。

第三,这个:

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

不会工作。 editBtn 在 p 内,因此 $('#editBtn').parent().parent() 是 td,而不是 tr。您需要再添加一个父级,尽管尝试检索数据,但以这种方式遍历 DOM 是一种不好的做法,并且可能导致代码中断和更难维护。最好使用特定的 id 或数据属性。

【讨论】:

  • 感谢您提供的知识,我将研究一种在编辑按钮上生成唯一 ID 的方法。几天前我刚开始使用 AJAX,所以我并不完全了解 done 函数的所有内容。您能否举例说明在完成时调用单击事件的含义?我现在只使用这样的完成:getPersons().done(handlePersons)。你的意思是我应该这样做:getPersons().done(click event here)?
【解决方案3】:

感谢您的数据。

只需将数据添加到每个动态生成的行(tr 元素):

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);
        });
    }

...然后使用编辑按钮在表格中的位置来定位其父行并根据需要读取其数据

$('.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)
})

结果

这里是Fiddle

【讨论】:

  • 这太奇怪了,我似乎无法让它工作。我也不知道如何解决它。我能想到的唯一不同的是,这一行 data-title="Edit" data-toggle="modal" data-target="#edit" 实际上对我做了一些事情。会不会有干扰?我知道它可能会干扰 alert(),但我觉得 console.log(id) 应该仍然可以工作。需要明确的是,它根本没有记录任何东西,就像我什至没有点击按钮一样。我试过“#editBtn”,我试过删除按钮 ID 并在你的例子中调用“.btn-xs”。只是......什么都没有。
  • 您能否在创建按钮后立即尝试动态添加点击处理程序。如果您要使用按钮的 ID,请确保您创建的每个按钮都有唯一的 ID(您当前为每个按钮使用相同的 ID),并为每个单独的点击事件使用唯一的 ID。我在填充所有数据后添加了单击事件,但是您正在等待从异步 ajax 调用返回的数据,因此您应该使用按钮动态添加事件。
猜你喜欢
  • 2020-01-06
  • 1970-01-01
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2014-08-29
相关资源
最近更新 更多