
本教程详细介绍了如何利用纯javascript实现html下拉菜单与表格内容的动态联动。通过一个维修表单示例,我们将学习如何根据下拉菜单选择的模型,实时过滤并展示兼容的新旧零件数据。内容涵盖了数据结构定义、下拉菜单和表格的动态填充、事件监听、以及现代javascript的最佳实践,确保即使在本地环境也能高效运行。
在前端开发中,根据用户在下拉菜单中的选择动态更新页面内容是一种常见的交互需求。本教程将以一个维修表单为例,演示如何使用原生JavaScript实现这一功能,即当用户选择不同的产品型号时,两个相关零件表格(新零件和旧零件)能够实时显示与该型号兼容的零件列表。
1. HTML结构准备
首先,我们需要搭建基本的HTML骨架,包括表单元素、下拉菜单以及用于显示零件的表格。
<!DOCTYPE html>
<html>
<head>
<title>维修表单</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
form {
display: flex;
flex-direction: column;
gap: 15px; /* 使用gap代替br来控制间距 */
width: 300px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
background-color: #f9f9f9;
}
label {
font-weight: bold;
margin-bottom: 5px;
}
input[type="text"], select {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
width: calc(100% - 16px); /* 考虑padding */
}
h1, h2 {
color: #333;
margin-top: 20px;
margin-bottom: 10px;
}
.table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
background-color: #fff;
}
.table th, .table td {
border: 1px solid #ccc;
padding: 8px;
text-align: left;
}
.table th {
background-color: #eee;
font-weight: bold;
}
.green-button {
background-color: #4CAF50; /* 绿色 */
color: white;
padding: 10px 15px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.green-button:hover {
background-color: #45a049;
}
.red-button {
background-color: #f44336; /* 红色 */
color: white;
padding: 10px 15px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.red-button:hover {
background-color: #da190b;
}
</style>
</head>
<body onload="populateDropdowns()">
<h1>维修表单</h1>
<form onsubmit="return false;"> <!-- 阻止表单默认提交行为 -->
<label for="technician">技术员:</label>
<input type="text" id="technician" name="technician" required>
<label for="sku">SKU:</label>
<select id="sku" name="sku" required></select>
<label for="serial">序列号:</label>
<input type="text" id="serial" name="serial" required>
<label for="model">型号:</label>
<select id="model" name="model" required onchange="populateTables()">
<option value="">请选择型号</option> <!-- 添加默认选项 -->
</select>
<h2>新零件</h2>
<table class="table" id="new-parts-table"></table>
<h2>旧零件</h2>
<table class="table" id="salvaged-parts-table"></table>
<input type="submit" value="提交" id="submit-button" onclick="checkValue()">
</form>
</body>
</html>HTML结构关键点:
- onload="populateDropdowns()": 在页面加载完成后调用JavaScript函数来填充下拉菜单。
- onchange="populateTables()": 当“型号”下拉菜单的值改变时,调用JavaScript函数来更新表格内容。
- : 为型号下拉菜单添加一个默认的占位选项,提升用户体验。
- 表单的 onsubmit="return false;" 用于阻止默认的提交行为,因为我们通过JavaScript处理逻辑。
- 使用CSS的 gap 属性代替
标签来控制表单元素的间距,这是一种更现代和灵活的布局方式。
2. JavaScript核心逻辑
JavaScript部分将负责数据的管理、下拉菜单的填充、以及根据选择动态更新表格。
立即学习“Java免费学习笔记(深入)”;
<script>
// 使用let或const声明变量,推荐使用const对于固定数据,let对于可能改变的数据
const skuData = [
["SKU1", "Description 1", "Value 1"],
["SKU2", "Description 2", "Value 2"],
["SKU3", "Description 3", "Value 3"]
];
const modelData = [
["Brand 1", "Gen 1", "Model 1"],
["Brand 2", "Gen 2", "Model 2"],
["Brand 3", "Gen 3", "Model 3"]
];
const newPartsData = [
["Part 1", "10", "5", "Model 1,Model 2"],
["Part 2", "20", "3", "Model 1,Model 3"],
["Part 3", "30", "2", "Model 2,Model 3"]
];
const salvagePartsData = [
["Part 4", "15", "4", "Model 1,Model 3"],
["Part 5", "25", "6", "Model 2,Model 3"],
["Part 6", "35", "2", "Model 1,Model 2"]
];
/**
* 填充SKU和型号下拉菜单
*/
function populateDropdowns() {
const skuDropdown = document.getElementById('sku');
const modelDropdown = document.getElementById('model');
// 填充SKU下拉菜单
skuData.forEach(item => {
const option = document.createElement('option');
option.value = item[0];
option.textContent = `${item[1]} - ${item[2]}`;
skuDropdown.appendChild(option);
});
// 填充型号下拉菜单
// 关键修正:option.value 应该设置为型号名称 (item[2])
modelData.forEach(item => {
const option = document.createElement('option');
option.value = item[2]; // 将型号名称作为option的值
option.textContent = `${item[0]} - ${item[1]} - ${item[2]}`;
modelDropdown.appendChild(option);
});
// 页面加载时,如果modelDropdown有默认值(例如通过服务器渲染),则可以立即填充表格
// 如果没有,则在用户选择后populateTables()会被onchange触发
// 此处不再立即调用populateTables,因为我们添加了“请选择型号”的默认空选项
// populateTables();
}
/**
* 根据选择的型号填充新零件和旧零件表格
*/
function populateTables() {
const modelSelection = document.getElementById('model').value;
// 如果没有选择型号,则清空表格并返回
if (!modelSelection) {
document.getElementById('new-parts-table').innerHTML = "";
document.getElementById('salvaged-parts-table').innerHTML = "";
return;
}
// 填充新零件表格
const newPartsTable = document.getElementById('new-parts-table');
// 清空现有内容并添加表头
newPartsTable.innerHTML = "<thead><tr><th>零件</th><th>数量</th><th>选择</th></tr></thead><tbody></tbody>";
const newPartsTableBody = newPartsTable.querySelector('tbody');
newPartsData.forEach(item => {
// 检查零件是否兼容当前选择的型号
if (item[3].includes(modelSelection)) {
const row = document.createElement('tr');
const partCell = document.createElement('td');
partCell.textContent = item[0];
row.appendChild(partCell);
const quantityCell = document.createElement('td');
quantityCell.textContent = item[2]; // 假设item[2]是库存数量
row.appendChild(quantityCell);
const selectCell = document.createElement('td');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.name = 'new_parts[]';
checkbox.value = item[0];
// 添加事件监听器,以便在选择零件时更新总价值和按钮状态
checkbox.addEventListener('change', checkValue);
selectCell.appendChild(checkbox);
row.appendChild(selectCell);
newPartsTableBody.appendChild(row);
}
});
// 填充旧零件表格 (逻辑与新零件表格类似)
const salvagePartsTable = document.getElementById('salvaged-parts-table');
salvagePartsTable.innerHTML = "<thead><tr><th>零件</th><th>数量</th><th>选择</th></tr></thead><tbody></tbody>";
const salvagePartsTableBody = salvagePartsTable.querySelector('tbody');
salvagePartsData.forEach(item => {
if (item[3].includes(modelSelection)) {
const row = document.createElement('tr');
const partCell = document.createElement('td');
partCell.textContent = item[0];
row.appendChild(partCell);
const quantityCell = document.createElement('td');
quantityCell.textContent = item[2]; // 假设item[2]是库存数量
row.appendChild(quantityCell);
const selectCell = document.createElement('td');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.name = 'salvaged_parts[]';
checkbox.value = item[0];
checkbox.addEventListener('change', checkValue);
selectCell.appendChild(checkbox);
row.appendChild(selectCell);
salvagePartsTableBody.appendChild(row);
}
});
}
/**
* 检查选定零件的总价值并更新提交按钮状态
*/
function checkValue() {
// 将SKU的值从字符串转换为整数,并处理可能为空的情况
const skuDropdown = document.getElementById('sku');
const skuValue = parseInt(skuDropdown.options[skuDropdown.selectedIndex].text.split(' - ')[2]) || 0; // 假设SKU的值在描述的第三部分
const newParts = document.getElementsByName('new_parts[]');
const salvagedParts = document.getElementsByName('salvaged_parts[]');
let totalValue = 0;
// 计算新零件的总价值
for (let i = 0; i < newParts.length; i++) {
if (newParts[i].checked) {
const partName = newParts[i].value;
const part = newPartsData.find(item => item[0] === partName);
if (part) {
totalValue += parseInt(part[1]); // 假设item[1]是零件的价值
}
}
}
// 计算旧零件的总价值
for (let i = 0; i < salvagedParts.length; i++) {
if (salvagedParts[i].checked) {
const partName = salvagedParts[i].value;
const part = salvagePartsData.find(item => item[0] === partName);
if (part) {
totalValue += parseInt(part[1]); // 假设item[1]是零件的价值
}
}
}
const submitButton = document.getElementById('submit-button');
if (totalValue < skuValue) {
submitButton.classList.remove('red-button');
submitButton.classList.add('green-button');
submitButton.textContent = '通过';
} else {
submitButton.classList.remove('green-button');
submitButton.classList.add('red-button');
submitButton.textContent = '不通过';
}
}
</script>JavaScript关键点:
-
数据声明 (const / let):
- 现代JavaScript推荐使用 const 和 let 替代 var。
- const 用于声明常量,一旦赋值不能重新分配。
- let 用于声明变量,其值可以被重新分配。
- 对于 skuData, modelData, newPartsData, salvagePartsData 这些在运行时不改变的数据,使用 const 更为合适。
-
populateDropdowns() 函数:
- 型号下拉菜单的 option.value 修正: 原始代码将 item[0] (品牌) 作为型号下拉菜单的值,导致无法正确匹配零件。已修正为 option.value = item[2],确保下拉菜单的值是实际的“型号名称”(例如“Model 1”)。
- option.textContent 用于设置选项在下拉菜单中显示的内容。
- 为了更好的用户体验,不再在 populateDropdowns 内部直接调用 populateTables(),因为默认选项“请选择型号”应该为空。当用户真正选择一个型号后,onchange 事件会触发 populateTables()。
-
populateTables() 函数:
- 获取选定值: document.getElementById('model').value 获取当前选择的型号。
- 清空并重建表格: newPartsTable.innerHTML = "... "; 是一种有效清空表格并重新添加表头的方式。然后通过 newPartsTable.querySelector('tbody') 获取到 tbody 元素,将动态生成的行添加到其中。
- 数据过滤: 使用 forEach 遍历零件数据,并通过 item[3].includes(modelSelection) 判断当前零件是否兼容选定型号。item[3] 存储了兼容型号的字符串,如 "Model 1,Model 2"。
-
动态创建元素: 使用 document.createElement() 创建
, , , input 等HTML元素,并使用 appendChild() 将它们添加到DOM中。 - Checkbox 事件监听: 为每个动态生成的复选框添加 change 事件监听器,使其在状态改变时能实时调用 checkValue() 函数,更新提交按钮的状态。
checkValue() 函数:
- 此函数负责计算所有选定零件的总价值,并根据与SKU值的比较来更新提交按钮的样式和文本。
- 注意 skuValue 的获取方式,它从SKU下拉菜单的文本中解析出数值,这需要根据实际数据格式进行调整。
- findIndex 或 find 方法用于在零件数据数组中查找匹配的零件,以便获取其价值。
3. 注意事项与最佳实践
- 本地运行: 本教程中的代码完全是客户端JavaScript和HTML,可以直接在浏览器中打开HTML文件运行,无需部署到Web服务器。
- 数据源: 示例中数据是硬编码在JavaScript中的。在实际应用中,这些数据通常会通过AJAX请求从服务器API获取(例如JSON格式)。
- 变量声明: 始终优先使用 const 和 let。const 用于声明值不会改变的变量(如数据数组),let 用于声明值可能改变的变量(如循环计数器、动态元素引用)。
-
HTML语义化: 避免使用
进行布局,而是利用CSS的 margin, padding, flexbox 或 grid 等属性来控制元素间距和布局,使HTML结构更具语义性。 - 用户体验: 为下拉菜单添加一个默认的“请选择...”选项,可以引导用户操作,并避免在页面加载时意外显示数据。
- 性能优化: 对于大型数据集,频繁操作 innerHTML 可能会影响性能。更优化的方法是使用 DocumentFragment 批量构建DOM,或者使用虚拟DOM库(如React, Vue)。但对于本例中的小规模数据,直接操作 innerHTML 是可接受且简便的。
- 错误处理: 在实际应用中,应增加对数据解析(如 parseInt)和元素查找的错误处理,以提高代码的健壮性。
总结
通过本教程,我们学习了如何使用纯JavaScript和HTML实现一个动态的维修表单。核心在于理解如何通过下拉菜单的 onchange 事件触发JavaScript函数,该函数负责根据选定值过滤数据,并动态生成或更新HTML表格内容。同时,我们强调了现代JavaScript变量声明的最佳实践,以及如何通过CSS优化页面布局,以构建一个功能完善且用户友好的前端应用。










