【WEB版 WEAR】position: sticky で似ているアイテム検索の使いやすい UI/UX を実現する

この記事は[ZOZOテクノロジーズ #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech2) 19日目の記事になります。
昨日は、@saitoryuji さんによる「[新卒2年目のエンジニアが単体テストをやってみる](https://qiita.com/saitoryuji/private/fb438218998d04337f79)」でした。

また、今年は全部で5つのAdvent Calendarが公開されています。

- [ZOZOテクノロジーズ #1 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech)
- [ZOZOテクノロジーズ #3 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech3)
- [ZOZOテクノロジーズ #4 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech4)
- [ZOZOテクノロジーズ #5 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech5)

#はじめに
最近、スマートフォンのWEB版 WEARで、コーデ画像をもとにAIがアイテムを検出し、さらにそのアイテムに似ているアイテムを検出して一覧で表示する機能をリリースしました:tada:
<img width="375" alt="真似したいコーデの服が簡単に見つかる！" src="//cdn.wimg.jp/content/dm/20191125/img_01.png">

- 【URL】[https://wear.jp/sp/tera1008/15491266/](https://wear.jp/sp/tera1008/15491266/)　※新機能はスマホのみ対応
- 新機能について、詳しくは[こちら](https://wear.jp/news/info/20191125.html)


この機能をWEBで実装するにあたり、**使いやすいUI/UX**を目指した奮闘記を紹介します！

#こんなのをやりたい
まず、大まかな要件は以下の通りです。

1. コーデ画像の左下の<img width="25" alt="検索アイコン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322782/aa8ff00f-da33-4fb8-6928-65319889fd1b.png">をタッチすると検索スクリーンが下から上がってくる
2. コーデ画像からアイテムが検出された箇所を「◯」で表示する
3. スクリーン下部に、選択されたアイテムの「似ているアイテム」の一覧を表示する
4. スクロールすると「似ているアイテム」の一覧が上がってくる
5. 「似ているアイテム」の一覧はヘッダーが固定され、一覧内でスクロールできる

<img width="160" alt="コーデ詳細.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322782/2b3da5ba-89d0-2004-8c32-36c96ec05438.png">　→　<img width="160" alt="デフォルト.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322782/a1cd9ba8-80d0-3e9b-3408-72e9d8599932.png">　→　<img width="160" alt="スクロール.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322782/a6946684-ca81-3955-60e7-d9ec3129d5d4.png">　→　<img width="160" alt="一覧表示.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322782/d225eb67-bb36-01ac-fcb4-a4f547e17f5c.png">

最初にこの要件を聞いたとき、経験上難しそうだなーと感じたのは、
**windowのスクロールとは別に、一覧内もスクロールできるようにしないとけない**ところでした。
実装自体は可能なのですが、スクロールできる要素の中にスクロールできる要素を配置すると、スクロールしたい方じゃない方がスクロールされるなど、とても使いにくいイメージがありました。。。:sweat:

今回は上記の要件のうち、1、4、5の部分について説明します。
※以下に出てくるソースコードは、説明用のサンプルです。

#ボツになった案
それでも、まずは思いついたままに、以下のような構成で実装してみた。
（最初に言っておきますが、これはボツです笑）

##実装

```HTML:HTML
<div id="searchScreen" style="position: relative; transform: translateY(0px);">
    <div id="image" style="position: absolute;">...</div>
    <div id="list">
        <div id="listHead"></div>
        <div id="listBody" style="height: 812px; overflow-y: scroll;">
            <ul>
                <li></li>
                :
            </ul>
        </div>
    </div>
</div>
```

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="html,result" data-user="mitanih" data-slug-hash="PowwJdQ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Sample1">
  <span>See the Pen <a href="https://codepen.io/mitanih/pen/PowwJdQ">
  Sample1</a> by mitanih (<a href="https://codepen.io/mitanih">@mitanih</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

**#searchScreen**
コーデ画像の<img width="25" alt="検索アイコン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322782/aa8ff00f-da33-4fb8-6928-65319889fd1b.png">ボタンのクリックイベントで、下から上がってくる検索スクリーン
下から上げる処理として、CSSアニメーションで`transform`を`translateY(100%)`から`translateY(0px)`に切り替えて表現する<br>※marginの切り替えでも表現できるが、残像ができたり動きがカクつくなどの問題があった

**#image**
検索スクリーン内上部のイメージエリア
windowがスクロールされても固定したいので、`position: absolute`を指定して、常に画面の同じ位置にいるようJavaScriptで制御する<br>※親要素に`translate`を指定しているので`position: fixed`は使えない

**#list**
検索スクリーン内下部の一覧

**#listHead**
一覧のヘッダー

**#listBody**
一覧のボディ
一覧内をスクロールさせるため`height`を設定し、`overflow-y: scroll`を指定

##問題点
触ってみて、やっぱり使いにくかった。。。:sweat_smile:
iOSのSafariで動作確認した範囲ですが、windowをスクロールさせて一覧を上に持っていきたいのに、一覧内がスクロールされてしまったり、慣性スクロールの影響なのか、windowがスクロールして完全に止まらないと、一覧内がスクロールできないなどの現象も起きた。
スクロールしやすいように工夫して、一覧が一番上まで来ていなければ一覧内はスクロールできないようJavaScriptで制御も入れてみたが、スムーズな動きとはいかなかった。
あと、#imageの位置を固定するためスクロールの度にJavaScriptで位置を計算して設定したが、フラフラ動いてなんか変。
**何より無理し過ぎな気がしました。。。:sweat:**

#採用した方法
いや、もっといい方法はあるはずだ。他のサービスでこんなぎこちないの見たことないぞ。。。:thinking:
と、いろいろ調べていて、**`position: sticky`**に辿り着きました！
Excelの行固定みたいな機能が簡単に実現できるあのスタイルです。

position: stickyの説明<br>https://developer.mozilla.org/ja/docs/Web/CSS/position

メジャーなiOS、Androidのブラウザ対応も問題なさそう！<br>https://caniuse.com/#search=sticky

##実装

```HTML:HTML
<div id="searchScreen" style="position: relative; transform: translateY(0px);">
    <div id="image" style="position: sticky;">...</div>
    <div id="list">
        <div id="listHead" style="position: sticky;"></div>
        <div id="listBody">
            <ul>
                <li></li>
                :
            </ul>
        </div>
    </div>
</div>
```

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="html,result" data-user="mitanih" data-slug-hash="eYmmVOP" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Sample2">
  <span>See the Pen <a href="https://codepen.io/mitanih/pen/eYmmVOP">
  Sample2</a> by mitanih (<a href="https://codepen.io/mitanih">@mitanih</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

細かい調整は省略しますが、要は#imageと#listHeadに`position: sticky`を設定するだけで解決しました！
windowのスクロールで#listが上に来るまでは、#imageがstickyしてくれる。#listが上までくると#listHeadがstickyし、windowのスクロールでそのまま#listBodyが下まで見られる。
まさに理想的なスタイル！
**無理していない実装なので動きもスムーズです！:blush:**

#まとめ
JavaScriptやCSSのプロパティを覚えると、自力でゴリゴリ書けちゃうんですが、実機で確認すると動きがぎこちなくてなんか無理してる感じがすることってあると思います。
そんなときは視点を変えてもっとシンプルな方法を探すと、よりよい解決策が見つかるということに気付かされます。
プログラミングって、そんなことのくり返しのような気がする。。。

明日は、@ikkou さんによる「[WebAR の現状確認 2019 Winter](https://qiita.com/ikkou/items/c32d57e30b28068b268d)」です。
お楽しみに～！