You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bootstrap 5响应式DataTable展开行后删除/编辑按钮失效求助

响应式DataTable移动端展开行后编辑/删除按钮失效问题
  • 基于Bootstrap 5搭建的响应式DataTable,在桌面端未展开行状态下,编辑、删除按钮均可正常触发;
  • 切换到移动端屏幕,点击+展开行后,按钮完全无法触发点击事件;
  • 非专业开发者,通过Google Script制作页面,需解决该问题。

问题代码

/* Prevent forms from submitting. */
function preventFormSubmit() {
  var forms = document.querySelectorAll('form');
  for (var i = 0; i < forms.length; i++) {
    forms[i].addEventListener('submit', function(event) {
      event.preventDefault();
    });
  }
}

window.addEventListener("load", functionInit, true); 

/**
 * INITIALIZE FUNCTIONS ONLOAD
 * */
function functionInit(){  
  preventFormSubmit();
  getLastTenRows();
};  

/**
 * HANDLE FORM SUBMISSION
 * */
function handleFormSubmit(formObject) {
  google.script.run.withSuccessHandler(createTable).processForm(formObject);
  document.getElementById("message").innerHTML = "<div class='alert alert-warning' role='alert'>Added the record successfully.</div>";
  document.getElementById("myForm").reset();
}

/**
 * Clear form when pop-up is closed.  
 * */
function clearForm() {
  document.getElementById("message").innerHTML = "";
  document.getElementById("myForm").reset();
}

google.script.run.withSuccessHandler(createTable).getAllData();
document.getElementById('resp-spinner5').classList.remove("d-none");
document.getElementById('resp-spinner6').classList.remove("d-none");
document.getElementById('resp-spinner7').classList.remove("d-none");
document.getElementById('resp-spinner8').classList.remove("d-none");
document.getElementById('resp-spinner9').classList.remove("d-none");
document.getElementById('resp-spinner10').classList.remove("d-none");

function createTable(dataArray) {
  $('#btn-close').click()
  $(document).ready(function(){
    document.getElementById('resp-spinner5').classList.add("d-none");
    document.getElementById('resp-spinner6').classList.add("d-none");
    document.getElementById('resp-spinner7').classList.add("d-none");
    document.getElementById('resp-spinner8').classList.add("d-none");
    document.getElementById('resp-spinner9').classList.add("d-none");
    document.getElementById('resp-spinner10').classList.add("d-none");  
    $('#dataTable').DataTable({
      data: dataArray.slice(1),
      columns: [
        { title: "ID" },
        { title: "Name" },
        { title: "State" }, 
        { title: "Date" }, 
        { title: "Mobile No" },
        { title: "Address" },
        { title: "Remarks" },
        { 
          title: "Edit", 
          "render": function (data,type, row, meta){
            return "<button type='button' class='btn btn-outline-warning btn-xs editBtn' data-toggle='modal' data-target='#myModal' onclick='editData(this);'><i class='fa fa-edit'></i></button>";
          }
        },
        { 
          title: "Delete", 
          "render": function (data,type){
            return "<button type='button' class='btn btn-outline-danger btn-xs deleteBtn' onclick='deleteData(this);'><i class='fa fa-trash''></i></button>";
          }
        },
      ],
      "fnRowCallback": function(nRow, aData, iDisplayIndex) {
        nRow.setAttribute('id',aData[0]);
      },
      destroy:true, 
      responsive:true,     
      lengthMenu: [
        [5, 10, 25, 50, 100, -1 ],
        ['5', '10', '25', '50','100', 'All' ]
      ],
      order: [[2, "asc"], [2, "asc"]],
    });
  });
}

/**
 * DELETE DATA
 * */
function deleteData(el) {
  Swal.fire({
    position:'top',    
    title: 'Are you sure?',
    text: "You won't be able to revert this!",
    icon: 'warning',
    showCancelButton: true,
    confirmButtonColor: '#3085d6',
    cancelButtonColor: '#d33',
    cancelButtonText: 'Cencel',
    confirmButtonText: 'Ok'
  }).then((result) => {
    if (result.isConfirmed) {
      Swal.fire('Deleted!','Your file has been deleted.')
    }
    if (result.isDismissed) {
      swal.fire("Cancelled!", "Files not deleted!", "warning");
      console.log("Doing Nothing")
      var recordId = el.parentNode.parentNode.cells[0].innerHTML;
      google.script.run.withSuccessHandler(createTable).deleteData(recordId);
    }
  });
}

/** 
 * EDIT DATA
 * */
function editData(el){
  var recordId = el.parentNode.parentNode.cells[0].innerHTML; 
  google.script.run.withSuccessHandler(populateForm).getRecordById(recordId);
}

/** 
 * POPULATE FORM
 * */
function populateForm(records){
  document.getElementById('RecId').value = records[0][0];
  document.getElementById('name').value = records[0][1];
  document.getElementById('State').value = records[0][2];
  document.getElementById('date').value = records[0][3];
  document.getElementById('data1').value = records[0][4];
  document.getElementById('data2').value = records[0][5];
  document.getElementById('data3').value = records[0][6];
  document.getElementById("message").innerHTML = "<div class='alert alert-warning' role='alert'>Update Record [ID: "+records[0][0]+"]</div>";
}

/** 
 * RETRIVE DATA FROM GOOGLE SHEET FOR State DROPDOWN
 * */ 
function createStateDropdown() {
  google.script.run.withSuccessHandler(StateDropDown).getDropdownList("State!A1:A4"); 
}

/**
 * POPULATE State DROPDOWNS
 * */
function StateDropDown(values) { 
  var list = document.getElementById('State');   
  for (var i = 0; i < values.length; i++) {
    var option = document.createElement("option");
    option.value = values[i];
    option.text = values[i];
    list.appendChild(option);
  }
}

function loading(){
  window.addEventListener("load", functionInit, false);
  window.addEventListener("load", preventFormSubmitSearch, true);
}

解决方案

1. 核心问题分析

DataTable响应式展开的行是动态生成的DOM元素,原代码用onclick绑定事件仅对初始渲染的DOM生效;同时展开行后DOM层级改变,el.parentNode.parentNode.cells[0]无法正确获取ID。

2. 具体修改步骤

步骤1:修改按钮渲染逻辑,移除onclick,添加数据ID

在createTable的列配置中,给按钮添加data-id存储行ID,去掉onclick:

// Edit列修改
{ 
  title: "Edit", 
  "render": function (data,type, row, meta){
    return "<button type='button' class='btn btn-outline-warning btn-xs editBtn' data-toggle='modal' data-target='#myModal' data-id='"+row[0]+"'><i class='fa fa-edit'></i></button>";
  }
},
// Delete列修改
{ 
  title: "Delete", 
  "render": function (data,type, row){
    return "<button type='button' class='btn btn-outline-danger btn-xs deleteBtn' data-id='"+row[0]+"'><i class='fa fa-trash'></i></button>";
  }
},

步骤2:用事件委托绑定动态元素的点击事件

在createTable函数中,DataTable初始化完成后添加事件委托(替换原editData和deleteData函数):

function createTable(dataArray) {
  $('#btn-close').click()
  $(document).ready(function(){
    // ... 原隐藏spinner代码不变 ...
    $('#dataTable').DataTable({/* ... 原配置不变 ... */});

    // 编辑按钮事件委托
    $('#dataTable').on('click', '.editBtn', function(){
      var recordId = $(this).data('id');
      google.script.run.withSuccessHandler(populateForm).getRecordById(recordId);
    });

    // 删除按钮事件委托
    $('#dataTable').on('click', '.deleteBtn', function(){
      var recordId = $(this).data('id');
      Swal.fire({
        position:'top',    
        title: 'Are you sure?',
        text: "You won't be able to revert this!",
        icon: 'warning',
        showCancelButton: true,
        confirmButtonColor: '#3085d6',
        cancelButtonColor: '#d33',
        cancelButtonText: 'Cancel',
        confirmButtonText: 'Ok'
      }).then((result) => {
        if (result.isConfirmed) {
          Swal.fire('Deleted!', 'Your file has been deleted.', 'success');
          google.script.run.withSuccessHandler(createTable).deleteData(recordId);
        } else if (result.isDismissed) {
          Swal.fire("Cancelled!", "Files not deleted!", "warning");
        }
      });
    });
  });
}

注:修复了原删除函数中,删除请求写在取消分支的逻辑错误

步骤3:修复脚本加载顺序和冗余代码

  • 将jQuery脚本放在所有自定义代码最前面:
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <!-- 其他依赖(DataTable、Bootstrap、SweetAlert等)也需放在自定义脚本前 -->
    <script>/* 自定义JS代码 */</script>
    
  • 删除冗余的重复初始化代码:
    // 删掉这段无效代码
    $('#datatable').DataTable().columns.adjust().responsive.recalc();
    

内容的提问来源于stack exchange,提问作者Yurii Holovko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 11:56:36