Svelteでこんな感じのよくある横並びメニューを作りたい!
そして画面からはみ出た分は横スクロールもできるようにしたい!
と思ったのでその際の実装をメモしておきます。
リストタグを横並びにする&スクロールもできるようにする
まずは横並びのメニューを作成&横スクロール可能にする実装です。
スクロールバーも非表示にしています
Chrome/Safari以外のブラウザなどシラヌ
ちなみにoverflow-x: hidden;
を指定するとスクロールルバーは消えるけど横スクロールもできなくなるぞっ!!!!
こうすると
<div class="menu">
<ul>
{#each menus as menu}
<li>{menu}</li>
{/each}
</ul>
</div>
<script>
const menus = ['menu1', 'menu2', 'menu3', 'menu4', 'menu5'];
</script>
<style>
.menu {
width: 100%;
}
ul {
padding: 0;
/* 先頭に表示される"・"を消す */
list-style: none;
/* 左右の表示領域からあふれる時にスクロールバーを表示する */
overflow-x: auto;
/* 横並びにした時に返しを行わせない */
white-space: nowrap;
}
/* スクロールバーを非表示にする */
ul::-webkit-scrollbar {
display: none;
}
li {
/* リストを横並びにする */
display: inline-block;
/* 見た目整える用 */
width: 100px;
height: 50px;
background: #fa8072;
margin-right: 5px;
}
</style>
こんな感じになる!
クリックしたメニューのスタイルを変える
クリックした要素が分かるようにスタイルを変えたい!という時の実装です。
class:focus={pos === i}
のように記述することで
pos === i
の条件を満たす場合だけfocus
というclass
をつける事ができるので
クリック時につけたいstyleを追加しておけばクリックされた要素の見た目を変えられます。
こうすると
<div class="menu">
<ul>
{#each menus as menu, i}
<li class:focus={pos === i} on:click={() => pos = i}>{menu}</li>
{/each}
</ul>
</div>
<script>
let pos;
const menus = ['menu1', 'menu2', 'menu3', 'menu4', 'menu5'];
</script>
<style>
.menu {
width: 100%;
}
ul {
padding: 0;
/* 先頭に表示される"・"を消す */
list-style: none;
/* 左右の表示領域からあふれる時にスクロールバーを表示する */
overflow-x: auto;
/* 横並びにした時に返しを行わせない */
white-space: nowrap;
}
/* スクロールバーを非表示にする */
ul::-webkit-scrollbar {
display: none;
}
li {
/* リストを横並びにする */
display: inline-block;
/* 見た目整える用 */
width: 100px;
height: 50px;
background: #fa8072;
margin-right: 5px;
}
/* クリックされた要素のスタイル */
.focus {
background: #87ceeb;
}
</style>
こんな感じになる!
クリック時にイイ感じの場所にメニューをスクロールさせる
見切れているメニューをクリックした時に見切れたままにするのは気持ちが悪いので
クリックされたメニューをいい感じの場所にスクロールさせたい!という時の実装です。
ulタグにbind:this={menu}
を追加することでulのDOM要素を操作できるようになります。
scrollTo
で水平方向のスクロール位置を指定することでulタグ内の任意の位置にスクロールさせることができるようになります。
こうすると
<div class="menu">
<ul bind:this={menu}>
{#each menus as menu, i}
<li class:focus={pos === i} on:click={() => onClickMenu(i)}>{menu}</li>
{/each}
</ul>
</div>
<script>
let pos;
let menu;
const menus = ['menu1', 'menu2', 'menu3', 'menu4', 'menu5'];
const onClickMenu = i => {
pos = i;
menu.scrollTo({
top: 0,
left: 80 * i,
behavior: 'smooth',
})
};
</script>
<style>
.menu {
width: 100%;
}
ul {
padding: 0;
/* 先頭に表示される"・"を消す */
list-style: none;
/* 左右の表示領域からあふれる時にスクロールバーを表示する */
overflow-x: auto;
/* 横並びにした時に返しを行わせない */
white-space: nowrap;
}
/* スクロールバーを非表示にする */
ul::-webkit-scrollbar {
display: none;
}
li {
/* リストを横並びにする */
display: inline-block;
/* 見た目整える用 */
width: 100px;
height: 50px;
background: #fa8072;
margin-right: 5px;
}
/* クリックされた要素のスタイル */
.focus {
background: #87ceeb;
}
</style>
こんな感じになる!!
bind:thisを使う時に気をつけること
bind:this={menu}
はコンポーネントがマウントされるまでは変数menu
の値はundefined
にため以下のような実装だとエラーになるので
onMount()
やon:click
で使用する関数の中で使用しなければならないです。
<div class="menu">
<ul bind:this={menu}>
{#each menus as menu, i}
<li class:focus={pos === i} on:click={() => onClickMenu(i)}>{menu}</li>
{/each}
</ul>
</div>
<script>
let menu;
menu.scrollTo({
top: 0,
left: 50 * i,
behavior: 'smooth',
})
</script>
おわり
いい感じに横スクロールして動くメニューができた!!!