<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Unformed Building</title><link>https://unformedbuilding.com/</link><description><![CDATA[ウェブ制作に関するあれこれと個人的な記録。]]></description><language>ja-JP</language><managingEditor> pnr.matori@gmail.com (matori)</managingEditor><webMaster>pnr.matori@gmail.com (matori)</webMaster><atom:link href="https://unformedbuilding.com/feed/webdev/" rel="self" type="application/rss+xml"/><item><title><![CDATA[許可した漢字だけを使うように指摘するtextlintルールを作った]]></title><link>https://unformedbuilding.com/articles/released-textlint-rule-ja-allowed-kanji/</link><description><![CDATA[許可する漢字をコントールできたり、特定パターンの場合は許可できるようなtextlintルールを作りました]]></description><pubDate>Fri, 23 Jun 2023 06:06:49 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/released-textlint-rule-ja-allowed-kanji/</guid><content:encoded><![CDATA[<p>タイトルどおりですが、許可した漢字だけを使うように指摘する<a href="https://github.com/matori/textlint-rule-ja-allowed-kanji">textlint-rule-ja-allowed-kanji</a>というtextlintルールを作りました。</p><p>すでに特定の漢字セットを使うように指摘するtextlintルールはありますが、自分の要望とはちょっと違う感じでした。</p><p>たとえば常用漢字以外は必ずエラーになるというものなので、ユーザーが許可する漢字を増やしたり減らしたりできるようにしたかったのです。</p><p>また、基本的にはエラーになる漢字でも特定の熟語は許可するようにしたかったというのもあります。</p><section><h2>動機</h2><p>もともとはNHK放送文化研究所の『<a href="https://www.nhk.or.jp/bunken/book/irregular/dic_kanji_notation.html">NHK漢字表記辞典</a>』を基準にしたルールがほしかったという事情があります。</p><p>「<a href="https://www.nhk.or.jp/bunken/summary/kotoba/research/024.html">新用字用語辞典の概要まとまる（１） | ことば（放送用語） - ことばの研究 | NHK放送文化研究所</a>」のPDFを見ると分かるように、NHKの基準は常用漢字とは異なっています。<br></p><p>ほかに、<a href="https://www.pressnet.or.jp/publication/book/pdf/shimbun_yogo.pdf">日本新聞協会の新聞用語集（2010年版, PDF）</a>を見ると、新聞常用漢字は常用漢字の一部を使わないようにしたり、特定の熟語の場合は使えるように、などとなっています。<br></p><p>こういった状況に対応できるようなtextlintルールがほしかったのです。</p><p>とはいえ、必要な漢字セットを更新し続けるというのも自分にはできそうになかったので、いくつかの漢字プリセットを用意し、そこから漢字を除外したり追加したりできるようにしました。<br>熟語の対応は、特定パターンの場合は許可するオプションを追加して対応しました。</p></section><section><h2>設定例</h2><p>リポジトリのREADMEにも似たものがありますが、オプションの例を出します。<br>次のコードはtextlintの対象とするテキストです。</p><pre>職権濫用の諜報から得た情報です。
川が氾濫したそうですが、今日のおかずは野菜炒めです。炒飯もあります。

豆を炒るのは明日です。</pre><p>むちゃくちゃな文章ですが気にしないでください。<br>これに対して当ルールを初期状態ままtextlintを使うと次のような結果が得られます。</p><pre><samp>1:6   error  「諜」は許可されていない漢字です。  ja-allowed-kanji
2:22  error  「炒」は許可されていない漢字です。  ja-allowed-kanji
2:27  error  「炒」は許可されていない漢字です。  ja-allowed-kanji
4:3   error  「炒」は許可されていない漢字です。  ja-allowed-kanji
</samp></pre><p>初期状態では常用漢字のみを使うようにしているので、このような結果になります。</p><p>次に、先ほどのNHK漢字表記を参考に、「濫」を「氾濫」のみに限定し、「諜」を許可、「炒」を「炒め」の場合のみ許可する設定です。</p><pre><code class="language-json">{
  "rules": {
    "ja-allowed-kanji": {
      "preset": {
        "regular": true, // 常用漢字のみの場合はpresetごと無指定でもOK
      },
      "exclude": "濫",
      "allowKanji": "諜",
      "allowPatterns": [
        "氾濫",
        "/炒(?=め)/"
      ]
    }
  }
}</code></pre><p>この設定からは次の結果が得られます。</p><pre><samp>1:3   error  「濫」は "氾濫" 以外のパターンでは許可されていない漢字です。        ja-allowed-kanji
2:27  error  「炒」は "/炒(?=め)/" 以外のパターンでは許可されていない漢字です。  ja-allowed-kanji
4:3   error  「炒」は "/炒(?=め)/" 以外のパターンでは許可されていない漢字です。  ja-allowed-kanji</samp></pre></section><p>こういう感じで、ユーザーが用途に合わせて調整できます。<br>許可する漢字を増やせば簡単にエラーを消せるので、そのあたりのバランスはお任せします。</p><p>その他の設定についてはリポジトリのREADMEを参照してください。</p><section><h2>やりたいことはできた</h2><p>ひとまず、自分の要望を満たすルールは完成したので満足しました。<br>同じ要望を持つ方にも使ってもらえたら嬉しいです。</p></section>]]></content:encoded></item><item><title><![CDATA[わたしはページ内検索を普通に使いたい]]></title><link>https://unformedbuilding.com/articles/using-find-in-page-without-interference/</link><description><![CDATA[ブラウザーに搭載されているページ内検索が阻害される例]]></description><pubDate>Thu, 19 Jan 2023 05:43:03 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/using-find-in-page-without-interference/</guid><content:encoded><![CDATA[<p><a href="https://unformedbuilding.com/articles/clipped-text-and-search-within-webpage/">以前にも少し書いた</a>のですが、ウェブブラウザーで使えるページ内検索による体験が阻害されるのが本当にストレスで、その例を残しておこうと思います。</p><p>採用側の気持ちも分かりますので、どうなると皆が幸せになるのか分からないという種類のものなので困ったところです。</p><section><h2>省略されたテキスト</h2><p><code>overflow: hidden</code>と<code>text-overflow: ellipsis</code>または<code>-webkit-line-clamp</code>によってテキストが省略された場合、その省略部分がページ内検索にヒットすると非常に探しづらいです。<br>特に、グリッドで区切られたアイテムが複数列・複数行で並んでいる際に顕著です。</p><p>以前の記事のとおり、わたしはYouTubeでよく遭遇します。</p><p>下記のデモは、1000個のアイテムを並べたものです。<br>このうち、「Red Rose」というワードは12個のアイテムに含まれています。<br>デモを開いて「Red Rose」でページ内検索をしてください。</p><ul><li><a href="https://unformedbuilding.com/demo/2023/using-find-in-page-without-interference/grid-line-clamped.html">テキストが省略されたグリッドアイテム</a></li><li><a href="https://unformedbuilding.com/demo/2023/using-find-in-page-without-interference/grid-default.html">テキストの省略のないグリッドアイテム</a></li></ul><p>テキストが省略されたデモでは、2、4、6、12番目にヒットしたアイテムがどこにあるのか判断できないと思います。<br>フォーカスが移動するわけでもありませんので、<code>document.activeElement</code>を使っても判断できません。</p><p>なお、Safariの場合はこの問題に遭遇しません。</p><figure><img alt="スクリーションショット画像：Safariで「テキストが省略されたグリッドアイテム」デモを開き、「Red Rose」でページ内検索をしたときの画面。" src="https://unformedbuilding.com/images/2023/using-find-in-page-without-interference/find-in-page-on-safari.png"></figure><p>スクリーションショット画像は2件目を表示したときのものです。<br>ページ全体が暗くなり、ヒットした部分だけがハイライトされます。</p><p>さらに、ヒットしたテキストが省略された部分であっても、文の途中を省略してすることでヒットしたテキストを表示しています。<br>ヒット部分の1行目の末尾が省略記号になっているのが分かります。</p><p><code>text-overflow</code>の場合はヒットしたテキストの表示はうまくいっていないように見えますが、画面が暗くなってヒットした部分のハイライトは行われますので、どこにヒットしたテキストがあるのかは判断できます。</p></section><section><h2>仮想スクロール</h2><p>仮想スクロールは大量のデータを表示する際に、パフォーマンス向上を目的として使われるものです。</p><p>その原因と仮想スクロールの仕組み上、画面に表示されないアイテムはDOMツリーから取り除かれます。結果として、ページ内検索にヒットしなくなります。</p><p>次のデモは、前述のデモと同じデータを使ったものです。デモを開いて「Red Rose」でページ内検索をしてください。<br>仮想スクロールには<a href="https://www.npmjs.com/package/virtual-scroller">virtual-scroller</a>というライブラリーを使用していますが、これは他のものを使っても同じです。</p><ul><li><a href="https://unformedbuilding.com/demo/2023/using-find-in-page-without-interference/list-virtual-scroll.html">仮想スクロールのリスト</a></li><li><a href="https://unformedbuilding.com/demo/2023/using-find-in-page-without-interference/list-default.html">普通のリスト</a></li></ul><p>仮想スクロールでは、実際に見えている部分とその前後少しに対象ワードが存在する場合にのみページ内検索にヒットします。</p><p>このときもっとも理不尽に感じるのは、上から順にスクロールしながら見ているときです。<br>特定のワードが確実に存在することが分かっているのに、実際にはページ内検索にヒットしないので頑張ってスクロールしながら探さなくてはなりません。</p><p>わたしの場合、この現象はTwitterの検索結果ページで発生します。</p><p>これはユーザー側でどうにかできるものではありませんので、余計に不便です。<br>テキスト省略の場合は、開発者ツールで該当スタイルを削除すればどうにかなりますが、仮想スクロールの場合はそうではありません。</p><p>制作側としては専用の検索フォームを用意するのが解決策となると思います。<br>しかしユーザー側としては、本来は<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>F</kbd>で済むのに、サイトごとにフォームの位置を覚えたり、仮想スクロールかどうかを調査しないといけないのは手間に感じます。</p><p>大量のデータを一気に描画して落ちるパフォーマンスを改善するのは重要だとは思います。<br>使われるか分からないページ内検索よりも、必ず表示されるデータのほうが優先度は高いでしょう。</p></section><section><h2>まとめ</h2><p>最初にも書いたように、「採用するとページ内検索が機能しなくなるが、採用したほうが多くの場合は有用である」という類いのものは非常に悩ましいです。</p><p>ページ内検索は、モバイルブラウザーではアクセスが少し面倒ですので、使われる場面がデスクトップブラウザーより少ないと思われます。<br>画面が小さく、処理能力もPCより劣るモバイルデバイスを考えると、ページ内検索の優先度はさらに下がるでしょう。 <a href="https://github.com/WICG/scroll-to-text-fragment">WICG/scroll-to-text-fragment</a>によると、Android Chromeでページ内検索を使うユーザーは1%もいないということです。</p><p>テキストの省略は機能としてはあってもなくても変わりませんが、デザインの都合や意思決定者の強い要望によって採用されることも多いでしょう。<br>セレクターにページ内検索がヒットしている状態の疑似クラスがあれば解決できそうです。<br>もしくは、各ブラウザーのページ内検索がSafariのようになるかです。</p><p>仮想スクロールについては何も思い浮かびません。<br>ユーザー側としてはおとなしく用意された検索フォームを使うしかないでしょう。<br>Twitterの検索結果ページの場合、さらに検索ワードを追加して検索し直す、という流れになるでしょうか。</p><p><a href="https://html.spec.whatwg.org/multipage/interaction.html#find-in-page">HTML Standard - 6 User interaction - 6.9 Find-in-page</a>を見ると、<code>window.find()</code>という形でAPIが予定されてはいるようです。<br>該当Issue「<a href="https://github.com/whatwg/html/issues/3539">Potentially standardize window.find() · Issue #3539 · whatwg/html</a>」を見ると、議論の経過が分かります。</p><p>現行ブラウザーでも<code>window.find()</code>は使えるのですが、それは非標準なもので、非推奨になっています。</p><p>このAPIによってページ内検索でイベントが発生するようになったり、該当のノードを取得できるようになれば、今回の問題はどちらも解決するかもしれません。<br></p></section>]]></content:encoded></item><item><title><![CDATA[CSS Gridで作られたコンポーネントの列数と行数を知りたい]]></title><link>https://unformedbuilding.com/articles/how-to-know-columns-rows-count-css-grid/</link><description><![CDATA[CSS Gridの自動リピートで配置されている場合に、列数と行数をJavaScriptから知りたい。]]></description><pubDate>Thu, 01 Dec 2022 07:10:06 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/how-to-know-columns-rows-count-css-grid/</guid><content:encoded><![CDATA[<p>タイトルどおりなんですが、<code>repeat(auto-fill, ...)</code>または<code>repeat (auto-fit, ...)</code>が使われているCSS Gridのコンポーネントの、現在の列数（カラム数）と行数（ロウ数）をJavaScriptから知る方法についてです。<br>ただし、すべてのグリッドアイテムは1セル分の大きさであるという前提条件があります。</p><p>はじめはグリッドコンテナーのサイズと、グリッドアイテムのサイズを調べて計算しないといけないのかと考えていましたが、検索したらシンプルな解決法を見つけました。<br>やはり同様の要望を持つ人はいるようで、StackOverflowで質問されており、その回答が分かりやすいものでした。</p><p><a href="https://stackoverflow.com/questions/49506393/how-to-get-count-of-rows-and-columns-in-javascript-for-flexbox-and-grid-layout">html - How to get count of rows and columns in javascript for flexbox and grid layout? - Stack Overflow</a></p><figure><blockquote cite="https://stackoverflow.com/a/66186894"><p>The questions is al little bit older but in case anybody needs this:</p><p>If you are working with CSS-Grid you don't need so much calculation. You can get the current template settings e.g. with</p><pre><code class="language-javascript">getComputedStyle(container).getPropertyValue("grid-template-rows")</code></pre><p>in modern Browsers this returns the actual values, not the values from your css, so you get a string like</p><pre><code class="language-text">250px 250px 250px</code></pre><p>you can than calculate the current number of rows by splitting the string and counting the elements in the resulting array.</p><p>This might work in older IEs as well, I did not test it.</p></blockquote><figcaption><cite><a href="https://stackoverflow.com/a/66186894">「How to get count of rows and columns in javascript for flexbox and grid layout?」についた回答の1つ</a></cite></figcaption></figure><p>自動的に列数が変更されるグリッドでも、上記コードを使えば行のサイズ一覧をテキストで得られる（同様に列数も取れる）ので、複雑な計算は必要ないとのことです。<br>これならシンプルな解決ができそうに見えますので、<a href="https://unformedbuilding.com/demo/2022/how-to-know-columns-rows-count-css-grid/">デモを作って試しました</a>。</p><p>列数を取得するコードは、最初は次のような処理をしていました。</p><pre><code class="language-javascript">const raw = getComputedStyle(element).getPropertyValue("grid-template-columns");
const count = raw.split(" ").length;</code></pre><p>試して分かりましたが、<code>auto-fill</code>の場合はこれでうまくいきます。1行になって右側に空白ができてもアイテム幅はちゃんとデータとして取得されます。<br>しかし、<code>auto-fit</code>の場合、空白を埋める場合に<code>0px</code>の幅が追加されていきます。これはデモの「スタイル値」を見ると分かるでしょう。結果、見た目の列数と異なった結果を得てしまいます。<br>これを回避するには、<code>0px</code>の幅を取り除いてから数えるしかないでしょう。</p><pre><code class="language-javascript">const raw = getComputedStyle(element).getPropertyValue("grid-template-columns");
const count = raw.split(" ").filter(w => w !== "0px").length;</code></pre><p>こんな感じです。<br>これで見た目と同じ列数を取得できました。</p>]]></content:encoded></item><item><title><![CDATA[CSS Niteに登壇した]]></title><link>https://unformedbuilding.com/articles/cssnite-2021-11-19/</link><description><![CDATA[CSS Nite「CodeGridから読み解くイマドキのCSS 第二弾」に登壇したときの話。]]></description><pubDate>Tue, 25 Jan 2022 02:49:42 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/cssnite-2021-11-19/</guid><content:encoded><![CDATA[<p>昨年の話になりますが、2021年11月に開催されたCSS Nite「CodeGridから読み解くイマドキのCSS 第二弾」に登壇しました。</p><p>発表タイトルは「今から使える、いつか使える日本語ウェブ文字組みの機能」ということで、主にCSSのテキスト周りの機能について話しました。</p><p>昨今の情勢から、イベントはオンラインで開催されたのですが、オンラインの発表というものは聴衆の視線がないので非常に快適でした。ただ、音声については不安があったので、できるだけはっきりと話すように心がけました。<br>とはいえ、わたしは普段あまり大きな声で話さないので、実際のところどうだったのかは疑問ではあります。</p><p>CSS Niteということで、ひとつのテーマを深く話すというよりも、ある程度の範囲を浅すぎない程度に話すという目標で発表しました。<br>普段ブログや会社のオンラインマガジンで書いているものは前者になりますので、どのあたりまで話せばいいのか、これを言わなくて大丈夫なのか、特定ケースで発生する不具合の紹介はしなくていいのか、そういう悩みがつきまといました。<br>少し広めに、深すぎず浅すぎずに話すというのはかなり難しいものだと実感しました。</p><p>スライドについてですが、デモというか「こういう表現ができますよ」の画像がテキストの画像ですので、本文は少なめにし、伝えたいことは話して伝えるという形にしました。<br>スライド内の本文にたくさん書くと、あとで見たときに何を話していたのか分かりやすいという利点がありますが、スライドはあくまでも補助であってメインは話であるという意識で取り組むのがよいのではないかと考えを改めた結果です。<br>また、オンラインのイベントで参加者がスライドを見る大きさ（ストリーミング動画の再生解像度）が異なるので、スライド自体は簡易な見た目のほうが利点が大きいと思いました。</p><p>当日のスライドは「<a href="https://unformedbuilding.com/slide/cssnite-2021-11-19/">今から使える、いつか使える日本語ウェブ文字組みの機能</a>」です。<br>使用したものは<a href="https://revealjs.com/">reveal.js</a>です。</p><p>最終的にストリーミング動画として配信されるのを前提としていましたので、わたしの環境でちょうどいいサイズに収まるレイアウトで作っています。<br>閲覧環境によってはコンテンツが一画面に収まらないかもしれません。</p><p>登壇は久々でしたが、オンラインということで緊張感はほどほどにできて助かりました。<br>参加者の皆さん、ご清聴ありがとうございました。</p>]]></content:encoded></item><item><title><![CDATA[display:noneな画像のダウンロードを避けるために]]></title><link>https://unformedbuilding.com/articles/avoid-downloading-images-with-display-none/</link><description><![CDATA[HTMLで読み込む画像はdisplay:noneされていてもダウンロードされるので、それを避けるための方法。]]></description><pubDate>Thu, 27 May 2021 09:44:05 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/avoid-downloading-images-with-display-none/</guid><content:encoded><![CDATA[<p><code>img</code>要素で読み込む画像は、CSSで<code>display: none</code>が指定されていたとしてもダウンロードされることはご存知かと思います。<br>それをどうにかしたいという話です。</p><p>前提として、画面サイズに応じたデザインの変更による画像表示の切り替えを想定しているわけではありません。<br>それにも対応できますが、使いにくいと思います。</p><p>結論から言うと「<code>url()</code>関数を指定した<code>content</code>プロパティをインラインスタイルで指定する」、これだけです。</p><section><h2>なんでこんなことをしたかったのか</h2><p>大量の小さな画像が配置されたウェブページを作っていましたが、それらの大半はユーザーが操作しない限り表示されないUIでした。<br>表示されるかどうか分からない画像をダウンロードさせるのもイマイチな気がして、ダウンロードを遅らせたかったというのが理由です。</p></section><section><h2>通常の<code>img</code>指定</h2><p><a href="https://unformedbuilding.com/demo/2021/avoid-downloading-images-with-display-none/1.html">デモを作ってあります</a>ので、確認してみましょう。<br><code>display: none</code>になっているのは親要素ですが、自身につけても変わりません。</p><pre><code class="language-css">p {
  display: none;
  padding: 10px;
  overflow: auto;
  resize: both;
  background-color: gainsboro;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}</code></pre><pre><code class="language-html">&lt;button id="button">画像を表示する&lt;/button>
&lt;div id="container" class="container">
  &lt;img src="./long-tailed-blue.jpg"
       class="image"
       width="2048"
       height="1536"
       alt="写真：ウラナミシジミ">
&lt;/div>

&lt;script>
const button = document.querySelector('#button');
button.addEventListener('click', function() {
  const container = document.querySelector('#container');
  container.style.display = 'block';
  this.disabled = true;
});
&lt;/script></code></pre><p>デモページで開発者ツールを開き、ネットワークパネルを見てもらうと分かるように、ページを開いた時点で画像がダウンロードされています。<br>これを、ボタンが押下されて<code>.container</code>が<code>display: block</code>になったときにダウンロードさせたいというわけです。</p></section><section><h2>表示されてからダウンロードを開始する</h2><p>ご存知のように、CSSの<code>background-image</code>で指定された背景画像は、対象が<code>display: none</code>になっているとダウンロードされません。<br>これは<code>background-image</code>というより<code>&lt;image></code>型で扱われるリソースがそうなっているのではないかと思うのですが、ちょっとソースが分かりませんでした。CSS Images Moduleあたりかなと思ったんですが、仕様ではなく実装のほうなのかもしれません。</p><p>ともかく、背景画像にすれば<code>display: none</code>のときにダウンロードされないようにはできるのですが、非常に不便です。<br>背景画像を使うということは、該当要素のサイズ指定が必要になりますが、そのためには画像のサイズを知っていなければなりません。</p><p>そこで<code>content</code>プロパティによる要素置換を使います。</p><p><a href="https://unformedbuilding.com/demo/2021/avoid-downloading-images-with-display-none/2.html">デモをご覧ください</a>。<br>画像として扱う要素が<code>img</code>から<code>div</code>になっている以外は同じです。</p><pre><code class="language-html">&lt;div id="container" class="container">
  &lt;div style="content: url(./long-tailed-blue.jpg)"
       class="image">
  &lt;/div>
&lt;/div></code></pre><p>開発者ツールのネットワークパネルを見ると、ボタンを押下して<code>display: none</code>な<code>.cotainer</code>が<code>display: block</code>になって初めて画像がダウンロードされているのが分かります。</p><p><code>.cotainer</code>をリサイズすると、それに合わせて画像になっている<code>div</code>のサイズも変わります。これは<code>img</code>要素で読み込んだときと同じ動作です。</p></section><section><h2>読み上げ対応</h2><p><code>content</code>プロパティに<code>&lt;image></code>型が指定された要素は、アクセシビリティツリー上では画像として扱われます。<br>開発者ツールで確認してみてください。<br>こういう点では問題ありません。</p><p>しかし<code>div</code>には<code>alt</code>属性が使えませんので、代替テキストが指定できません。<br>この問題に対しては、次のように解決されることが望ましいでしょう。</p><pre><code class="language-css">content: url(./long-tailed-blue.jpg) / "写真：ウラナミシジミ"</code></pre><p><code>content</code>プロパティ値の<code>/</code>（スラッシュ）で区切ったあとの文字列は代替テキストとなります。<br>構文的には次のようなものです。（2021年5月17日版 Editor’s Draft）</p><pre><code>normal | none | [ &lt;content-replacement> | &lt;content-list> ] [/ [ &lt;string> | &lt;counter> ]+ ]? </code></pre><p>しかし、2021年5月現在、スラッシュ後の文字列に対応している環境はBlinkエンジンのみです。GeckoやWebkitでは使用できません。</p><p>対応している環境、たとえばGoogle Chromeを使って読み上げてみると、ちゃんと画像であることを伝えてくれます。<br>そうではない環境では、不正なCSSとして<code>content</code>プロパティ自体が無視され、画像すら表示されません。</p><p>対処方法としては、替わりに<code>aria-label</code>を使うことでしょう。</p><pre><code class="language-html">&lt;div style="content: url(./long-tailed-blue.jpg)"
     class="image"
     aria-label="写真：ウラナミシジミ">
&lt;/div></code></pre><p><a href="https://unformedbuilding.com/demo/2021/avoid-downloading-images-with-display-none/3.html">両者を比較するデモをご覧ください</a>。<br>このデモを使って開発者ツールを確認すると、ここまでの説明がわかりやすいかと思います。</p></section><section><h2>問題点</h2><p><code>img</code>要素ではありませんので、<code>width</code>または<code>height</code>属性が使えません。つまり、画像のロードが完了しないとレイアウトが完了しません。<br>ただし、そのような場合は画像のサイズが分かっていることが前提ですので、CSSで指定すればいいでしょう。</p><pre><code class="language-css">.image {
  max-width: 100%;
  height: auto;
}</code></pre><pre><code class="language-html">&lt;div style="
       content: url(./long-tailed-blue.jpg);
       aspect-ratio: 2048 / 1536;
     "
     class="image"
     aria-label="写真：ウラナミシジミ">
&lt;/div></code></pre><p>また、<code>img</code>要素ではないことから、<code>loading</code>属性が使えません。<br>そもそもこの方法を採用するのは特殊なケースかと思いますので、ビューポート外の画像を遅延ロードさせたいなら普通に<code>img</code>要素を使ったほうがいいのではないかと思います。</p><p>レスポンシブ画像はCSSの<code>image-set()</code>関数を使えば解決できそうです。</p><ins datetime="2021-05-28T16:10:05+09:00"><p>これはあくまでも<code>div</code>ですので、コンテキストメニューが画像用のものにはなりません。<br>その点は解決できないでしょう。</p></ins></section><ins datetime="2021-05-28T16:10:05+09:00"><section><h2><code>loading="lazy"</code>じゃ駄目なの？</h2><p>それでもいいです。<br>ただ、現在はWebkitで使えないことは念頭に置いておく必要があるでしょう。<br><a href="https://bugs.webkit.org/show_bug.cgi?id=196698">https://bugs.webkit.org/show_bug.cgi?id=196698</a>がWebkitの対象バグです。</p></section></ins><section><h2>まとめ</h2><p>わたしが使ったケースとしては、タブが20個あるタブUIで、そのコンテンツはリンク集であり、各リンクに小さな画像がついているというものでした。<br>タブUIの中身というものは、表示されるかどうか不明という意味では代表的なものかもしれません。<br>他には<code>details</code>要素でしょうか。</p><p>ほとんどの場合、<code>img</code>要素を使うべきだと思いますが、このような方法もあると知っていれば役に立つことがあるかもしれません。</p></section><ins datetime="2021-05-28T16:10:05+09:00"><aside><h2>2021年5月28日追記</h2><p><a href="https://twitter.com/SaekiTominaga/status/1397914352204668945">@SaekiTominagaさんからご指摘がありました</a>ので、その内容について追加しました。</p></aside></ins>]]></content:encoded></item><item><title><![CDATA[テキストの省略とページ内検索]]></title><link>https://unformedbuilding.com/articles/clipped-text-and-search-within-webpage/</link><description><![CDATA[長いテキストを省略したとき、ページ内検索の結果がわからなくなるという話。]]></description><pubDate>Thu, 10 Dec 2020 11:57:01 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/clipped-text-and-search-within-webpage/</guid><content:encoded><![CDATA[<p>これはなにかの解決方法とかではなく、どうしたらいいんだろうなという話です。</p><p>YouTubeの動画一覧などがわかりやすいのですが、タイトルが特定行で省略されてellipsisが付与されています。<br>問題は1行でも複数行でも関係ないのですが、この省略されたテキストです。</p><p>このような省略されたテキストがページ内検索にマッチした場合、ページはその位置までスクロールするのですが、ハイライトが見えない（省略されているので）ために、どこがマッチしているのかまったくわかりません。<br>これがただの縦に詰むタイプのレイアウトならば、ある程度の予測はつけられますが、複数カラムのグリッドにアイテムが並ぶようなレイアウトだと完全にお手上げです。</p><p>やる気に溢れている場合は開発者ツールを開いて省略しているスタイルを削除すればいいのですが、手間です。<br>ユーザーCSSという手もありますが、そこまで頻繁にページ内検索しているわけでもないので手間とのバランスが微妙です。そして対象サイト以外だと結局開発者ツール頼りになります。</p><p>省略されているテキストを検索できる機能が別に存在している場合もありますが、サイト固有機能の学習が必要です。<br>一方、ページ内検索というものはデスクトップのウェブ・ブラウザーには搭載されていますし、どのサイトであっても共通動作で実行でき、非常に便利です。</p><p>ページ内検索は問題なく動作しているのに、サイトのCSSでその機能がほとんど使い物にならない状態に陥るのは、使っている側は理不尽に感じます。</p><p>一方、ウェブ制作者としてはテキストが省略されたデザインというものは珍しいものではなく、実装する機会もそれなりにあります。<br>個人的な気持ちで言えば省略したくないことが多いのですが、デザイン以前の様々な事情もあって省略することになっている場合もある、ということは理解できます。</p><p>そういうわけで、省略されたテキストはページ内検索にひっかかるけど見つけられない、という状況はどうしたらいんだろうなという話でした。</p><hr><p>当然ですが、HTML上で省略されているものは、そもそもページ内検索に引っかからないのでこのような問題は起きません。</p>]]></content:encoded></item><item><title><![CDATA[キーボード操作に優しくないドロップダウンの例]]></title><link>https://unformedbuilding.com/articles/keyboard-unfriendly-dropdown/</link><description><![CDATA[キーボード操作時、視覚的にのみ不具合が発生するドロップダウンの原因と解決法。]]></description><pubDate>Sat, 19 Sep 2020 05:57:39 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/keyboard-unfriendly-dropdown/</guid><content:encoded><![CDATA[<p>先日、おもしろい不具合を起こしているドロップダウンUIを見かけたので、どうしてその不具合が起こるのかを残しておきます。</p><p>まずは実際に見たUIの仕組みを再現した<a href="https://unformedbuilding.com/demo/2020/keyboard-unfriendly-dropdown/example-1.html">デモページ</a>を見てください。<br>「リンク集」と記載のある部分のクリックをトリガーとしてドロップダウンが動作します。<br>おそらく、マウスやタッチなどのポインティング・デバイスでは何事もなく動作するでしょう。</p><p>ところが、キーボードによるナビゲーションを使っていると、ドロップダウンにフォーカスした瞬間、ドロップダウンのテキストが見えなくなります。<br>ブラウザー下部などに表示されるリンク先URLは表示されますので、ドロップダウン内部のリンクにフォーカスが当たっているようです。<br>リンクとして動作はしても、リンク・テキストは見えず、操作しづらいのは間違いありません。</p><p>この不具合の面白いところは、スクリーン・リーダーでは大きな問題とならないところです。<br>ユーザーのアクションを元に折りたたみまたは展開するという機能は動作しませんが、リンク自体は何事もなく読み上げられます。<br>問題となるのは、視覚的にブラウジングしているキーボード・ユーザーです。</p><p>では、このドロップダウンのコードがどうなっているのかを見ます。<br>いつものとおり重要ではない部分は省略してありますので、全部見たい方はデモページでソースを表示してください。</p><pre><code class="language-html">&lt;div class="example">
  &lt;dl class="dropdown">
    &lt;dt class="dropdown-trigger">リンク集&lt;/dt>
    &lt;dd class="dropdown-content">
      &lt;a href="...">...&lt;/a>
      &lt;!-- ... -->
    &lt;/dd>
  &lt;/dl>
&lt;/div></code></pre><pre><code class="language-css">.dropdown {
  background-color: white;
  height: 2rem;
  overflow: hidden;
  transition-property: all;
  transition-duration: 0.2s;
}

.dropdown.open {
  height: 14rem;
}

.dropdown-trigger {
  line-height: 2rem;
}

.dropdown-content {
  opacity: 0;
  transition-property: all;
  transition-duration: 0.2s;
}

.dropdown.open .dropdown-content {
  opacity: 1;
}

.dropdown-content a {
  display: block;
  line-height: 2rem;
}</code></pre><pre><code class="language-javascript">const dropdown = document.querySelector('.dropdown')
const trigger = dropdown.querySelector('.dropdown-trigger')

trigger.addEventListener('click', event => {
  event.preventDefault()
  dropdown.classList.toggle('open')
})</code></pre><p>見てのとおり<code>.dropdown-trigger</code>のクリックをトリガーに<code>.dropdown</code>にクラスを付け外ししているだけです。</p><p><code>.dropdown</code>は高さが指定されており、展開時には高さが変更されます。また、折りたたみ時のドロップダウン外への影響を考慮してか、<code>overflow: hidden</code>も指定されています。<br></p><p>ドロップダウンの中身である<code>.dropdown-content</code>は、折りたたみ時は<code>opacity: 0</code>によって不透明度が<code>0</code>となって視覚的には隠され、展開時は不透明度が<code>1</code>になって視覚的に表示されます。</p><p>それで、どうしてキーボード操作時に不具合が起きるのかです。<br>タブキーによるナビゲーション中にドロップダウン内部のリンクにフォーカスがあたることで、<code>overflow: hidden</code>になっている<code>.dropdown</code>内が強制的にスクロールされ、該当リンクが表示可能領域に移動してきます。<br>フォーカスが当たったリンクが<code>.dropdown</code>の表示可能領域に移動してきても、それは<code>opacity: 0</code>の状態なので、視覚的には何も表示されなくなる、というわけです。<br>そして、トリガーとなる<code>.dropdown-trigger</code>はタブキーによるフォーカスが行われませんので、ドロップダウンUIの表示可能領域に戻ってこれなくなります。</p><p><code>opacity</code>プロパティは不透明度を変更するだけのプロパティなので、その値を<code>0</code>にしても視覚的に見えなくなるだけで、要素ボックス自体はそこにあり、操作もできます。<br>また、<code>overflow: hidden</code>な要素の表示領域から飛び出た要素であっても、フォーカス可能要素ならタブキーによるキーボード操作で到達できます。さらに今回の場合はそれがスクロールによって描画可能な場所にありました。<br>これら2つの動作を想定していないために前述のドロップダウンは不具合を起こしているのです。</p><p>これをキーボード操作でも問題なく動作するように修正する方法はいくつもあるでしょうが、とりあえず応急処置的にHTMLとCSSだけ調整してみましょう。<br><a href="https://unformedbuilding.com/demo/2020/keyboard-unfriendly-dropdown/example-2.html">修正したデモ</a>をご覧ください。</p><pre><code class="language-html">&lt;div class="example">
  &lt;dl class="dropdown">
    &lt;dt>
      &lt;button type="button" class="dropdown-trigger">リンク集&lt;/button>
    &lt;/dt>
    &lt;dd class="dropdown-content">...&lt;/dd>
  &lt;/dl>
&lt;/div></code></pre><pre><code class="language-css">.dropdown-trigger {
  /* button要素特有のスタイルを消す */
}

.dropdown-content {
  visibility: hidden;
  opacity: 0;
  transition-property: all;
  transition-duration: 0.2s;
}

.dropdown.open .dropdown-content {
  visibility: visible;
  opacity: 1;
}</code></pre><p>トリガーを<code>dt</code>から<code>button</code>要素へと変更しました。<br>これにより、トリガーにもキーボード操作でフォーカスが当たるようになりました。<br>今回の場合、<code>dt</code>から<code>button</code>へのクラス名の移動はしなくてもいいのですが、名前と実態が合わなくなるので移動しました。</p><p><code>.dropdown-content</code>に<code>visibility</code>プロパティを使うことで、視覚的に見えないときは非表示という状態にしてフォーカスが当たらなくなりました。<br><code>visibility</code>プロパティはアニメーション可能なので、<code>transition</code>プロパティによっていい感じに遷移アニメーションしてくれるでしょう。</p><p>トリガーは<code>dt</code>要素のままでも、<code>tabindex</code>属性とイベント処理で<code>button</code>要素と似た動作を作ることはできるのですが、面倒なのでおすすめできません。<br>HTMLを変更できるならそちらのほうが楽です。<br>たとえば、<code>button</code>要素が持っている機能の一部をリストで書き出してみます。</p><ul><li>タブキーでフォーカス可能</li><li><code>disabled</code>な状態を持てる</li><li>エンターキーでクリックできる（<code>keydown</code>時）</li><li>スペースキーでクリックできる（<code>keyup</code>時）</li><li>スペースキーはスクロール動作を起こす（<code>keydown</code>時）が、<code>button</code>要素にフォーカスしているときは発生しない</li></ul><p>今回のドロップダウンに関係するものだけでもこのようになります。<br>これをJavaScriptで書いていくより、<code>button</code>要素を使ったほうが早いでしょう。</p><p>HTMLもCSSも、またはJavaScriptも、もっとよくできるだろう、という意見もあると思います。<br>今回はとりあえずドロップダウンを動作させることだけを目的にしたので、このような修正を行いました。</p><p><code>overflow: hidden</code>と、<code>visibility</code>プロパティを伴わない<code>opacity</code>プロパティの組み合わせで発生し、視覚的ブラウジングをするキーボード操作のユーザーのみに発生する不具合というものは珍しかったので、とても参考になりました。<br>また興味をひかれるUIの不具合を見かけたら紹介したいと思います。</p>]]></content:encoded></item><item><title><![CDATA[「検索メモ」というものを始めた]]></title><link>https://unformedbuilding.com/articles/about-search-memos/</link><description><![CDATA[少し前から「検索メモ」というコンテンツを始めたので、そのモチベーションとかの話です。]]></description><pubDate>Fri, 18 Sep 2020 11:49:14 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/about-search-memos/</guid><content:encoded><![CDATA[<p>少し前から「<a href="https://unformedbuilding.com/memos/">検索メモ</a>」というものを始めました。</p><section><h2>モチベーションとか</h2><p>日頃からいろいろなことを検索してTwitterでツイートしたりしているのですが、ツイートだとタイトルとURLでかなり文字数を使ってしまいます。<br>文字数に制限があるTwitterではそれ以外に何か書こうとしてもほとんど書けないので、せっかく調べたことを残せません。<br>オンラインで閲覧可能なメモ用サービスとかを使うことも考えましたが、どうもあの手のツールは向いていないようで、すぐに使わなくなってしまいます。<br>結果、自分のサイトに置いておけばいいではないかということになりました。</p><p>実際にやってみて、普通のブログ記事にするには短すぎたり中途半端すぎたりするような、雑なものでも調べた部分だけメモっておけるというのはかなり気が楽です。</p><p>いまのところ楽しくできているので、飽きるまでは続けようと思います。</p></section><section><h2>技術的な部分</h2><p>モチベーションを保つために、雑に書いて雑に公開できる、ということはとても重要でした。<br>メインのブログ記事はHTMLで書いてGitHubにPushして公開というフローですが、きっちりできる反面、雑な公開には向いていません。</p><p>かといっていまさらCMSをインストールして……とかはやりたくなかったので、どこかのブログサービスを使うとか、それこそ<a href="https://note.com/">note</a>を使うとかも考えました。<br>結論としては、<a href="https://www.11ty.dev/">Eleventy</a>で作っているんだからヘッドレスCMSを使えばいいじゃないかということで、<a href="https://microcms.io/">microCMS</a>を使うことにしました。</p><p>microCMSで1項目ごとにタイトルと本文だけの記事を作成し、EleventyでAPIを叩いて記事をすべて取得。<br>microCMSのデータには自動的に公開日時や更新日時が入るので、データ内の公開日ごとに記事を整理し、日付ベースでページを作成するという処理をしています。</p><p>普段の記事はHTMLですが、検索メモはMarkdownで書いています。これも雑にやるという目的があります。<br>しかしMarkdownの範囲を超えるようなもの（画像を<code>figure</code>でマークアップする、など）はどうにもならないので、そこはHTMLで書いています。</p></section><section><h2>内容について</h2><p>検索メモの内容はバラバラです。<br>JavaScriptライブラリだったり、動物だったり、食べ物だったり、本当に興味本位で検索したものや偶然知ったものをメモしています。</p><p>動物のメモには写真がある場合もあるので、そういうのが苦手な人はその日付をスキップしたほうがいいかもしれません。</p></section><p>以上です。<br>興味がわいたら暇つぶしにでも覗いてみてください。</p>]]></content:encoded></item><item><title><![CDATA[『ウェブタイポグラフィ』という本のレビューに参加した]]></title><link>https://unformedbuilding.com/articles/web-typography-book/</link><description><![CDATA[2020年6月下旬発売の『ウェブタイポグラフィ』という書籍に、レビュワーとして参加したので宣伝です。]]></description><pubDate>Wed, 24 Jun 2020 08:48:56 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/web-typography-book/</guid><content:encoded><![CDATA[<p>株式会社ボーンデジタルから発売される『<a href="https://www.borndigital.co.jp/book/18440.html">ウェブタイポグラフィ─美しく効果的でレスポンシブな欧文タイポグラフィの設計</a>』という書籍のレビューに参加しました。<br>書籍レビューは初めてでしたが、とてもいい経験になりました。</p><p>ここからは紹介と感想を兼ねた宣伝です。<br></p><p>副題に書かれているとおり欧文タイポグラフィについての本ですので、日本語環境下においてはそのままでは使いづらい（または、使う機会がない）テクニックもあります。<br>しかし、ウェブページ上で文字組みをするには言語を問わない問題は多々あります。そういった点における解決方法やテクニックは日本語環境においても応用できます。</p><p>また、当然ながら欧文には欧文特有の、和文とは異なる歴史やウェブでの文字組みにおける問題点や目標があります。<br>なぜそれを目指すのか、それらにどう対処するかが解説されていています。</p><p>CSS関連でいうと、レイアウトの組み方、テーブルの扱いなど大きな部分から、サブピクセル・レンダリングへの対処といった小さな部分まであります。<br>かなりの部分にCSSのコード断片が掲載されていますので、デザイナーだけでなくCSSをメインに書くデベロッパーにも役立つと思われます。</p><p>個人的にもっとも興味深かったのは「かつて段落間に1行スペースを設けることは贅沢であった」などの細かな歴史の部分です。</p><p>わたしの紹介ではあまり魅力的に感じないかもしれませんが、ウェブでの文字組みに興味がある人なら役職を問わずに楽しめるはずです。</p><p>オンラインショップでは6月29日発売です。興味を持っていただけたら手にとってもらいたいです。</p><figure class="amazon"><a href="https://www.amazon.co.jp/o/ASIN/4862464769/pnr-22" style="max-width: 200px"><img alt="『ウェブタイポグラフィ─美しく効果的でレスポンシブな欧文タイポグラフィの設計』書影" height="500" src="https://images-na.ssl-images-amazon.com/images/P/4862464769.09.LZZZZZZZ" width="354"></a><figcaption><a href="https://www.amazon.co.jp/o/ASIN/4862464769/pnr-22">ウェブタイポグラフィ─美しく効果的でレスポンシブな欧文タイポグラフィの設計</a></figcaption></figure>]]></content:encoded></item><item><title><![CDATA[スクロール・コンテナーへのアクセス]]></title><link>https://unformedbuilding.com/articles/accessing-scroll-containers/</link><description><![CDATA[ページ内に存在する、スクロール可能な要素へは、マウスやキーボードを利用してちゃんとアクセスできるのでしょうか。]]></description><pubDate>Wed, 11 Mar 2020 10:34:15 GMT</pubDate><guid isPermaLink="true">https://unformedbuilding.com/articles/accessing-scroll-containers/</guid><content:encoded><![CDATA[<p>ウェブページ内には、しばしばスクロール可能な要素が存在します。スクロール可能な要素のことをスクロール・コンテナーと呼びますが、それらの要素のコンテンツをすべて閲覧するにはスクロールが必要になります。<br>何を当たり前のことを言っているのかと思われるでしょうが、マウスのみ、またはキーボードのみの環境でもスクロールを完了させることが可能なのでしょうか。</p><section><h2>キーボード操作のみでのアクセス</h2><p>ホイール機能のついたマウスにおいては、スクロール・コンテナーまでカーソルを移動し、ホイールを実行することでスクロールが可能です。</p><p>それに対し、キーボードではスクロール・コンテナー自身、またはそのコンテンツ内にフォーカスを移動しなければカーソルキーや、その他スクロール操作を行うキーによるスクロール動作を行うことができません。</p><p><code>iframe</code>や<code>textarea</code>など、初期状態でスクロールが可能な要素は、<code>tabindex</code>属性を指定せずともキーボード・ナビゲーションによるアクセスは基本的に可能です。<br>しかし、そうではない要素に対し、ページの著者がCSSで<code>overflow</code>プロパティ（<code>overflo-x</code>、<code>overflow-y</code>も含む）を<code>scroll</code>や<code>auto</code>にした場合はどうでしょうか。</p><p>「<a href="https://html.spec.whatwg.org/multipage/interaction.html">HTML Living Standard - 6 User interaction</a>」の「<a href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">Focusable area</a>」には次の文が含まれています。</p><figure><blockquote cite="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area"><p>The scrollable regions of elements that are <a href="https://html.spec.whatwg.org/multipage/rendering.html#being-rendered">being rendered</a> and are not <a href="https://html.spec.whatwg.org/multipage/interaction.html#expressly-inert">expressly inert</a>.</p></blockquote><figcaption><cite><a href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">HTML Living Standard - 6 User interaction - Focusable area</a></cite></figcaption></figure><p>inert（不活性）ではないスクロール領域はフォーカス可能である、ということがHTMLの仕様に掲載されていることがわかります。</p><p><a href="https://unformedbuilding.com/demo/2020/accessing-scroll-containers/1.html">デモを作成しました</a>ので、ご確認ください。<br>以下はデモページのHTMLとCSSの断片です。</p><pre><code class="language-html">&lt;div class="content">
  &lt;p class="scrollable">
    ...
  &lt;/p>
&lt;/div></code></pre><pre><code class="language-css">:focus {
  outline-width: 2px;
  outline-style: solid;
  outline-color: skyblue;
  outline-offset: 2px;
}

.content {
  border-width: 1px;
  border-style: solid;
  border-color: gray;
  padding: 1em;
  width: 20em;
}

.scrollable {
  margin: 0;
  overflow: auto;
  height: 10em;
}</code></pre><p>それぞれの<code>p</code>要素には<code>overflow: auto</code>が指定されています。3種類のケースの違いは次のとおりです。</p><dl><dt>Case 1-A</dt><dd>スクロールの必要がない程度のコンテンツ</dd><dt>Case 1-B</dt><dd>スクロールが必要な量のコンテンツ</dd><dt>Case 1-C</dt><dd>スクロールが必要な量のコンテンツで、内部にボタンがある</dd></dl><p>どうですか、すべてのケースでマウス、またはキーボードで最後まで見られましたか。<br>キーボードのみの操作ではFirefox以外を利用している方は見られなかったのではないかと思います。</p><p>先ほど示した仕様に準拠しているのは、2020年3月現在、Firefoxのみです。</p><p>Chrome（というよりBlinkエンジン）では該当イシュー「<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=585413">Issue 585413: Scrollable area does not get focus with TAB focus navigation</a>」は2019年にFixedになっているものの、動作していないように見えます。<br>これは現段階ではフラグ付きの実装のためです。<span class="path">chrome://flags</span>から「Experimental Web Platform features」を有効にすることで動作を確認できます。</p><p>Safari（Webkit）については特に見つかりませんでした。<br>またMicrosoft Edge レガシ、Internet Explorerでも動作しません。</p><p>このような状況で、スクロール・コンテナー内のコンテンツにアクセスするにはどうしたらよいのでしょう。<br>キーボードでフォーカス可能な要素がコンテンツ内にある場合はスクロール位置がそこまで移動してしまうものの、要素のスクロール自体はキーボードのみで可能です。<br>そうではない場合、スクロール・コンテナーへフォーカス可能でないブラウザーの利用者はキーボードでアクセスできないのでしょうか。</p><p>思いつくのはスクロール・コンテナーである要素に<code>tabindex</code>属性を付与し、その値を<code>0</code>以上にすることです。<br>これはシンプルでスマートな解決法に見えます。</p><p>該当要素に<code>overflow: scroll</code>が指定されているのならば、この方法はうまくいくでしょう。</p><p>しかし、<code>overflow: auto</code>の場合はどうでしょうか。スクロールの必要がない量のコンテンツの場合、特に意味のないフォーカス可能要素が生まれてしまいます。致命的な現象ではありませんが、違和感のある動作になってしまいます。</p><p>これを解消するには、JavaScriptの力が必要となります。<br>該当要素の矩形と、コンテンツ量に応じて必要とされる矩形を比較し、<code>tabindex</code>属性をつけ外しすることになります。<br>また、レスポンシブなレイアウトにおいては、要素のサイズを監視する必要もあるでしょう。</p><p>当然それには制作コストがかかります。<br>Firefoxは対応済み、Blink系統のブラウザーもそのうちフラグが外れるでしょう。こういった状態で追加コストで対応するかどうかはチームで判断するしかないでしょう。</p><ul><li>ページ内のコンテンツにスクロールが必要なパーツを作らない</li><li>スクロールの必要があるのならば、<code>overflow: scroll</code>と<code>tabindex</code>属性を指定し、コンテンツ量によってはスクロールの必要がない状態でもスクロールバーが表示されるのを許容する</li><li>それもできないのならばJavaScriptで頑張る</li><li>そもそも仕様ではスクロール・コンテナーはフォーカス可能であり、対応も進みつつあるので何もしない</li></ul><p>といった選択肢があります。<br>いえ、わたしが思いつかないだけできっともっとあるでしょう。</p></section><section><h2>マウスとキーボードを併用している場合</h2><p>レアケースかと思いますが、マウスでスクロール・コンテナーにアクセスし、スクロール操作自体はキーボードで行う場合があります。</p><p>わたしがそうなのですが、スクロール・コンテナー内の任意を場所をクリックし、カーソルキーでスクロールをすることがあります。<br>事情としてはマウスを頻繁に長時間使い続けて右手首の腱鞘炎を起こしたことがあり、マウスに触れる時間を少しでも減らそうとした、というものです。</p><p>マウスの利便性を保ちつつ、非常に単純な操作はキーボードで済ませる、という使用方法です。</p><p>既出のデモページで、同様の操作（スクロール・コンテナー内をクリックしたあとでカーソルキーでスクロール）を行ってみてください。<br>問題なく操作できるはずです。</p><p>しかし、この操作方法が不可能なパターンがあります。<br>それはスクロール・コンテナーの先祖に<code>tabindex</code>が指定されている場合です。</p><p>先ほどと同様に<a href="https://unformedbuilding.com/demo/2020/accessing-scroll-containers/2.html">デモを作りました</a>のでご覧ください。<br>1つ目のデモのスクロール・コンテナーの親要素に<code>tabindex="0"</code>を追加したものです。</p><p>スクロール・コンテナー内をクリックすると、その親にフォーカスを奪われるのでスクロール・コンテナーをキーボードで操作できません。<br>これはスクロール・コンテナーにキーボードでフォーカス可能なFirefoxでも同様です。</p><p>これを回避するには、やはりスクロール・コンテナー自体に<code>tabindex</code>属性を付与するしかないのですが、すでに述べたように、この方法にはいくつかの解決すべき問題があります。</p><p>この現象に遭遇するパターンとしては、わたしのような使い方をしている場合、または、ホイール機能のないマウスを利用していて、結果としてわたしと同じような操作をしている場合でしょう。</p><p>非常に限定的なパターンかつレアケースではあると思いますが、このようなシチュエーションも存在します。</p></section><hr><p>この記事を書くきっかけとしては「<a href="https://shb.unformedbuilding.com/">しんぷるはてぶ</a>」を作っていたときにキーボードでうまくスクロール操作ができなかったことです。シチュエーションとしてはマウスとキーボードの併用です。<br>なお、「しんぷるはてぶ」ではスクロールが必要なコンテンツには大量のリンクが存在し、アクセスするのに不便ではないので特別な対応はしていません。</p><p>現象に気づいた際に話し相手になってくれた矢倉さん（<a href="https://twitter.com/myakura">@myakura</a>）、ありがとうございました。</p><p>以下は参考資料です。</p><ul><li><a href="https://html.spec.whatwg.org/multipage/interaction.html">HTML Living Standard - 6 User interaction</a>（<a href="https://momdo.github.io/html/interaction.html">日本語訳</a>）</li><li><a href="https://www.chromestatus.com/feature/5231964663578624">Keyboard-focusable scroll containers - Chrome Platform Status</a></li><li><a href="https://allyjs.io/data-tables/focusable.html">Focusable Elements - Browser Compatibility Table</a></li></ul>]]></content:encoded></item></channel></rss>
