Svelte3 横並びのメニューを作る！


Svelteでこんな感じのよくある横並びメニューを作りたい！
そして画面からはみ出た分は横スクロールもできるようにしたい！
と思ったのでその際の実装をメモしておきます。

![スクリーンショット 2019-12-14 10.43.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206915/7a5de3f5-b753-2bd1-a22d-fe1b0deee23e.png "スクリーンショット 2019-12-14 10.43.41.png") 

## リストタグを横並びにする＆スクロールもできるようにする
まずは横並びのメニューを作成＆横スクロール可能にする実装です。

スクロールバーも非表示にしています
~~Chrome/Safari以外のブラウザなどシラヌ~~

ちなみに`overflow-x: hidden;`を指定するとスクロールルバーは消えるけど横スクロールもできなくなるぞっ！！！！

こうすると

```html
<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>
```

こんな感じになる！

![scroll1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206915/4312cc98-7488-6484-38c5-9330b713b2de.gif "scroll1.gif")



## クリックしたメニューのスタイルを変える
クリックした要素が分かるようにスタイルを変えたい！という時の実装です。

`class:focus={pos === i}`のように記述することで
`pos === i`の条件を満たす場合だけ`focus`という`class`をつける事ができるので
クリック時につけたいstyleを追加しておけばクリックされた要素の見た目を変えられます。

こうすると

```html
<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>
```

こんな感じになる！

![scroll2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206915/108cd419-6d6f-afce-2b11-c3cf81636d48.gif "scroll2.gif")


## クリック時にイイ感じの場所にメニューをスクロールさせる
見切れているメニューをクリックした時に見切れたままにするのは気持ちが悪いので
クリックされたメニューをいい感じの場所にスクロールさせたい！という時の実装です。

ulタグに`bind:this={menu}`を追加することでulのDOM要素を操作できるようになります。
`scrollTo`で水平方向のスクロール位置を指定することでulタグ内の任意の位置にスクロールさせることができるようになります。

こうすると

```html
<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>
```

こんな感じになる！！

![scroll3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206915/97ffa596-3346-28f4-dd11-2e37a76946ca.gif "scroll3.gif")


### bind:thisを使う時に気をつけること
`bind:this={menu}`はコンポーネントがマウントされるまでは変数`menu`の値は`undefined`にため以下のような実装だとエラーになるので
`onMount()`や`on:click`で使用する関数の中で使用しなければならないです。

```html
<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>
```

## おわり
いい感じに横スクロールして動くメニューができた！！！