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

如何用JavaScript实现含PHP标签的表单行动态增删?

解决动态添加/删除含PHP生成内容的表单行问题

核心思路

  • 让PHP一次性渲染出可复用的表单行模板(隐藏在页面中),避免JS硬写HTML导致重复维护PHP逻辑
  • 通过JS克隆模板生成新行,同时处理ID重复、事件绑定问题
  • 用事件委托处理新增行的交互,无需每次新增都重新绑定事件

步骤1:重构PHP代码,提取可复用模板

把表单行抽成独立模板文件,PHP只需渲染一次模板,JS直接复用该模板生成新行。

主页面代码修改

<?php 
if ($clientType === $slaClient) {
?>
<table id='dyntbl' class='table border text-nowrap text-md-nowrap table-striped mb-0'>
    <thead>
        <tr>
            <th class="text-center">Device Brand</th>
            <th class="text-center">Device Model</th>
            <th class="text-center">Serial Number</th>
            <th class="text-center" style="width:10%">SLA Device</th>
            <th><button type="button" class="btn text-success" data-name='add'><i class="fe fe-plus-circle" data-id='ad' style="font-size:1.6em;"></i></button></th>
        </tr>
    </thead>
    <tbody class="field_wrapper" id="table_body">
        <!-- 初始显示的第一行 -->
        <?php include 'device_row_template.php'; ?>
    </tbody>
</table>
<!-- 隐藏的模板,用于生成新行 -->
<div id="device_row_template" style="display:none;">
    <?php include 'device_row_template.php'; ?>
</div>
<?php } ?>

<?php
if ($clientType === $nonSla) {
?>
<table class='table border text-nowrap text-md-nowrap table-striped mb-0'>
    <thead>
        <tr>
            <th>Product Model Non-SLA</th>
            <th>Serial Number Non-SLA</th>
            <th>Device Status Non-SLA</th>
            <th><button type="button" class="btn text-success" data-name='add'><i class="fe fe-plus-circle" data-id='ad' style="font-size:1.6em;"></i></button></th>
        </tr>
    </thead>
    <tbody class="field_wrapper" id="table_body">
        <!-- 初始显示的第一行 -->
        <?php include 'non_sla_row_template.php'; ?>
    </tbody>
</table>
<!-- 隐藏的模板 -->
<div id="non_sla_row_template" style="display:none;">
    <?php include 'non_sla_row_template.php'; ?>
</div>
<?php } ?>

创建模板文件

device_row_template.php(SLA设备行模板)

<tr>
    <td>
        <select class="form-control form-select brand" data-bs-placeholder="Select" name="brand[]" required="" onchange="checkDeviceStatus()">
            <?php 
            $readALL = "SELECT * FROM productbrands WHERE deleted = 0";
            $displayAll = mysqli_query($conn,$readALL);
            while($rowFetchAll = mysqli_fetch_array($displayAll)){
                $brandName = $rowFetchAll['brandName'];
                $brandid = $rowFetchAll['brandID'];
            ?>
            <option value="<?=$brandid?>"><?=$brandName?></option>
            <?php } ?>
        </select>
    </td>
    <td>
        <select class="form-control form-select model" data-bs-placeholder="Select" name="model[]" required="" onchange="checkDeviceStatus()">
            <?php 
            $readALL1 = "SELECT * FROM productmodels WHERE deleted = 0";
            $displayAll1 = mysqli_query($conn,$readALL1);
            while($rowFetchAll1 = mysqli_fetch_array($displayAll1)){
                $modelName = $rowFetchAll1['modelName'];
                $modelid = $rowFetchAll1['modelID'];
            ?>
            <option value="<?=$modelid?>"><?=$modelName?></option>
            <?php } ?>
        </select>
    </td>
    <td><input type="text" name="serialNo[]" class="form-control serialNo" onchange="checkDeviceStatus()"></td>
    <td><input type="text" name="deviceLevel[]" class="form-control" readonly=""></td>
    <input type="text" name="client[]" value="<?=$clientID?>" hidden="" class="client">
    <td><button type="button" class="btn text-danger" data-name="del"><i class="fe fe-minus-circle" style="font-size:1.6em;"></i></button></td>
</tr>

non_sla_row_template.php(非SLA设备行模板)

<tr>
    <td><input type="text" name="non_sla_model[]" class="form-control"></td>
    <td><input type="text" name="non_sla_serial[]" class="form-control"></td>
    <td><input type="text" name="non_sla_status[]" class="form-control"></td>
    <td><button type="button" class="btn text-danger" data-name="del"><i class="fe fe-minus-circle" style="font-size:1.6em;"></i></button></td>
</tr>

关键修改点:

  • 移除所有元素的id属性(避免新增行ID重复导致JS选择器出错)
  • 将表单字段name改为数组格式(如brand[]),后端可通过$_POST['brand']获取所有行数据

步骤2:编写JavaScript逻辑

用事件委托处理添加/删除按钮,以及新增行的交互事件:

document.addEventListener('DOMContentLoaded', function() {
    const tableBody = document.getElementById('table_body');
    // 根据客户端类型获取对应模板
    const templateId = <?php echo ($clientType === $slaClient) ? "'device_row_template'" : "'non_sla_row_template'"; ?>;
    const template = document.getElementById(templateId).innerHTML;

    // 处理添加按钮点击
    document.addEventListener('click', function(e) {
        const addBtn = e.target.closest('[data-name="add"]');
        if (addBtn) {
            tableBody.insertAdjacentHTML('beforeend', template);
        }
    });

    // 处理删除按钮点击(至少保留一行)
    document.addEventListener('click', function(e) {
        const delBtn = e.target.closest('[data-name="del"]');
        if (delBtn) {
            const row = delBtn.closest('tr');
            if (tableBody.querySelectorAll('tr').length > 1) {
                row.remove();
            }
        }
    });

    // 事件委托处理checkDeviceStatus,避免新增行事件失效
    document.addEventListener('change', function(e) {
        const target = e.target;
        if (target.classList.contains('brand') || target.classList.contains('model') || target.classList.contains('serialNo')) {
            checkDeviceStatus(target);
        }
    });
});

// 重构checkDeviceStatus,通过传入元素定位当前行
function checkDeviceStatus(element) {
    const row = element.closest('tr');
    const brand = row.querySelector('.brand').value;
    const model = row.querySelector('.model').value;
    const serialNo = row.querySelector('.serialNo').value;
    const clientId = row.querySelector('.client').value;
    const deviceLevelInput = row.querySelector('[name="deviceLevel[]"]');

    // 此处放入你的AJAX逻辑,更新当前行的deviceLevel值
    // 示例:
    // fetch('/api/check-device-status', {
    //     method: 'POST',
    //     headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    //     body: `brand=${brand}&model=${model}&serialNo=${serialNo}&clientId=${clientId}`
    // }).then(res => res.json()).then(data => {
    //     deviceLevelInput.value = data.level;
    // });
}

关键注意事项

  1. ID唯一性:绝对不能给重复行的元素设置相同ID,改用class选择器定位
  2. 表单提交:所有需要提交的字段name必须用数组格式,确保后端能接收多行数据
  3. 事件绑定:始终用事件委托绑定到父元素或document,避免新增行的交互事件失效
  4. 模板复用:将PHP生成的行抽成独立模板,避免重复写数据库查询逻辑,降低维护成本

内容的提问来源于stack exchange,提问作者Cephas Armah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:57:23