記事タイトルがターゲットニッチすぎて見つけられないことは承知の上、この現象を紹介します。
まずはこちらのサイトの動きを見てください。(1度リロードしています)
こちらのページでは、SNSボタン付近が読み込まれる時、ローディング後(今回はリロード後)に、画面が少し下がってからSNSボタンが表示されます。
このような少し不自然なガクッとなる状況を、Vue.jsのtemplate
タグを使って、解決しようと思います。
ガクッとなる原因のコードは?
まずは、このガクッとなる部分で何が起きているのか見ていきます。
デベロッパーツールで確認してみると、このような構成になっていました。

SNSボタン表示後のスタイルを見ると、周りに画像のような margin がみられます。
これはHTMLのコードで書いてみれば下記のような感じです。
<!-- html -->
<div v-if="SNSボタンがあれば" class="sns-wrap">
<div class="sns-wrap__btn">シェア</div>
</div>
<NotfoundBlock v-else-if="SNSボタンがなければ(*1)"/>
<LoadingBlock v-else="SNSボタンを読み込み中(*2)" />
.sns-wrap {
&__btn {
margin: 10px 20px 10px 0;
}
}
※NotfoundBlock
(データが取得できない)とLoadingBlock
(ローディング中のクルクル)は独自のコンポーネントです。
上記のようなコードの場合処理の順番は
-
<~ class="sns-wrap">
のloading中だとLoadingBlock
が呼ばれる。 - そのあとloadされた
<~class="sns-wrap">
が出てくる。
このときLoadingBlock
と<~ class="sns-wrap">
の大きさの差(sns-wrapにmarginが付いているため)でガクッと表示されているように見える。
です。
ガクッと潰れてしまう問題を解決したいので、こんな解決策が思いついたので試してみます。
安直な解決策(間違い)
marginを指定しているdiv
の中に書くことで、読み込みがされている最中にもmarginが適用され、ガクッとなる問題は解決するのでは(?)と思い、NotfoundBlock
とLoadingBlock
のコンポーネントを、marginをつけている div
の中に入れてみました。(これは間違いです)
<div v-if="SNSボタンがあれば" class="sns-wrap">
<div class="sns-wrap__btn">シェア</div>
<NotfoundBlock v-else-if="SNSボタンがなければ"/>
<LoadingBlock v-else="SNSボタンを読み込み中" />
</div>
v-if
の直下に v-else
, v-else-if
が配置されています。しかし、この3つは本来並列に配置されるものなので、そもそも書き方が間違っているため、このコードでは正しくありません。
正しい解決策
v-if
, v-else
, v-else-if
を並列に配置しながら、marginの影響化に置きたいとき、template
タグを使うことでうまくいきます。
以下のようにtemplate
を使います。
<div class="sns-wrap">
<template v-if="SNSボタンがあれば">
<div class="sns-wrap__btn">シェア</div>
</template>
<NotfoundBlock v-else-if="SNSボタンがなければ"/>
<LoadingBlock v-else="SNSボタンを読み込み中" />
</div>
このように template
を使用することで、sns-wrap
のスタイル(margin)を残したまま、読み込み、読み込み中、読み込みできなかったが実装することができます。
template
タグを使えば、読み込み後に急にmarginがついてガクッとなるというような違和感のあるコードを修正することができます。
template タグの役割
template
タグは、div
タグのような階層をつくることなく、並列関係を保つことができます。また、classはつけられないという特徴もあります。
div タグでもいい?
div
でもいいけれど、template
のように本来ないタグのように扱われないので、divタグで囲った中の要素は、その上のタグの直下の影響下に置くことはできません。
下記コードにおいて、A, B, Cは並列関係にあります。
(例) template
タグを使用した例
<!-- html -->
<div class="sns-wrap">
<template v-if="SNSボタンがあれば">
<div class="sns-wrap__btn">シェア</div> // A
</template>
<NotfoundBlock v-else-if="SNSボタンがなければ"/> // B
<LoadingBlock v-else="SNSボタンを読み込み中" /> // C
</div>
(例) div
タグを使用した例
<!-- html -->
<div class="sns-wrap">
<div v-if="SNSボタンがあれば"> // A
<div class="sns-wrap__btn">シェア</div>
</div>
<NotfoundBlock v-else-if="SNSボタンがなければ"/> // B
<LoadingBlock v-else="SNSボタンを読み込み中" /> // C
</div>
template
タグ、div
タグそれぞれの効果を考えて、使用する際に使い勝手の良いものを使用するのがいいですね。
記事冒頭にも書きましたが、タイトルがターゲットニッチすぎていいねがもらえないことは承知の上、この現象を紹介しました。しかし、読んでいただいて気づかれたかもしれませんが、この現象は様々なページでよくある現象なのです。
フロントエンドエンジニアたるもの、こういった細かい知識の積み重ねが、UIを崩さない理想の開発者に繋がります。ユーザーの「なにこれ?」という違和感を生まないためにもこういった知識の積み重ねを私自身怠らないようにしたいと思います。
【緩募】
なんという検索でこちらの記事に行き着きましたか?w よければコメントで教えてくださいww