构建一个手风琴组件,该组件显示一个垂直堆叠的部分列表,每个部分都包含标题和内容片段。为您提供一些HTML,作为示例内容以及雪佛龙图标。
要求
- 默认情况下,所有部分都折叠并隐藏在视图中。
- 单击部分标题可切换内容。
- 如果部分崩溃,则将扩展部分并将显示内容。
- 如果扩展了部分,则该部分将折叠并隐藏内容。
- 这些部分彼此独立。
笔记
- 这个问题的重点是功能,而不是样式。不要花太多时间编写自定义CSS。 您可以修改标记(例如添加ID,数据属性,替换一些标签等),然后使用客户端渲染。
- 您可能想考虑改善应用程序的用户体验并实施它们的方法(在面试中您会获得奖励信用)。
解决方案
config / api设计< / strong>
建立组件的复杂性的一部分是为其设计API。手风琴函数接受根元素$rootEl
,其中将插入手风琴部分。手风琴函数的第二个参数是用于存储配置选项的对象。最低限度,我们将需要以下选项:
-
items
:项目对象列表。每个项目都是带有字段的对象:- 值:手风琴项目的唯一标识符。
- 标题:“要在手风琴标题中显示的文本标签。”
- 内容:何时展示该部分的内容。
与我们典型的香草JS方法不同,我们选择不将任何状态保留在JavaScript这次,而依靠DOM来跟踪每个手风琴部分的状态,并根据该手风琴的状态扩展/折叠适当的元素。
accordion
函数调用init
和attachEvents
函数来通过渲染dom元素和附加必要的事件侦听器来设置手风琴组件。
init
此功能设置了整个组件生命周期中保留的DOM元素,也就是它们将永远不会被破坏。手风琴的部分(标题和内容)被渲染。
attachEvents
在此组件(即单击事件)中,仅需要一个事件侦听器才能添加到根元素中。我们利用事件委托,因此只能添加一个事件的侦听器,并且将为其所有孩子内容工作。 但是,我们必须小心检查要单击哪个元素,并确保我们仅在单击标题而不是内容时响应。
在手风琴标题上触发单击时,我们需要旋转图标并切换手风琴内容上的隐藏属性。
测试用例
- 应显示所有提供的部分。
- 单击崩溃的部分的标题应扩展。
- 单击扩展的部分的标题应崩溃。
- 测试所有部分都可以独立扩展和崩溃。
- 测试您能够初始化组件的多个实例,每个实例都具有独立状态。
可访问性
交互式元素需要集中精力,因此我们将使用<button>
进行标题。
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div id="accordion" class="accordion"></div>
<!-- below is single item DOM example after runs accordion fn -->
<!--
<div>
<div>
HTML
<span
aria-hidden="true"
class="accordion-icon"></span>
</div>
<div>
The HyperText Markup Language or HTML is the
standard markup language for documents designed to
be displayed in a web browser.
</div>
</div>
-->
<script src="index.js"></script>
</body>
</html>
JS:
const data = {
sections: [
{
value: 'html',
title: 'HTML',
contents:
'The HyperText Markup Language or HTML is the standard markup language for documents designed to be displayed in a web browser.',
},
{
value: 'css',
title: 'CSS',
contents:
'Cascading Style Sheets is a style sheet language used for describing the presentation of a document written in a markup language such as HTML or XML.',
},
{
value: 'javascript',
title: 'JavaScript',
contents:
'JavaScript, often abbreviated as JS, is a programming language that is one of the core technologies of the World Wide Web, alongside HTML and CSS.',
},
],
};
(() => {
function accordion($rootEl, { sections }) {
function attachEvents() {
// Use Event Delegation.
$rootEl.addEventListener('click', (event) => {
const target = event.target;
if (
target.tagName !== 'BUTTON' ||
!target.classList.contains('accordion-item-title')
) {
return;
}
// Find the icon and toggle the direction.
const $icon = target.querySelector('.accordion-icon');
$icon.classList.toggle('accordion-icon--rotated');
//key point --->通过target.nextSibling找到内容,然后toggle内容的显示隐藏
const $accordionContents = target.nextSibling;
$accordionContents.hidden = !$accordionContents.hidden;
});
}
function init() {
const $accordionSections = document.createDocumentFragment();
sections.forEach(({ value, title, contents }) => {
const $accordionSection = document.createElement('div');
$accordionSection.classList.add('accordion-item');
const $accordionTitleBtn = document.createElement('button');
$accordionTitleBtn.classList.add('accordion-item-title');
$accordionTitleBtn.type = 'button';
$accordionTitleBtn.setAttribute('data-value', value);
const $accordionIcon = document.createElement('span');
$accordionIcon.classList.add('accordion-icon');
$accordionIcon.setAttribute('aria-hidden', 'true');
$accordionTitleBtn.append(title, $accordionIcon);
const $accordionSectionContents = document.createElement('div');
$accordionSectionContents.classList.add('accordion-item-contents');
$accordionSectionContents.hidden = true;
$accordionSectionContents.textContent = contents;
$accordionSection.append($accordionTitleBtn, $accordionSectionContents);
$accordionSections.append($accordionSection);
});
$rootEl.appendChild($accordionSections);
}
init();
attachEvents();
}
accordion(document.getElementById('accordion'), data);
})();
CSS:
.accordion {
display: flex;
flex-direction: column;
width: 100%;
}
.accordion-item {
display: flex;
flex-direction: column;
row-gap: 4px;
padding: 4px 0;
}
.accordion-item:not(:first-child) {
border-top: 1px solid #eee;
}
.accordion-item-title {
align-items: center;
border: none;
background: none;
cursor: pointer;
font-weight: 500;
padding: 4px;
justify-content: space-between;
text-align: start;
display: flex;
}
.accordion-item-title:hover {
background-color: #eee;
}
.accordion-icon {
border: solid currentcolor;
border-width: 0 2px 2px 0;
display: inline-block;
height: 8px;
pointer-events: none;
transform: translateY(-2px) rotate(45deg);
width: 8px;
}
.accordion-icon--rotated {
transform: translateY(2px) rotate(-135deg);
}
.accordion-item-contents {
font-size: 14px;
line-height: 1.2em;
padding: 4px;
}