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

为何insertRow方法添加空白行?fetch请求数据延迟问题排查

问题解决:Fetch请求结果延迟渲染导致表格空白行

问题描述

尝试将Fetch请求返回的数据添加到表格新行,但第一次点击按钮时会插入空白行,目标数据需等到下一次点击才会显示。该问题还导致total()函数报错,因为首个单元格值为undefined。直接添加字符串时新行可正常生成,推测问题与变量或Fetch请求特性相关,尝试修改数据类型、调整表格插入位置、修正语法均无效。

原代码

JavaScript代码

document.getElementById('getFood').addEventListener('click', getFood);
document.getElementById('getFood').addEventListener('click', total);
/*function getText() {
  $.ajax({
    method: 'GET',
    url:
      'https://api.api-ninjas.com/v1/nutrition?query=' +
      document.getElementById('foodInput'),
    headers: { 'X-Api-Key': 'vrtwcc/pVgAr2o/a4dEyYA==hR1m7lLVdU4ho4hW' },
    contentType: 'application/json',
    success: function (result) {
      console.log(result);
    },
    error: function ajaxError(jqXHR) {
      console.error('Error: ', jqXHR.responseText);
    },
  });
}*/
/*function getFood(foodName) {
  let xhr = new XMLHttpRequest();
  xhr.open(
    'get',
    'https://api.api-ninjas.com/v1/nutrition?query=' + foodName,
    true
  );

  xhr.send();
  xhr.addEventListener('load', function () {
    console.log(xhr.responseText);
  });
}
getFood('fries');*/
function getFood() {
  fetch(
    'https://api.api-ninjas.com/v1/nutrition?query=' +
      `${document.getElementById('foodInput').value}`,
    {
      method: 'GET',
      headers: { 'X-Api-Key': 'vrtwcc/pVgAr2o/a4dEyYA==hR1m7lLVdU4ho4hW' },
      contentType: 'application/json',
    }
  )
    .then((res) => res.json())
    .then((data) => {
      foodQuery = data[0].name;
      calorieQuery = `${data[0].calories} calories`;
    });
  let table = document
    .getElementById('foodTable')
    .getElementsByTagName('tbody')[0];
  let row = table.insertRow(0);
  var cell1 = row.insertCell(0);
  var cell2 = row.insertCell(1);
  cell1.innerHTML = foodQuery;
  cell2.innerHTML = calorieQuery;

  // row.insertCell(0).innerHTML = foodQuery;
  // row.insertCell(1).innerHTML = calorieQuery;
  //document.getElementById('foodInput').value = '';
}
function total() {
  let table = document.getElementById('foodTable');
  let total = 0;
  for (let i = 1; i < table.rows.length; i++) {
    total += Number(table.rows[i].cells[2].innerText);
  }
  document.getElementById('tableTotal').value = total;
}

HTML代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script
      src="https://code.jquery.com/jquery-3.6.3.js"
      integrity="sha256-nQLuAZGRRcILA+6dMBOvcRh5Pe310sBpanc6+QBmyVM="
      crossorigin="anonymous"
    ></script>
    <link
      rel="stylesheet"
      href="node_modules/bootstrap/dist/css/bootstrap.css"
    />

    <link rel="stylesheet" href="style.css" />

    <title>Document</title>
  </head>
  <body>
    <div class="container-fluid border border-primary">
      <strong>Jason's Calorie Counter </strong><br />
      <form>
        <div class="form-group" id="">
          <label>Food Selection</label>
          <input
            class="form-control"
            id="foodInput"
            placeholder="Enter food here"
          />
          <small id="foodHelp" class="form-text text-muted"
            >Nutrition data for each food item is scaled to 100g unless a
            quantity is specified
          </small>
          <br />
          <button type="button" class="btn btn-primary" id="getFood">
            Get Food
          </button>
        </div>
      </form>
    </div>
    <table class="table table-bordered" id="foodTable">
      <thead>
        <tr>
          <th scope="col">Food</th>
          <th scope="col">Calories</th>
        </tr>
      </thead>
      <tbody>
        <tr id="rows1">
          <td colspan="2" id="tableTotal">Total</td>
        </tr>
      </tbody>
    </table>

    <script
      src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js"
      integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
      crossorigin="anonymous"
    ></script>
    <script src="script.js"></script>
  </body>
</html>

问题根源

  1. 异步操作顺序错误:Fetch是异步操作,原代码中表格插入逻辑写在then回调之外,浏览器会先执行插入行的代码,此时foodQuery和calorieQuery还未被API数据赋值(甚至是未定义的全局变量),因此插入空白行;API请求完成后变量才被赋值,下次点击时才会用上本次数据。
  2. 总计函数逻辑错误:表格仅两列,但代码尝试访问cells[2](索引从0开始,对应第三列),会获取到undefined导致转换数字报错;同时遍历行时包含了总计行,计算逻辑混乱。

修正后的代码

JavaScript代码

document.getElementById('getFood').addEventListener('click', handleFoodSubmit);

// 合并逻辑,确保先获取数据再更新表格和计算总计
function handleFoodSubmit() {
  const foodInput = document.getElementById('foodInput').value.trim();
  if (!foodInput) return; // 空输入直接返回

  fetch(
    `https://api.api-ninjas.com/v1/nutrition?query=${foodInput}`,
    {
      method: 'GET',
      headers: { 'X-Api-Key': 'vrtwcc/pVgAr2o/a4dEyYA==hR1m7lLVdU4ho4hW' },
    }
  )
  .then((res) => {
    if (!res.ok) throw new Error('请求失败');
    return res.json();
  })
  .then((data) => {
    if (!data.length) throw new Error('未找到对应食物数据');
    
    const foodName = data[0].name;
    const calories = data[0].calories;
    
    // 插入表格行(放在then里,确保拿到数据后再操作)
    const tableBody = document.getElementById('foodTable').querySelector('tbody');
    // 插入到总计行之前
    const row = tableBody.insertRow(tableBody.rows.length - 1);
    const cell1 = row.insertCell(0);
    const cell2 = row.insertCell(1);
    cell1.textContent = foodName;
    cell2.textContent = `${calories} calories`;

    // 清空输入框
    document.getElementById('foodInput').value = '';

    // 计算总计
    calculateTotal();
  })
  .catch((err) => {
    console.error(err.message);
    alert(err.message);
  });
}

function calculateTotal() {
  const tableBody = document.getElementById('foodTable').querySelector('tbody');
  let total = 0;
  // 遍历除了最后一行(总计行)的所有行
  for (let i = 0; i < tableBody.rows.length - 1; i++) {
    const calorieText = tableBody.rows[i].cells[1].textContent;
    // 提取数字部分
    const calories = parseInt(calorieText.split(' ')[0], 10);
    if (!isNaN(calories)) {
      total += calories;
    }
  }
  // 更新总计行内容
  document.getElementById('tableTotal').textContent = `Total: ${total} calories`;
}

HTML代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://code.jquery.com/jquery-3.6.3.js" integrity="sha256-nQLuAZGRRcILA+6dMBOvcRh5Pe310sBpanc6+QBmyVM=" crossorigin="anonymous"></script>
  <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css" />
  <link rel="stylesheet" href="style.css" />
  <title>Calorie Counter</title>
</head>
<body>
  <div class="container-fluid border border-primary">
    <strong>Jason's Calorie Counter </strong><br />
    <form>
      <div class="form-group">
        <label>Food Selection</label>
        <input class="form-control" id="foodInput" placeholder="Enter food here" />
        <small id="foodHelp" class="form-text text-muted">
          Nutrition data for each food item is scaled to 100g unless a quantity is specified
        </small>
        <br />
        <button type="button" class="btn btn-primary" id="getFood">Get Food</button>
      </div>
    </form>
  </div>
  <table class="table table-bordered" id="foodTable">
    <thead>
      <tr>
        <th scope="col">Food</th>
        <th scope="col">Calories</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td colspan="2" id="tableTotal">Total: 0 calories</td>
      </tr>
    </tbody>
  </table>

  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  <script src="script.js"></script>
</body>
</html>

关键修改点

  • 将表格插入逻辑移到Fetch的then回调内部,确保拿到API数据后再更新表格
  • 合并按钮点击事件,按"获取数据→更新表格→计算总计"的顺序执行,避免异步顺序问题
  • 修复calculateTotal函数的列索引错误,改为访问cells[1](卡路里列),并提取数字部分计算
  • 遍历表格行时排除总计行,避免重复计算
  • 添加输入验证和错误处理,防止空输入或API请求失败的情况
  • 使用textContent替代innerHTML,提升安全性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 09:27:41