前提・実現したいこと
Convert Jquery slidetoggle code to Javascript内にある2つ目の回答を参考に、素のJavaScriptでjQueryの slideToggle()
関数を以下のように実装しました。
js
1export const slideUp = (el, duration = 300) => { 2 const { style, offsetHeight } = el; 3 style.height = `${offsetHeight}px`; 4 style.transitionProperty = "height, margin, padding"; 5 style.transitionDuration = `${duration}ms`; 6 style.transitionTimingFunction = "ease"; 7 style.boxSizing = "border-box"; 8 style.overflow = "hidden"; 9 el.offsetHeight; // ←ここです。 10 style.height = 0; 11 style.paddingTop = 0; 12 style.paddingBottom = 0; 13 style.marginTop = 0; 14 style.marginBottom = 0; 15 setTimeout(() => { 16 style.display = "none"; 17 style.removeProperty("height"); 18 style.removeProperty("padding-top"); 19 style.removeProperty("padding-bottom"); 20 style.removeProperty("margin-top"); 21 style.removeProperty("margin-bottom"); 22 style.removeProperty("overflow"); 23 style.removeProperty("transition-duration"); 24 style.removeProperty("transition-property"); 25 style.removeProperty("transition-timing-function"); 26 }, duration); 27}; 28 29export const slideDown = (el, duration = 300) => { 30 const { style } = el; 31 32 style.removeProperty("display"); 33 let { display } = getComputedStyle(el); 34 if (display === "none") { 35 display = "block"; 36 } 37 style.display = display; 38 39 const { offsetHeight } = el; 40 style.height = 0; 41 style.paddingTop = 0; 42 style.paddingBottom = 0; 43 style.marginTop = 0; 44 style.marginBottom = 0; 45 style.overflow = "hidden"; 46 style.boxSizing = "border-box"; 47 el.offsetHeight; // ←ここです。 48 style.transitionProperty = "height, margin, padding"; 49 style.transitionDuration = `${duration}ms`; 50 style.transitionTimingFunction = "ease"; 51 style.height = `${offsetHeight}px`; 52 style.removeProperty("padding-top"); 53 style.removeProperty("padding-bottom"); 54 style.removeProperty("margin-top"); 55 style.removeProperty("margin-bottom"); 56 setTimeout(() => { 57 style.removeProperty("height"); 58 style.removeProperty("overflow"); 59 style.removeProperty("transition-duration"); 60 style.removeProperty("transition-property"); 61 style.removeProperty("transition-timing-function"); 62 }, duration); 63}; 64 65export const slideToggle = (el, duration = 300) => { 66 if (getComputedStyle(el).display === "none") { 67 return slideDown(el, duration); 68 } 69 return slideUp(el, duration); 70};
発生している問題
上記のソースコード内で、"ここです。" というコメントがされている el.offsetHeight;
の記述が、style.height =
${offsetHeight}px;
と style.height = 0
の間にない場合は、スライドのアニメーションが引き起こされません。この理由についてご教授いただきたく思い、質問させていただきました。
ここからは推測の話になりますが、おそらく、el.offsetHeight;
の部分で 強制同期レイアウトの回避で紹介されているような Forced Synchronous Layout(強制同期レイアウト)が引き起こされ、ブラウザのレンダリングエンジンがレイアウト計算を行うことで、ブラウザのレンダリングエンジンが1つ目の style.height =
の処理で指定した高さの値を理解し、2つ目のstyle.height =
の処理でアニメーションが引き起こされるのではないかと思っています。
つまり、el.offsetHeight;
の記述がない場合は、Forced Synchronous Layout(強制同期レイアウト)が引き起こされず、1つ目の style.height =
の処理と、2つ目のstyle.height =
の処理の間でブラウザがレイアウト情報を更新することなく style.height
の値がへんこうされるため、ブラウザのレンダリングエンジンにとっては 要素の高さが1度しか更新されていないと判断してアニメーションが引き起こされないのではないかと考えています。
上記の考えが合っているのかどうか、間違っている場合はなぜアニメーションが引き起こされないのかというところをご教授いただけると幸いです。
該当のソースコード
codesandboxに検証環境を用意しました。よろしければフォークして検証などに利用していただけると幸いです。
https://codesandbox.io/s/playground-for-slidetoggle-06pwu?file=/src/slideToggle.js
試したこと
offsetHeight
だけでなく、レイアウトの情報を返すメソッドやプロパティである、offsetWidth
や offsetTop
、clientHeight
でも同様の現象が起きました。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/11/23 06:25