iOS Safari の <select> 要素は密かに変わっているのをご存知ですか

私事ですが先日引っ越した. それはよくて,引っ越しすると住所変更が面倒で 外部キーでマスターからカスケードですべて変わってくれないかと思う. それもよくて,大事な書類が届かないと大変だと 銀行口座やクレジットカードの住所変更をしていた.

生活に必要なものを大抵スマホのアプリで入れている. クレジットカードアプリも生体認証でログインできたりして, いまや 2段階認証なりが必要な PC よりも楽な気がする. (なんか本末転倒でダメなことをしているような気もする)

なので,住所変更もキーボードのほうがやりやすいのは脳裏によぎりつつも 簡単な操作はスマホアプリからやっている.

前置きが長いけど,問題はドロップダウンで住所を選ぶ箇所で, iOS だと選択のドラムがグルグル回るやつが 住所が長いと途切れるということに気づいた:

f:id:mangano-ito:20211202093433p:plain
ここには住んでいません.なんか適当な郵便番号を入れまくって見つけた

省略されてしまって何丁目か確認できないので, ここかな というポジションを選択して 結果が入力されたフィールドを確認することになる:

f:id:mangano-ito:20211202093508p:plain
入力されたフィールドでは折り返しされているので確認できる

(※ お気づきのかたがいらっしゃるかもしれませんが,草稿を書いた当時は iOS 14 でした.後日談は後述します)

あまりにもバカバカしくて, 流石に世間でも話題になっていて なんらかの属性なりで回避できるだろう……と思って調べてみると CodePen の例が一番上に出てくる:

See the Pen IOS long dropdown values fix by Jérémie Gisserot (@ledjay) on CodePen.

f:id:mangano-ito:20211202093709p:plain
今回と同じケース

まさに今回のケースでどうするのかな,と思ったら 空の <optgroup> を末尾に挿入すると長い文字列でも折返しがされるらしい:

f:id:mangano-ito:20211202093729p:plain
折り返しされている

なかなか不可解なこの workaround は Web デザイナーの間で有名らしくて, 検索するといくつもページが出てくる:

f:id:mangano-ito:20211202095329p:plain
テク

ちなみに Android では <optgroup> がなくても折り返しされる:

f:id:mangano-ito:20211202100304p:plain
おなじみ

5年以上 Web プログラマーしていたけど, いままでこの仕様を知らなかったので きっと他にも知らない人がいるかもしれない. (それとも実はこれは一般教養だったりするかもしれない).

この経験のおかげで もし今後こういうケースに出会ったら 「末尾に <optgroup> いれましょう」と <!-- iOS では 末尾に空の optgroup を入れることで長い項目でも折返しさせる--> みたいなコメントを入れてコミットすることができる.

そもそも PC でも長い <option> は好ましくなさそうという話題もある. 異常に長い文字列を表示したときに途切れるのは変わらない:

f:id:mangano-ito:20211202094753p:plain
とぎれるのはいっしょ

f:id:mangano-ito:20211202094855p:plain
`<optgroup>` でとぎれないのはいっしょ

iOS 開発は知識ゼロだけど,カンだけでコードを見てみよう.ピッカーは UIPickerView というらしい.ガイドラインでは Consider using a picker to offer medium-to-long lists of items. となっている:

developer.apple.com

そのクラスから探すと,WebKit で該当の実装をしているソースはここだろうか:

github.com

WebKit 側でのピッカーには WKSelectSinglePickerWKMultipleSelectPicker という 2 つの実装があるようで,文字列がトリムされていたりいなかったり,後者はフォントやラップの指定に違いが見られるようだ:

ここで,<select> の実装がおもしろポイントになっている:

    if (!currentUserInterfaceIdiomIsSmallScreen())
        control = adoptNS([[WKSelectPopover alloc] initWithView:view hasGroups:hasGroups]);
    else if (view.focusedElementInformation.isMultiSelect || hasGroups)
        control = adoptNS([[WKMultipleSelectPicker alloc] initWithView:view]);
    else
        control = adoptNS([[WKSelectSinglePicker alloc] initWithView:view]);

WKFormSelectControl.mm#L88-L93

currentUserInterfaceIdiomIsSmallScreen() は対象が小さいスクリーンを持つデバイスかどうか判定しているようだ.コード的にはつまり iPhoneApple Watch のことだと思われる:

isSmallScreen = idiom == UIUserInterfaceIdiomPhone || idiom == UIUserInterfaceIdiomWatch;

UserInterfaceIdiom.mm#L51-L71

ここから

  • 1 つめの条件 if (!currentUserInterfaceIdiomIsSmallScreen()) は大きいスクリーンの環境であるときは WKSelectPopover のピッカー実装となりそうだ (ポップアップするピッカーだろう).

  • 2 つめは else if (view.focusedElementInformation.isMultiSelect || hasGroups) であるから,複数選択可の select 要素であるか,optgroup 要素によってグルーピングされている select 要素である場合は WKMultipleSelectPicker が選ばれる.

  • 最後にどちらでもないときは WKSelectSinglePicker が選ばれる.

つまり,この実装から推測すると,<optgroup> を含むか,multiple 属性により複数選択可になっていると同じく折り返しがされることになるはずだ:

f:id:mangano-ito:20211202104645p:plain
そして期待通りになった

繰り返すけれど iOS 開発はわからないので,期待と想像から雰囲気で実装だと主張している.間違っているかもしれない (鵜呑みにしないでください).しかし最後は符合したので,大筋ではあっている気がしている.

後日談

この体験のあと iOS 15 にアップデートした.Safari が変わっていてピッカーも変わっている.そして,ピッカーはポップアップになっていてうまく折り返しされるようになっている!

f:id:mangano-ito:20211202105320p:plain
折り返しされている!

f:id:mangano-ito:20211202105340p:plain
全部見える!

つまり,この workaround は既に過去の話題となっている!

この変更は https://github.com/WebKit/WebKit/commit/27da6af7a7aa54634af4b80d6a4d4b801f4fcdf0 で取り入れられたようで,いつのまにか状況は変わるものだ (僕が OS のバージョンアップをずっとしてなかったからなのですけれど).

ちなみに複数選択のコントロールも変わっている (新しいのは WKSelectMultiplePicker で,古いのは WKMultipleSelectPicker のようでややこしい):

f:id:mangano-ito:20211202112800p:plain
みやすいけど今度はこっちがとぎれはじめた

開発者ツールを使って selected="selected" にするユーザーがいるわけがないので, ユーザーエージェントでどうにかしてもらえるのはとてもありがたい.

余談

運転免許証の住所変更をすると 裏書きにあたらしい住所が追記されるだけで 次回更新まではそのままになる. 僕は既に2回の転居が追記されていて あわや あと1行という感じで確実に次回は入らない:

f:id:mangano-ito:20211202121641j:plain
加工しても汚くてごめんなさい

噂では足りないと紙が上から貼られるとか聞いたのだけれど, まさか身分証明書がそんなおてがる対応なのだろうか, この場合は新しい運転免許証が発行されるのだろうか. 調べればわかるのだけれど次回更新が来月までなのでもうよいと思った. (この転居の手続きをした後に更新のハガキが届いた.良い二度手間だ)

つまり,コンピューターでも日常でもはみ出す長い文字列はやっかいだということでこの話をまとめたかった.


🌟 今週の仲間求むのコーナー 🌟

ところで,僕は株式会社はてなマンガチーム で開発を行うエンジニアで,はてなでは一緒にはたらく仲間を募集しております.iOS / Android アプリエンジニアサーバーサイド・フロントエンド開発を行うエンジニアを積極募集しておりますので,ぜひご応募またはご連絡ください.そして この2職種だけではございません.詳しくは採用ページをごらんください:

hatenacorp.jp