アペフチ

Intersection Observerによるモバイル用スクロールスパイ

JxckさんによるIntersection Observer を用いた要素出現検出の最適化という記事を読んで、Intersection Observerを使ってみたくなった。そこで、これを使ってモバイル用のスクロールスパイを実装してみた:Scrollspy example(iOS以外のFirefoxが一番綺麗に動く。次点でSafari。)

Intersection Observer

Intersection Observerを使うとscrollイベントの監視が不要になるケースがあり、その際に動作が軽快になるというのが主なメリットで、ユースケースとしてはデータや(画像などの)リソースの遅延読み込みが想定されているようだ。img要素が画面に入ってくる直前のタイミングで画像読み込みを開始する、という用途だ。

ところがscrollイベントの出番は他にもあり、最近出くわしたのが画面上部固定のナビゲーションとスクロールスパイだった。

固定ナビゲーション

固定のナビゲーションというのは、 この言い方だったら「position: fixed使えば?」という感じだが、

  • 最初はナビゲーションが普通にスクロールで移動するようになっている
  • スクロールによってナビゲーションが画面最上部に来たら、その位置で固定する

という物で、「ナビゲーションが画面最上部に到達しているかどうか」を判定するのに、scrollイベントを監視しなくてはならない。これについてはIntersection Observerではなく、Passive Event Listenerの出番なのだが(これもJxckさんの記事Passive Event Listeners によるスクロールの改善が素晴らしい)、今回はブラウザーを絞って、CSSのposition: stickyで解決した(参考:素敵な position: sticky; Unformed Building)。

スクロールスパイ

残ったスクロールスパイを、Intersection Observerで実装してみた。それもモバイル用にしてみた。

別にモバイルに絞らなくても、Intersection Observerによるスクロールスパイの実装というのは妥当な選択ではあるのだが、別所で「モバイルでもスクロールスパイのようなナビゲーションが欲しい」という話をしていたこともあって、その解決策の例示と併せた。

モバイルでスクロールスパイがあまり見られないのは、やはり「常時サイドバーなどでナビゲーションを表示する」というのが、画面の狭い環境では受け入れ難いから。ただ、一般にヘッダーとフッターくらいは常時表示が許容されているので、ここをスクロールスパイに使ってしまおうというのがアイディアだった。スクロールスパイの目的は

  • 全体のナビゲーションを表示すること
  • その中で現在位置を示すこと

だが、後者に特にフォーカスして、「現在位置が変わる度に表示を切り替えて、その場所の名前にする」という方法をとった。前者については、タップするとナビゲーションの全メニューを表示することで解決とした。

あと、巷のスクロールスパイでは、現在位置が変わった時に

  1. 一旦ナビゲーション中の全部を「選択されていない」状態にする
  2. その後、現在位置に対応するメニューだけを「選択」状態にする

となっているのが多いのだが、Intersection Observerによって注目すべき対象が絞れるので、不要なループを削れたのも嬉しい。

FirefoxのCSS対応

スムーススクロールやカスタムプロパティ、position: sticky;text-decoration-styleなど、Firefoxが、意外と色々なCSSの機能に対応していて驚いた。

特にスムーススクロールはお気に入りで、JavaScriptでスムーススクロールを実装する場合には、自動でlocationのフラグメント部が変わらないし、何より:target擬似クラスの対象が変わらない。その点、ブラウザーネイティブのスムーススクロールなら:targetを使ったスタイリングもできるので非常にいい(今回は使わなかったが)。

最後に、改めてリンクを:

全文検索とスコアリングの仕組み

久し振りにGroongaで学ぶ全文検索 2016-08-26@イーライセンスシステムズに参加してきた。

今日は、全文検索がどういう物かよく分かっていないという人が複数人いたことから、全文検索の仕組みと、スコアリングについて話をしてもらった。

全文検索の仕組み

ホテルデータベースを考える。ホテルのある場所、種類(ホテルか旅館か)、泊数や日付、説明があって、それぞれで絞り込みができる。このうち、説明が全文検索(テキスト検索)の対象になる。

ある言葉(「軽井沢」)で検索する時に、全ホテルを順番に見て、それぞれに検索語が含まれているかを調べる方法は、非常に時間が掛かる。耐えられる時間内に検索結果が返ってこない。そこで、検索結果を速く返すための準備をすることになる。この準備をしたデータのことをインデックスと呼ぶ。

インデックスというのは、日本語では索引で、考え方は本の末尾にある索引と同じ。本の方の索引は、専門用語などの「言葉」と、その言葉が出てくる「ページのリスト」を並べた物だ。全文検索エンジンのインデックスも同様で、キーワードと、そのキーワードを含んでいる文書(のIDなど)のペアを並べた物になっている。検索語がこのインデックスに載っていると、検索するのが速い。但し、インデックスに載っていない検索語は、検索できなくなる。そこで、インデックスのキーに載っている単語を増やすのがコツになる。

元々の説明文などからこのキーを作る方法は二種類ある。一つは、説明を日本語として解釈して分割し、分割結果をキーにする方法。形態素解析と呼ばれる。

もう一つは単純に決めた文字数で区切ること。「軽井沢に行こう」という説明文だったら、「軽井」「井沢」「沢に」「に行」「行こ」「う」と区切ることができる。この時、「軽井沢」で検索すると、どこにもこれと同じキーはないのでヒットしない。そこで、検索語の方も同じように区切って「軽井」「井沢」とする。それから「説明文に『軽井』を含んでいるホテル一覧」と「『井沢』を含んでいるホテル一覧」を取得し、その共通部分を取ると、「軽井沢」を含むホテル情報が手に入る。正確には、「『軽井沢』を含む可能性のあるホテル情報一覧」が手に入る。「井沢さんと軽井さんが経営しています」という文書もヒットするからだ。それを防ぐため、共通部分を取る時に、「キーワードの順番と離れ具合を見て、この順番で隣り合っているような説明文のホテル一覧」を取得するようにする。すると「説明文に『軽井沢』を含んでいるホテル一覧」になる。

スコアリング

なぜスコアをつけるのか、という話を最初にした。それは、検索結果一覧の中のそれぞれで、ユーザーの欲しそう度合いが違うから。欲しそうな度合いの大きい物を先に提示して、そうでない物を後に提示すると、ユーザーが本当に欲しかった物をよりうまく提供できるだろうという考え方による。この欲しそう度合いを決める際に、エリアなどの項目をどれくらい重視するかというのを数値にした物のことを「重み」と呼んで、重みを決めることを「重み付け」と言う。検索スコアをつける際、それぞれの重みを考慮したスコアをつけることになる。

スコアリングの際には、確実さと重要さの二つを考えて行う。

確実さというのは、「軽井沢」で検索した時に、説明文で「軽井沢にほど近い」となっているホテルよりも、エリアがズバリ「軽井沢」となっている方が重くなるようにスコアをつけること。

重要さというのは、仮に説明文にタイトルと本文があった時に、タイトルの方が大事だろうと考えて、タイトルにヒットした場合により高いスコアをつけること。

Farewell to Firefox OS

携帯を買い換えた。

これまでFirefox OSのFx0を使っていたが、諸々の事情で、と言うかタブレットのNexus 7が充電できなくなってしまって、さすがに携帯端末がFirefox OSしかないのは不便が大きすぎるのでAndroid携帯に変えた。本当はNexusにしたかったけど、auにはFirefox OSを出してもらった義理を(一方的に)感じているので、一回はauを選びたかった。その中で有効な選択肢はGalaxy S7 edgeとHTC 10だったが何となくでGalaxyにした。

正直Galayx S7 edgeもHTP 10も大き過ぎるので、多少は小さいHTC 10にしようかなあとは思っていたのだが、店頭で見てみるとどちらも変わらないのでGalaxyにしたのだった。

それはそれとして、タブレットをどうしようかすごい迷ってて、Nexus 7は最高だったと思う。Nexus 9は大き過ぎる。iPad miniは有力候補だけどちょっとした不便を解消したいと思って簡単なアプリ作ってもストア申請めんどいなあ、だったらAndroidの方がまだ自由かなあとか、ASUSのAndroidタブレット大きさ的にはいいけど最新のOSに追従してほしいなあとか、なんかいい感じのところがない。一月くらいGalaxyとくらしてみて決めようと思う。早速BookLive!アプリでまんが読んでみたところやっぱちょっと画面小さいので、何かは買うと思うが。