|
在原生HTML折叠面板的基础上,通过CSS和JavaScript增加了动画效果,并动态计算每个部分的高度。如果用户禁用了JavaScript,界面也能保持正常功能。
效果演示
HTML
- <div class="wrapper">
- <section class="details-group">
- <details class="details" open>
- <summary class="details__summary">
- <details> and <summary>
- </summary>
- <div class="details__content">
- <p>The <details> and <summary> elements are used to allow common accordion-style functionality without relying on JavaScript.</p>
- </div>
- </details>
- <details class="details">
- <summary class="details__summary">Making it better</summary>
- <div class="details__content">
- <p>With a little help from JavaScript, we can supplement the default behavior to make an accordion that is super smooth and nicely-styled, but still degrades gracefully.</p>
- <p>This accordion uses JS to calculate heights so we can use CSS transitions without any max-height hacks. It also sets a timeout before removing the <code>open</code> attribute, so the content of the <code><details></code> element will stay visible until the height transition is finished.</p>
- <p>The cool part is that since we’re using these modern HTML5 elements, the accordion should work just fine even with JavaScript disabled. <strong><em>Gasp!</em> A simple web component in 2018 that doesn’t completely fail without JS?! Sound the alarm!</strong>
- </div>
- </details>
- <details class="details">
- <summary class="details__summary">Settings</summary>
- <div class="details__content">
- <ul>
- <li>
- <strong><code>speed</code></strong> <em>(default: <code>300</code>)</em> Speed of the transition in milliseconds. Setting this in the plugin will override the value specified in the CSS.
- </li>
- <li>
- <strong><code>one_visible</code></strong> <em>(default: <code>false</code>)</em> Determines whether details can be toggled independently, or if only one can be visible at a time.
- </li>
- </ul>
- </div>
- </details>
- <details class="details">
- <summary class="details__summary">Shameless Plug</summary>
- <div class="details__content">
- <p>If you liked this pen, check out <a target="_blank" href="https://keithpickering.github.io">my portfolio site</a>. I’m accepting new projects all the time, so drop me a line.</p>
- </div>
- </details>
- </section>
- </div>
复制代码- class Details {
- constructor(el, settings) {
- this.group = el;
- this.details = this.group.getElementsByClassName("details");
- this.toggles = this.group.getElementsByClassName("details__summary");
- this.contents = this.group.getElementsByClassName("details__content");
-
- // Set default settings if necessary
- this.settings = Object.assign({
- speed: 300,
- one_visible: false
- }, settings);
-
- // Setup inital positions
- for (let i = 0; i < this.details.length; i++) {
- const detail = this.details[i];
- const toggle = this.toggles[i];
- const content = this.contents[i];
-
- // Set transition-duration to match JS setting
- detail.style.transitionDuration = this.settings.speed + "ms";
-
- // Set initial height to transition from
- if (!detail.hasAttribute("open")) {
- detail.style.height = toggle.clientHeight + "px";
- } else {
- detail.style.height = toggle.clientHeight + content.clientHeight + "px";
- }
- }
-
- // Setup click handler
- this.group.addEventListener("click", (e) => {
- if (e.target.classList.contains("details__summary")) {
- e.preventDefault();
-
- let num = 0;
- for (let i = 0; i < this.toggles.length; i++) {
- if (this.toggles[i] === e.target) {
- num = i;
- break;
- }
- }
-
- if (!e.target.parentNode.hasAttribute("open")) {
- this.open(num);
- } else {
- this.close(num);
- }
- }
- });
- }
-
- open(i) {
- const detail = this.details[i];
- const toggle = this.toggles[i];
- const content = this.contents[i];
-
- // If applicable, hide all the other details first
- if (this.settings.one_visible) {
- for (let a = 0; a < this.toggles.length; a++) {
- if (i !== a) this.close(a);
- }
- }
-
- // Update class
- detail.classList.remove("is-closing");
-
- // Get height of toggle
- const toggle_height = toggle.clientHeight;
-
- // Momentarily show the contents just to get the height
- detail.setAttribute("open", true);
- const content_height = content.clientHeight;
- detail.removeAttribute("open");
-
- // Set the correct height and let CSS transition it
- detail.style.height = toggle_height + content_height + "px";
-
- // Finally set the open attr
- detail.setAttribute("open", true);
- }
-
- close(i) {
- const detail = this.details[i];
- const toggle = this.toggles[i];
-
- // Update class
- detail.classList.add("is-closing");
-
- // Get height of toggle
- const toggle_height = toggle.clientHeight;
-
- // Set the height so only the toggle is visible
- detail.style.height = toggle_height + "px";
-
- setTimeout(() => {
- // Check if still closing
- if (detail.classList.contains("is-closing"))
- detail.removeAttribute("open");
- detail.classList.remove("is-closing");
- }, this.settings.speed);
- }
- }
- (() => {
- const els = document.getElementsByClassName("details-group");
-
- for (let i = 0; i < els.length; i++) {
- const details = new Details(els[i], {
- speed: 500,
- one_visible: true
- });
- }
- })();
复制代码
|
|