• CSS & JS Effect – FAQ Accordion & Slide Down


    效果

    参考: 

    Youtube – Responsive FAQ accordion dropdown | HTML and CSS Tutorial

    几个难点

    1. 如何 align left for +- icon.

    2. 如何实现点击

    3. 如何实现 slide down

    一步一步做

    Semantic HTML

    <div class="container">
      <details>
        <summary>question 1</summary>
        <p>answer</p>
      </details>
    
      <details>
        <summary>question 2</summary>
        <p>answer</p>
      </details>
    </div> 

    Semantic 处理这种信息一般上是用 details > sumary + p

    效果

    默认就自带效果了, 但是这不是预期要的.

    HTML

    <div class="container">
      <details class="faq" open>
        <summary class="question">
          question 1<i class="plus-icon fa-solid fa-plus"></i
          ><i class="minus-icon fa-solid fa-minus"></i>
        </summary>
        <div class="answer">
          <p>answer</p>
        </div>
      </details>
    </div>

    加上了 plus / minus icon, 还有各个 class selector

    details 的 open attribtue 是为了要平仓掉默认的功能.

    完整版 HTML

    <div class="container">
      <details class="faq" open>
        <summary class="question">
          Q. Can CoolMan attend to my aircon issue at the time I want?<i
            class="plus-icon fa-solid fa-plus"
          ></i
          ><i class="minus-icon fa-solid fa-minus"></i>
        </summary>
        <div class="answer">
          <p>
            Yes, we can perform the repairing works at your desired timing, but
            note that our repair schedule varies depending on the job complexity
            and volumes. To avoid disappointment, we strongly recommend that you
            book an appointment with us through ONLINE BOOKING.
          </p>
        </div>
      </details>
      <details class="faq" open>
        <summary class="question">
          Q. What’s wrong with my aircon? It doesn’t turn on at all.<i
            class="plus-icon fa-solid fa-plus"
          ></i
          ><i class="minus-icon fa-solid fa-minus"></i>
        </summary>
        <div class="answer">
          <p>
            Check whether AC power gets to your aircon. If there’s no power,
            check the fuses or MCB circuit breakers. If there’s still no power,
            do contact our qualified technician to rectify the issue for you.
          </p>
        </div>
      </details>
    </div>
    View Code

    CSS Style

    .container {
      display: flex;
      flex-direction: column;
      gap: 2rem;
      width: 576px;
      margin-inline: auto;
    
      .faq {
        --answer-scroll-height: 0;
        cursor: pointer;
        .question {
          display: flex;
          justify-content: space-between;
          align-items: center;
          gap: 1rem;
    
          .minus-icon {
            display: none;
          }
        }
    
        .answer {
          height: 0;
          overflow: hidden;
          transition: height 0.4s;
        }
    
        &.opened {
          .question {
            .minus-icon {
              display: block;
            }
            .plus-icon {
              display: none;
            }
          }
          .answer {
            height: var(--answer-scroll-height);
          }
        }
      }
    }

    上面提到了 3 个难点

    1. 如何 align left for +- icon

    使用 Flex.

    还有一种做法是使用 :after 做出 icon, 然后绝对定位 rigth: 0, 在加上 padding-right 做空间. 但我觉得这种方法非常不直观. 还是用 Flex 好一些.

    2. 如何实现点击

    用 JS, 点击的时候添加一个 class opened, CSS 依赖这个 class 做效果

    还有一种做法是用 anchor + href # hash + :target 来实现 (上面参考的视频). 但是我个人觉得用 JS 更直观一些.

    3. 如何实现 slide down

    用 max-height, 但是会遇到 CSS 冷知识 – Transition 对 height: auto / fit-content 无效 的问题.

    解决方法是用 JS 获取 scroll height 然后通过 CSS variables 传进来,  (上面参考视频的方式是给一个大约的值, 这样做的好处是不需要 JS, 坏处就是不太准确)

    注: 如果用 JS 传 CSS variables 那就不需要用 max-height 了, 可以直接用 height.

    Javascript

    // 清除 HTML 默认 details 行为
    document.querySelectorAll("details").forEach((el) => {
      el.addEventListener("click", (e) => e.preventDefault());
    });
    
    const faqs = Array.from(document.querySelectorAll(".faq"));
    for (const faq of faqs) {
      const answer = faq.querySelector(".answer");
      faq.style.setProperty("--answer-scroll-height", answer.scrollHeight + "px"); // 注入每一个的 scroll height
    
      faq.addEventListener("click", () => {
        faqs.forEach((f) => f.classList.remove("opened")); // 关闭所有
        faq.classList.add("opened"); // 开启当前这一个
      });
    }

    补上一个场景难题: Flex item slide down

    中间这个 message 一开始是 hidden 的, 在 submit succeeded 后才 slide down.

    它一开始必须是 display:none 不然会导致 double gap

    因为即便 height: 0, visibility: hidden. element 依然是存在的, 那么就会有 gap

    所以必须用 display: none, 而 display: none 会导致无法获取到 scrollHeight.

    所以我们不可以像上面例子中那样, 提前去拿 scrollHeight, 而是要等到 display: block 了以后才获取 scrollHeight set CSS variable.

    CSS variable 的改变是会触发 transition 的. 不必担心. 类似这样:

    冷知识 – <details> 无法 set 不进 display: flex

    如果想在 question 和 answer 中制造间距, 就只能使用 margin-top 或 margin-bottom 了. 用不到 gap.

    不管是给 question margin-bottom 还是 answer margin-top. 关闭时看上去都很奇怪.

    中间大片空白就是 answer 的间距. 正确的做法应该是只有在开启的时候才给 margin-top.

  • 相关阅读:
    寄生组合式继承
    原型式继承
    js数学方法应用
    arguments.callee
    date日期比较和格式化方法
    转型函数 Boolean()
    javaScript高程第三版读书笔记
    《JavaScript dom 编程艺术》 placeholder占位符IE8兼容办法。
    正确设置网站title、keywords、description(转载)
    理解RESTful架构
  • 原文地址:https://www.cnblogs.com/keatkeat/p/15989492.html
Copyright © 2020-2023  润新知