<main>
<h2>Section 1</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque commodo purus quis mi cursus hendrerit eu eu metus. Aliquam aliquam arcu eget aliquet scelerisque. Pellentesque sodales turpis vitae venenatis vehicula.</p>
<p>Ut id porta velit. Ut eu dignissim dui, quis gravida est. Cras quis venenatis mauris, a bibendum enim. Sed at augue libero. Nullam tortor metus, tincidunt ut urna id, posuere placerat orci. Ut quis risus dictum risus facilisis imperdiet quis sed eros.</p>
<h2>Section 2</h2>
<p>Nullam tortor metus, tincidunt ut urna id, posuere placerat orci.</p>
<p>Ut quis risus dictum risus facilisis imperdiet quis sed eros. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>Quisque commodo purus quis mi cursus hendrerit eu eu metus. Aliquam aliquam arcu eget aliquet scelerisque. Pellentesque sodales turpis vitae venenatis vehicula. Ut id porta velit. Ut eu dignissim dui, quis gravida est. Cras quis venenatis mauris, a bibendum enim. Sed at augue libero.</p>
</main>
body {
max-width: 40rem;
margin: 0 auto;
padding: 1em;
}
main {
border-width: 2px 0;
border-style: solid;
}
main h2 {
margin: 0;
}
main > div + h2 {
border-top: 2px solid;
}
h2 button {
all: inherit;
border: 0;
display: flex;
justify-content: space-between;
width: 100%;
padding: 0.5em 0;
}
h2 button:focus svg {
outline: 2px solid;
}
button svg {
height: 1em;
margin-left: 0.5em;
}
[aria-expanded="true"] .vert {
display: none;
}
[aria-expanded] rect {
fill: currentColor;
}
/* page styles */
html {
font-family: Arial, sans-serif;
}
* {
box-sizing: border-box;
}
(function() {
// Get all the <h2> headings
const headings = document.querySelectorAll('main h2')
Array.prototype.forEach.call(headings, heading => {
// Give each <h2> a toggle button child
// with the SVG plus/minus icon
heading.innerHTML = `
<button aria-expanded="false">
${heading.textContent}
<svg aria-hidden="true" focusable="false" viewBox="0 0 10 10">
<rect class="vert" height="8" width="2" y="1" x="4"/>
<rect height="2" width="8" y="4" x="1"/>
</svg>
</button>
`
// Function to create a node list
// of the content between this <h2> and the next
const getContent = (elem) => {
let elems = []
while (elem.nextElementSibling && elem.nextElementSibling.tagName !== 'H2') {
elems.push(elem.nextElementSibling)
elem = elem.nextElementSibling
}
// Delete the old versions of the content nodes
elems.forEach((node) => {
node.parentNode.removeChild(node)
})
return elems
}
// Assign the contents to be expanded/collapsed (array)
let contents = getContent(heading)
// Create a wrapper element for `contents` and hide it
let wrapper = document.createElement('div')
wrapper.hidden = true
// Add each element of `contents` to `wrapper`
contents.forEach(node => {
wrapper.appendChild(node)
})
// Add the wrapped content back into the DOM
// after the heading
heading.parentNode.insertBefore(wrapper, heading.nextElementSibling)
// Assign the button
let btn = heading.querySelector('button')
btn.onclick = () => {
// Cast the state as a boolean
let expanded = btn.getAttribute('aria-expanded') === 'true' || false
// Switch the state
btn.setAttribute('aria-expanded', !expanded)
// Switch the content's visibility
wrapper.hidden = expanded
}
})
})()
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.