details > summary の開閉アニメーション

details[open] > summary にて、アニメーションでコンテンツを開く CSS を作成した。default-details-summary.css および details-summary-animation.css を HTML に読み込めばよい。

default-details-summary.css
@keyframes details-vertically-open {
  0% {
    opacity: 0;
    max-height: 0;
  }
  100% {
    opacity: 1;
    max-height: 100vh;
  }
}
@keyframes details-horizontally-open {
  0% {
    opacity: 0;
    max-width: 0;
  }
  100% {
    opacity: 1;
    max-width: 50vw;
  }
}
@keyframes details-diagonally-open {
  0% {
    opacity: 0;
    max-height: 0;
    max-width: 0;
  }
  100% {
    opacity: 1;
    max-height: 100vh;
    max-width: 50vw;
  }
}
details.marker-none	> summary::-webkit-details-marker { display: none; }
details.marker-none	> summary::marker { content: none; }
details.marker-none	> summary { display: block; }	/* for SeaMonkey */
	:
details-summary-animation.css
details:not(.horizontally):not(.diagonally)[open]:not(.noanimation) > summary ~ * {
  animation: details-vertically-open 1.6s ease;
}
details.horizontally[open]:not(.noanimation) > summary ~ * {
  animation: details-horizontally-open 1.6s ease;
}
details.diagonally[open]:not(.noanimation) > summary ~ * {
  animation: details-diagonally-open 1.6s ease;
}

details は垂直方向にアニメーションを行う。details.horizontally は水平方向にアニメーションを行う。details.diagonally は水平垂直方向にアニメーションを行う。人間の視覚の応答速度は 8 ms だそうで、その倍数になるようにアニメーションの時間間隔を定めている。

この欠点は、一つ、Safari で初回しかアニメーションをしない。二つ、閉じるときのアニメーションはどうも定義できないようである。

よって、details-summary-animation.css の代わり(読み込んではいけない)に、details-summary-animation.js を読み込めばこれらの欠点を克服するように制作しておいた。

details-summary-animation.js [2021/08/24修正]
<script src="../../common/local/cubic_equation.js"></script>
<script src="../../common/local/animation-timing-functions.js"></script>
	:
window.addEventListener('load', function() {
  const js_animation = true, css_compatible = !true;
  for (const summary of document.body.querySelectorAll('details:not(.noanimation) > summary')) {
    const details = summary.parentNode;
    const elements = Array.from(details.querySelectorAll('details:not(.noanimation) > *:not(summary)'));
    summary.addEventListener('click', function(event) {
      event.stopPropagation();
      details.open = !details.open;
      const intervallers = { open: null, close: null };
      summary.addEventListener('click', function(event) {
	event.stopPropagation();
	if (intervallers['open']) {
	  clearInterval(intervallers['open']);
	  intervallers['open'] = null;
	}
	if (intervallers['close']) {
	  clearInterval(intervallers['close']);
	  intervallers['close'] = null;
	  details.open = false;
	}
      });
      const open = details.open;
      const timeout = open ? 1600 : 400, interval = open ? 8 : 40, duration = timeout/interval;
      if (js_animation && (!css_compatible || open)) {
	const opacity = 1., width = open ? 50 : 25, height = open ? 100 : 50;
	const f = open ?
		  animation_timing_functions.ease/*linear*//*ease_in*//*ease_out*//*ease_in_out*/ :
		  animation_timing_functions./*ease*//*linear*//*ease_in*//*ease_out*/ease_in_out
		  /*steps_jump_end(duration)*/;
	let count = 0, val = f((open ? count : duration - count)/duration);
	elements.map(v=>v.style.opacity = `${(val < 0) ? 0 : (opacity*val).toFixed(6)}`);
	if (details.classList.contains('horizontally') || details.classList.contains('diagonally'))
	  elements.map(v=>v.style.maxWidth = `${(val < 0) ? 0 : (width*val).toFixed(3)}vw`);
	if (!details.classList.contains('horizontally') || details.classList.contains('diagonally'))
	  elements.map(v=>v.style.maxHeight = `${(val < 0) ? 0 : (height*val).toFixed(3)}vh`);
	const start_time = performance.now();
	intervallers[open ? 'open' : 'close'] = setInterval(function() {
	  const watch_time = performance.now();
	  const t = (watch_time - start_time)/timeout;
	  count = /*count + 1*/Math.round((watch_time - start_time)/interval);
	  val = f(/*(open ? count : duration - count)/duration*/open ? t : 1. - t);
	  if (open) details.open = true;
	  if (!(count < duration)) {
	    clearInterval(intervallers[open ? 'open' : 'close']);
	    intervallers[open ? 'open' : 'close'] = null;
	    if (!open) details.open = false;
	  }
	  elements.map(v=>v.style.opacity = (count < duration) ? `${(val < 0) ? 0 : (opacity*val).toFixed(6)}` : null);
	  if (details.classList.contains('horizontally') || details.classList.contains('diagonally'))
	    elements.map(v=>v.style.maxWidth = (count < duration) ? `${(val < 0) ? 0 : (width*val).toFixed(3)}vw` : null);
	  if (!details.classList.contains('horizontally') || details.classList.contains('diagonally'))
	    elements.map(v=>v.style.maxHeight = (count < duration) ? `${(val < 0) ? 0 : (height*val).toFixed(3)}vh` : null);
	}, interval);
      }
      else
	details.open = !details.open;
    });
  }
});

この様に、従前と違って、Safari でも初回のみでなく同様に動作する。ブラウザのバグが治るまでの代替として有用だろう。

Cozy lummox gives smart squid who asks for job pen.

details>summarydetails.marker-none
details>summarydetails.marker-none.before-arrow
details>summarydetails.marker-none.before-two_headed_arrow
details>summarydetails.marker-none.before-arrow_from_bar
details>summarydetails.marker-none.before-arrow_with_tip
details>summarydetails.marker-none.before-reversed_arrow_with_tip
details>summarydetails.marker-none.before-paired_arrows
details>summarydetails.marker-none.before-double_arrow
details>summarydetails.marker-none.before-dashed_arrow
details>summarydetails.marker-none.before-white_arrow
details>summarydetails.marker-none.before-black_pointing_triangle
details>summarydetails.marker-none.before-white_pointing_triangle
details>summarydetails.marker-none.before-black_pointing_small_triangle
details>summarydetails.marker-none.before-white_pointing_small_triangle
details>summarydetails.marker-none.before-white_pointing_index
details>summarydetails.marker-none.before-heavy_black_heart
details>summarydetails.marker-none.before-floral_heart
details>summarydetails.marker-none.before-arrow_with_stroke
details>summarydetails.marker-none.before-quadruple_arrow
details>summarydetails.marker-none.before-triangle_headed_arrow
details>summarydetails.marker-none.before-triangle_headed_dashed_arrow
details>summarydetails.marker-none.before-triangle_headed_arrow_to_bar
details>summarydetails.marker-none.before-triangle_headed_arrow_with_double_stroke
details>summarydetails.marker-none.before-black_circled_white_arrow
details>summarydetails.marker-none.before-black_arrow
details>summarydetails.marker-none.before-3d_lighted_equilateral_arrowhead
details>summarydetails.marker-none.before-black_equilateral_arrowhead
details>summarydetails.marker-none.before-triangle_headed_arrow_with_long_tip
details>summarydetails.marker-none.before-reversed_triangle_headed_arrow_with_long_tip
details>summarydetails.marker-none.before-black_curved_arrow
details>summarydetails.marker-none.before-reversed_black_curved_arrow
details>summarydetails.marker-none.before-ribbon_arrow
details>summarydetails.marker-none.before-reversed_ribbon_arrow
details>summarydetails.marker-none.before-black_medium_triangle_centred
details>summarydetails.marker-none.before-rocket
details>summarydetails.marker-none.before-black_isosceles_right_triangle
details>summarydetails.marker-none.before-arrow_with_small_triangle_arrowhead
details>summarydetails.marker-none.before-arrow_with_medium_triangle_arrowhead
details>summarydetails.marker-none.before-arrow_with_large_triangle_arrowhead
details>summarydetails.marker-none.before-arrow_with_small_equilateral_arrowhead
details>summarydetails.marker-none.before-rightwards_arrow_with_equilateral_arrowhead
details>summarydetails.marker-none.before-heavy_arrow_with_equilateral_arrowhead
details>summarydetails.marker-none.before-heavy_arrow_with_large_equilateral_arrowhead
details>summarydetails.marker-none.before-triangle_headed_arrow_with_narrow_shaft
details>summarydetails.marker-none.before-triangle_headed_arrow_with_medium_shaft
details>summarydetails.marker-none.before-triangle_headed_arrow_with_bold_shaft
details>summarydetails.marker-none.before-triangle_headed_arrow_with_heavy_shaft
details>summarydetails.marker-none.before-triangle_headed_arrow_with_very_heavy_shaft
details>summarydetails.marker-none.before-finger_post_arrow
details>summarydetails.marker-none.before-squared_arrow
details>summarydetails.marker-none.before-compressed_arrow
details>summarydetails.marker-none.before-heavy_compressed_arrow
details>summarydetails.marker-none.before-heavy_arrow
details>summarydetails.marker-none.before-sans_serif_arrow
details>summarydetails.marker-none.before-wide_headed_light_barb_arrow
details>summarydetails.marker-none.before-wide_headed_barb_arrow
details>summarydetails.marker-none.before-wide_headed_medium_barb_arrow
details>summarydetails.marker-none.before-wide_headed_heavy_barb_arrow
details>summarydetails.marker-none.before-wide_headed_very_heavy_barb_arrow
details>summarydetails.marker-none.before-triangle_arrowhead
details>summarydetails.marker-none.before-white_arrow_within_triangle_arrowhead
details>summarydetails.marker-none.before-arrow_with_notched_tail

section>h2

ここは、縦中横を利用して数学記号もビュレットとして使えないかと、試みて失敗した節で意味はありません。せっかく調べたそれっぽい記号の記録として残します。

>≫≻⊢⊦⊧⊨⊩⊪⊫⊱⊳⋺⋻⋼⨠⩥⩺⪢⪼⪾⫐⫸⟢⟣⟥⧐⧽
>≫≻⊢⊦⊧⊨⊩⊪⊫⊱⊳⋺⋻⋼⨠⩥⩺⪢⪼⪾⫐⫸⟢⟣⟥⧐⧽

section>h2

section

section>h2

section

section>h2

section

section>h2

section

section>h2

section

section>h2

section

section>h2

section
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------

左の列の details > summary は summary::marker の content を指定している。しかし、Safari, SeaMonkey ではそれが効かないので summary::marker に相当するものを display: none; にするとともに、summary::before 擬似要素に content を指定している。他のブラウザでも有効であることから、よい代替手段であろう。但し、SeaMonkey では加えて summary { display: block; } としないと既定のビュレットが残ってしまう。

details.marker-none before
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------

そして、summary の後ろにビュレットを置きたい場合にはいずれのブラウザでも summary::after を指定するほかないので、以下が同等の手法で有効なのは価値が高い。

details.marker-none after
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------

その上、Safari では summary::marker { color: orange; } なども効かないので、ビュレットに相当するものを色々とカスタマイズしたければ ::before, ::after 擬似要素で統一してしまうのも一考である上に特段逃げ道でもないだろう。その際結局、以下の3行の非互換性対応で措置は済む。

details.marker-none	> summary::-webkit-details-marker { display: none; }
details.marker-none	> summary::marker { content: ''; }
details.marker-none	> summary { display: block; }	/* for SeaMonkey */

これで好みのビュレットが指定できるようになるが、そもそも Safari はフォントに依存してサロゲートペアを要するグリフの表示に得意でないので、稀有な記号は表示が難しいので注意すること。よって、当面、右の列の記号の種類に限っておいた方がいいだろう。

  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------
  • ------------------------