如何遍历值为数组的嵌套对象并实现手风琴式展示?
动态手风琴组件实现方案
需求说明
有一个嵌套对象,其键为动态名称(如“Product Details”“_General”),对应的值是属性数组。需要遍历该对象,将每个键作为手风琴组件的标题,同时遍历对应数组元素,把属性的label和value展示为手风琴内容。
示例数据:
{ "Product Details": [ {"attribute_code": "type2","label": "Variant / Model","value": "100 4jet Vario"} ], "_General": [ {"attribute_code": "designer","label": "Design by","value": "Hansgrohe"}, {"attribute_code": "delivery_time","label": "Delivery Time","value": "on stock"} ], "_Characteristics": [ {"attribute_code": "shape_outside","label": "Shape","value": "round"}, {"attribute_code": "water_heater","label": "Water heater","value": "suitable for instantaneous water heaters"} ], "_Color-Material-Surface": [ {"attribute_code": "material","label": "Material","value": "metal / plastic"} ] }
实现方案
1. 原生JavaScript实现
无需框架,直接用HTML+CSS+JS实现:
<!DOCTYPE html> <html> <head> <style> .accordion { width: 100%; max-width: 600px; margin: 20px auto; } .accordion-item { border: 1px solid #ddd; margin-bottom: 10px; } .accordion-header { background-color: #f5f5f5; padding: 15px; cursor: pointer; font-weight: bold; display: flex; justify-content: space-between; align-items: center; } .accordion-content { padding: 15px; display: none; } .accordion-content.active { display: block; } .attribute-item { margin-bottom: 8px; display: flex; justify-content: space-between; } .attribute-label { color: #555; } .attribute-value { font-weight: 500; } </style> </head> <body> <div class="accordion" id="productAccordion"></div> <script> const productData = { "Product Details": [{"attribute_code": "type2","label": "Variant / Model","value": "100 4jet Vario"}], "_General": [{"attribute_code": "designer","label": "Design by","value": "Hansgrohe"},{"attribute_code": "delivery_time","label": "Delivery Time","value": "on stock"}], "_Characteristics": [{"attribute_code": "shape_outside","label": "Shape","value": "round"},{"attribute_code": "water_heater","label": "Water heater","value": "suitable for instantaneous water heaters"}], "_Color-Material-Surface": [{"attribute_code": "material","label": "Material","value": "metal / plastic"}] }; const accordionContainer = document.getElementById('productAccordion'); Object.entries(productData).forEach(([title, attributes]) => { const accordionItem = document.createElement('div'); accordionItem.className = 'accordion-item'; const header = document.createElement('div'); header.className = 'accordion-header'; header.textContent = title.replace(/^_/, ''); header.innerHTML += ' <span>▼</span>'; const content = document.createElement('div'); content.className = 'accordion-content'; attributes.forEach(attr => { const attrItem = document.createElement('div'); attrItem.className = 'attribute-item'; attrItem.innerHTML = ` <span class="attribute-label">${attr.label}:</span> <span class="attribute-value">${attr.value}</span> `; content.appendChild(attrItem); }); header.addEventListener('click', () => { content.classList.toggle('active'); header.querySelector('span').textContent = content.classList.contains('active') ? '▲' : '▼'; }); accordionItem.appendChild(header); accordionItem.appendChild(content); accordionContainer.appendChild(accordionItem); }); </script> </body> </html>
2. Vue 3 实现
<template> <div class="accordion"> <div v-for="(attributes, title) in productData" :key="title" class="accordion-item"> <div class="accordion-header" @click="toggleAccordion(title)"> {{ title.replace(/^_/, '') }} <span>{{ activeAccordion === title ? '▲' : '▼' }}</span> </div> <div class="accordion-content" :class="{ active: activeAccordion === title }"> <div v-for="attr in attributes" :key="attr.attribute_code" class="attribute-item"> <span class="attribute-label">{{ attr.label }}:</span> <span class="attribute-value">{{ attr.value }}</span> </div> </div> </div> </div> </template> <script setup> import { ref } from 'vue'; const productData = ref({ "Product Details": [{"attribute_code": "type2","label": "Variant / Model","value": "100 4jet Vario"}], "_General": [{"attribute_code": "designer","label": "Design by","value": "Hansgrohe"},{"attribute_code": "delivery_time","label": "Delivery Time","value": "on stock"}], "_Characteristics": [{"attribute_code": "shape_outside","label": "Shape","value": "round"},{"attribute_code": "water_heater","label": "Water heater","value": "suitable for instantaneous water heaters"}], "_Color-Material-Surface": [{"attribute_code": "material","label": "Material","value": "metal / plastic"}] }); const activeAccordion = ref(null); const toggleAccordion = (title) => { activeAccordion.value = activeAccordion.value === title ? null : title; }; </script> <style scoped> .accordion { width: 100%; max-width: 600px; margin: 20px auto; } .accordion-item { border: 1px solid #ddd; margin-bottom: 10px; } .accordion-header { background-color: #f5f5f5; padding: 15px; cursor: pointer; font-weight: bold; display: flex; justify-content: space-between; align-items: center; } .accordion-content { padding: 15px; display: none; } .accordion-content.active { display: block; } .attribute-item { margin-bottom: 8px; display: flex; justify-content: space-between; } .attribute-label { color: #555; } .attribute-value { font-weight: 500; } </style>
3. React 实现
import { useState } from 'react'; const ProductAccordion = () => { const productData = { "Product Details": [{"attribute_code": "type2","label": "Variant / Model","value": "100 4jet Vario"}], "_General": [{"attribute_code": "designer","label": "Design by","value": "Hansgrohe"},{"attribute_code": "delivery_time","label": "Delivery Time","value": "on stock"}], "_Characteristics": [{"attribute_code": "shape_outside","label": "Shape","value": "round"},{"attribute_code": "water_heater","label": "Water heater","value": "suitable for instantaneous water heaters"}], "_Color-Material-Surface": [{"attribute_code": "material","label": "Material","value": "metal / plastic"}] }; const [activeTitle, setActiveTitle] = useState(null); const toggleAccordion = (title) => { setActiveTitle(activeTitle === title ? null : title); }; return ( <div className="accordion"> {Object.entries(productData).map(([title, attributes]) => ( <div key={title} className="accordion-item"> <div className="accordion-header" onClick={() => toggleAccordion(title)} > {title.replace(/^_/, '')} <span>{activeTitle === title ? '▲' : '▼'}</span> </div> <div className={`accordion-content ${activeTitle === title ? 'active' : ''}`} > {attributes.map(attr => ( <div key={attr.attribute_code} className="attribute-item"> <span className="attribute-label">{attr.label}:</span> <span className="attribute-value">{attr.value}</span> </div> ))} </div> </div> ))} </div> ); }; export default ProductAccordion;
对应CSS(单独文件引入):
.accordion { width: 100%; max-width: 600px; margin: 20px auto; } .accordion-item { border: 1px solid #ddd; margin-bottom: 10px; } .accordion-header { background-color: #f5f5f5; padding: 15px; cursor: pointer; font-weight: bold; display: flex; justify-content: space-between; align-items: center; } .accordion-content { padding: 15px; display: none; } .accordion-content.active { display: block; } .attribute-item { margin-bottom: 8px; display: flex; justify-content: space-between; } .attribute-label { color: #555; } .attribute-value { font-weight: 500; }
内容的提问来源于stack exchange,提问作者Mohammed Ismail
相关产品推荐
相关产品推荐

