I LOVE YOU, 唇噛んだ

ジャンル問わず色々つらつらと

No.908

どうも、去年くらいに家焼肉がしたくて焼肉用グリルを買っていたんですが、しみじみ買ってて良かったと思ったジョーです。こんにちは!
猛烈に「焼肉食べたいな」と思った時、スーパーで値引きされて1,200円くらいになっていた焼肉用牛カルビ&豚カルビのセットを買い、家でのんびりビール開けながら食べ、トータル1,500円くらいで焼肉晩酌が出来るから最高だな…と感じていました。笑



現在は "details "と "summary "タグを使用すればとっても簡単にアコーディオンが設置出来ますよね。
しかも "details "要素のname属性を付ければ、常に1つのアコーディオンしか開かない(開いていた奴は自動的に閉じる)排他的なアコーディオンの実装まで出来ちゃいます。簡単で素晴らしい~!
と言う訳でうちのサイトも最近色々弄った際に色々アコーディオンにしました。

アコーディオンの開閉アニメーションや挙動について更にJavaScriptを使っていたんですが、初めは "summary" 要素をクリックすると、対応する "accordion" 要素が滑らかに開閉するアニメーションだけを使っていました。

で、確認をしていると、新しい要素を開いて元々の開いていた要素を閉じる際、閉じる要素がはちゃめちゃに長文だとさっきまでスクロールで下りた分はそのままになってしまうので「え?私は何を見ていたんだっけ?」とページ内で迷子になってしまいました。
しょうがないかな~とも思ったんですが、やっぱストレス!!!新しくアコーディオンの要素を開いたらそっちの先頭に戻りたい!と思ったのでJavaScriptを更に追加することに。

調べても全然でてこなかったので、ChatGPTに何度も相談しトライ&エラーを重ねてやっと理想的な動きに辿り着いたので、折角なので残しておきます。コーダーさんでも何でもない奴なのでプロが見たらおかしいことがあるかもしれませんが、動いてるからええ!の勢いです(笑)
7/25からずっとやってました…😂絵を描いてても頭の片隅でずっと考えて気になっていたからやっと落ち着いて他の事に集中できる~。


▼やりたいこと
  • 新しいアコーディオンを開いて元々開いていた要素が閉じる時、元々開いていた要素の分上方向にスクロールする。
  • 既に開いていた要素が閉じる際にスクロール位置を調整する時、新しく開いたアコーディオンの先頭の、更に0.5remだけ上の位置に戻る。(余白なくビッタビタに上がるのがちょっと気持ち悪かったので…)
  • 新しいアコーディオンの中身の高さがページ下部の高さを超える場合は、画面(描画領域)の高さから中身の要素の画面(描画領域)に表示されている部分だけの高さを引いた分だけ上にスクロールするようにする。
  • 初めに開いたアコーディオンの場合、スクロール調整は行わない。



HTMLは上に書いてあることから順に読み込んでいきます。JavaScriptは読み込むのに時間がかかるので、HTMLの最下部に設置して下さいね。</body>の手前がベストです。
例えばこのJavaScriptを『accordion.js』という名前で保存し、自サーバーに上げた場合は↑のHTMLのように入れておけばOKです。

ついでに、現状だとname属性を付けて排他的アコーディオンを作っても全モダンブラウザで正常に動かないので、その辺も全部JavaScriptに入ってます。だから全モダンブラウザで排他的アコーディオンになってます。
↑のCodePenの結果で見るとあんまり正しく動いてない気がするんですが、うちのサイトのプロフィールの嗜好ページとかギャラリーのMPのとこに入れてる峰不二子とオトコ達リストページとか見ると理想的に動いているので多分大丈夫なはずです。

尚、私がアホなのでJavaScriptにコメントアウトを入れまくってますが、消して文章短くした方がオススメです。


document.addEventListener("DOMContentLoaded", () => {
  document.querySelectorAll(".details").forEach(function (el) {
    const summary = el.querySelector(".summary");
    const accordion = el.querySelector(".accordion");
    
    summary.addEventListener("click", (event) => {
      event.preventDefault();

      const previouslyOpen = document.querySelector(".details[open]");
      const accordionHeight = accordion.offsetHeight;

      if (el.getAttribute("open") !== null) {
        const closingAnim = accordion.animate(closingAnimation(accordion), animTiming);
        closingAnim.onfinish = () => {
          el.removeAttribute("open");
        };
      } else {
        if (!previouslyOpen) {
          el.setAttribute("open", "true");
          const openingAnim = accordion.animate(openingAnimation(accordion), animTiming);
        } else {
          if (previouslyOpen && previouslyOpen !== el) {
            const prevAccordion = previouslyOpen.querySelector(".accordion");
            const closingAnim = prevAccordion.animate(closingAnimation(prevAccordion), animTiming);
            closingAnim.onfinish = () => {
              previouslyOpen.removeAttribute("open");
              adjustScroll(el, accordionHeight);
            };
          } else {
            adjustScroll(el, accordionHeight);
          }
          el.setAttribute("open", "true");
          const openingAnim = accordion.animate(openingAnimation(accordion), animTiming);
        }
      }
    });
  });
});

const animTiming = {
  duration: 300,
  easing: "ease-in-out",
};

const closingAnimation = (accordion) => [
  {
    height: accordion.offsetHeight + "px",
    opacity: 1,
  },
  {
    height: 0,
    opacity: 0,
  },
];

const openingAnimation = (accordion) => [
  {
    height: 0,
    opacity: 0,
  },
  {
    height: accordion.offsetHeight + "px",
    opacity: 1,
  },
];

function adjustScroll(element, accordionHeight) {
  const summaryTop = element.querySelector(".summary").getBoundingClientRect().top + window.scrollY;
  const halfRem = parseFloat(getComputedStyle(document.documentElement).fontSize) * 0.5;
  const newScrollPosition = summaryTop - halfRem;
  const viewportHeight = window.innerHeight;

  if (accordionHeight + newScrollPosition > document.body.scrollHeight) {
    window.scrollTo({
      top: document.body.scrollHeight - viewportHeight,
      behavior: 'smooth'
    });
  } else {
    window.scrollTo({
      top: newScrollPosition,
      behavior: 'smooth'
    });
  }
}
畳む


こんな感じでJavaScript使って、てがろぐの続きを読むを開いて迷子になる問題も対処できないかな~。まぁやり始めると原稿スケジュール狂いそうなので、考えるとしてもスパーク終わってから考えるようにします。笑

Search this site

Archives