基于 JSON 的联系人列表与详情页动态切换教程

4次阅读

基于 JSON 的联系人列表与详情页动态切换教程

本文介绍如何使用原生 javascript 实现从 json 文件加载联系人数据,并在页面中动态切换「姓名列表视图」与「单个联系人详情视图」,无需后端渲染或框架依赖。

本文介绍如何使用原生 javascript 实现从 json 文件加载联系人数据,并在页面中动态切换「姓名列表视图」与「单个联系人详情视图」,无需后端渲染或框架依赖。

在构建轻量级前端数据展示应用时,常需从静态 json 文件读取结构化数据并实现交互式视图切换。本教程以“联系人管理”为典型场景,完整演示:① 安全加载并解析 JSON 数据;② 渲染可点击的姓名列表;③ 点击后动态生成并插入详情面板;④ 点击关闭按钮销毁详情视图——全程采用现代原生 Web API(fetch、addEventlistener、Element.append()),避免全局变量污染与重复 dom 操作。

✅ 核心思路:数据绑定 + 事件委托

关键在于将原始数据直接绑定到 DOM 元素上,而非依赖索引或 ID 查找。在创建

  • 时,通过自定义属性 li.personData = person 将对应联系人对象挂载至元素实例。这样,在点击事件中可通过 e.target.personData 直接访问完整数据,彻底规避了闭包陷阱与作用域丢失问题(如原代码中 data 和 i 在 showDetails 中不可访问)。

    同时,采用事件委托策略:不在每个

  • 上单独绑定事件监听器,而是将监听器绑定到父级
      ,通过 e.target.nodeName === ‘LI’ 判断触发源。此举显著提升性能(尤其数据量大时),并天然支持后续动态增删列表项。

      ✅ 完整可运行代码示例

      以下为优化后的 HTML、JavaScript 与关键 CSS 片段(已剔除冗余样式,聚焦功能逻辑):

      <!DOCTYPE html> <html lang="zh-CN"> <head>   <meta charset="UTF-8">   <title>联系人管理</title>   <link rel="stylesheet" href="style.css"> </head> <body>   <header><h1>联系人管理</h1></header>   <main class="view"></main>   <script src="script.js"></script> </body> </html>
      // script.js fetch('data.json')   .then(res => {     if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);     return res.json();   })   .then(data => createListContact(data))   .catch(err => console.error('加载数据失败:', err));  function createListContact(data) {   const main = document.querySelector('main.view');   main.innerHTML = ''; // 清空旧内容    const ol = document.createElement('ol');   ol.classList.add('list-view');   ol.addEventListener('click', showDetails);    data.personen.forEach((person, index) => {     const li = document.createElement('li');     li.classList.add('button');     li.id = `person-${index}`;     li.textContent = `Name: ${person.nachname}, ${person.vorname}`;     li.personData = person; // ✅ 关键:数据直连 DOM     ol.appendChild(li);   });    main.appendChild(ol); }  function showDetails(e) {   if (e.target.nodeName !== 'LI') return;    const { personData } = e.target;   const details = document.createElement('div');   details.className = 'details';   details.id = 'contact-details';    const closeBtn = document.createElement('button');   closeBtn.className = 'close-btn';   closeBtn.textContent = '×';   closeBtn.type = 'button';   closeBtn.addEventListener('click', () => details.remove());    const infoEl = document.createElement('div');   infoEl.className = 'contact-info';   infoEl.innerHTML = `     <h2>${personData.vorname} ${personData.nachname}</h2>     <p><strong>电话:</strong>${personData.telefonnummer || '未提供'}</p>     <p><strong>邮箱:</strong>${personData.email || '未提供'}</p>   `;    details.append(closeBtn, infoEl);   document.body.appendChild(details); }
      /* style.css */ body {   font-family: "Segoe UI", system-ui, sans-serif;   background-color: #f5f5f5;   margin: 0;   padding: 20px; }  main.view {   max-width: 800px;   margin: 0 auto;   background: white;   border-radius: 8px;   box-shadow: 0 2px 10px rgba(0,0,0,0.1);   overflow: hidden; }  .list-view {   list-style: none;   padding: 0;   margin: 0; }  .list-view .button {   padding: 12px 20px;   margin: 4px;   background: #4a6fa5;   color: white;   border: none;   border-radius: 4px;   cursor: pointer;   font-size: 16px;   text-align: left;   transition: background 0.2s; }  .list-view .button:hover {   background: #3a5a80; }  .details {   position: fixed;   top: 50%;   left: 50%;   transform: translate(-50%, -50%);   background: white;   border-radius: 8px;   box-shadow: 0 10px 30px rgba(0,0,0,0.2);   width: 90%;   max-width: 500px;   z-index: 1000;   padding: 24px;   border: 1px solid #e0e0e0; }  .close-btn {   float: right;   background: none;   border: none;   font-size: 24px;   cursor: pointer;   color: #999;   padding: 0;   margin: -8px -8px 16px 0; }  .close-btn:hover {   color: #333; }  .contact-info h2 {   margin-top: 0;   color: #2c3e50; }  .contact-info p {   margin: 8px 0;   line-height: 1.5; }

      ⚠️ 注意事项与最佳实践

    • JSON 结构一致性:确保 data.json 中 personen 是数组,且每个对象包含 vorname、nachname、telefonnummer、email 字段(缺失字段建议用 || ‘未提供’ 防错)。
    • 错误处理必须显式添加:fetch 可能因网络、CORS 或 JSON 格式错误失败,务必用 .catch() 捕获并提示用户(示例中已包含基础错误日志)。
    • 内存与性能:details.remove() 比 parentNode.removeChild() 更简洁安全;避免在循环中频繁调用 document.body.appendChild(),应先构建完整 DOM 片段再插入。
    • 无障碍与体验:生产环境建议为 .button 添加 role=”button” 和键盘事件(Enter/Space 触发),并为模态框添加 aria-modal=”true” 和焦点管理。
    • 样式隔离:.details 使用 position: fixed 并设 z-index,确保覆盖其他内容;关闭按钮浮动右上角,符合用户直觉。

    通过以上实现,你获得了一个零依赖、高可维护、语义清晰的前端数据展示方案。它不仅解决了“视图切换”的核心问题,更体现了现代 Web 开发中数据驱动、事件委托与关注点分离的设计思想。

  • text=ZqhQzanResources