先日公開した記事「CSS コンテナクエリ考察」のなかで、コンテナクエリの課題のひとつとして検証方法が確立していない点を取り上げました。
この問題を緩和させるために、対象要素をリサイズ可能にしてコンテナクエリ(@container)のスタイルを検証できる Bookmarklet「cq-resizer」を作成しました。
Bookmarklet
見出し「Bookmarklet」今回作成した Bookmarklet は、以下のリンクをブラウザのブックマークバーにドラッグ & ドロップすることで登録できます。
Bookmarklet のコードは、以下の GitHub Gist にもアップしています。
https://gist.github.com/griponminds/506f75ae0aa7d12e72a85571727a635f
Bookmarklet とは
Bookmarklet(ブックマークレット)は、ウェブブラウザのブックマークから JS のコードを実行する仕組みのことをいい、javascript:
スキームを即時関数で実行することによって実現します。
javascript:(() => {
/* 実行するスクリプト */
})();
例えば、以下は閲覧している記事の title
を prompt
で表示する Bookmarklet です。
javascript:(() => {
prompt('title', document.title);
})();
まず、以下の手順で Bookmarklet を登録します。
- 適当なウェブページをブックマーク(このページでも可)
- ブックマークを編集して、URL を JS のコードに差し替える
- ブックマーク名をわかりやすい名前に変更(任意)
試しに、先ほどの Bookmarklet を登録して実行すると、ダイアログが表示されテキストフィールドにページの <title>
が表示されます。
デベロッパーツールのコンソールに、JS コード全体をペーストしても実行可能です。
なお、Bookmarklet は JS なので、以下の制約が挙げられます。
- Content Security Policy(CSP)の指定内容によっては、スクリプトの実行が拒否される
- ブラウザの拡張機能や、ドキュメントで読み込んでいる JS や CSS と競合する場合がある
<iframe>
や Shadow DOM の内側には、基本的にはアクセス不可- 異なるオリジンのリソースには、基本的にはアクセス不可
また、ブックマークとして登録するときに 1 行のコードになるため、JS を記述するときには以下の点に気をつける必要があります。
- 行末のセミコロン(
;
)を省略しない - コメントは複数行コメント(
/* */
)を使用する
以下は、コンテナクエリを指定したデモです。登録したブックマークを選択して Bookmarklet を実行してみてください。
Bookmarklet を実行すると、以下のように 5 つの基準コンテナ(container-type
プロパティが指定されている要素)の右下にリサイズハンドルが追加され、リサイズが可能になります。加えて、画面の右上にはインフォパネルが追加されます。
インフォパネル
見出し「インフォパネル」インフォパネルは以下の要素で構成されています。
項目 | 内容 |
---|---|
タイトル | Bookmarklet の名称 |
Close ボタン | インフォパネルを閉じるボタン |
No. | 通し番号 |
セレクタ名 | 基準コンテナの class もしくはタグ名 |
横幅 | 基準コンテナの横幅(px) |
チェックボックス | リサイズのオン・オフ |
タイトルをドラッグすることで、インフォパネルを移動できます。Close ボタンでインフォパネルを閉じると移動した位置はリセットされます。
セレクタ名にはアンカーリンクが付与されており、選択すると該当の基準コンテナの垂直位置までスクロール移動します。なお、セレクタ名は class
属性の値が表示されますが、class
の指定がない場合には HTML の要素名を表示します。
横幅のフィールド(<input type="number">
)と基準コンテナの幅は連動しているので、フィールドの数値から基準コンテナの横幅を変更することもできます。
チェックボックスでは、基準コンテナのリサイズ指定のオン・オフを切り替えることができます。
既知の問題
見出し「既知の問題」現時点では以下の問題を認識しています。
リサイズハンドルの表示
見出し「リサイズハンドルの表示」Safari において、基準コンテナの上に背景を持つ子孫要素が重なると、リサイズハンドルが隠れてしまうことがあるようです。
なお、Google Chrome や Firefox のデバイスモード下では、リサイズハンドルが小さく表示されますが、かろうじてリサイズすることはできるようです。
Flexbox
見出し「Flexbox」基準コンテナが flex アイテムとして他の要素と並んでいる状態だと、リサイズハンドルでの操作や、フィールドの数値からスムーズにサイズを変更できないことがあります。
スタイルの競合
見出し「スタイルの競合」基準コンテナに style
属性を追加するため、すでに style
属性が付与されている場合には、スタイルが競合して動作や表示に影響を及ぼす可能性があります。
対象外
見出し「対象外」<iframe>
の内部(「CodePen」のコードなど)や、ShadowDOM の内部で使用されている基準コンテナは対象外です。
実装内容
見出し「実装内容」ここからは、Bookmarklet の実装内容の一部を紹介します。
基準コンテナへのスタイル追加
見出し「基準コンテナへのスタイル追加」まず、ページ内のすべての HTML 要素のなかで、CSS の container-type
プロパティが指定されている要素(基準コンテナ)を検索し、見つからない場合にはメッセージを表示します。
次に、基準コンテナのなかで、親子関係にある要素で、水平・垂直位置が同じ場合には子孫要素を除外しています。これは、リサイズハンドルが重なることで操作が複雑になるのを避けたいのと、親要素側でリサイズできれば十分だというのが理由です1。
続いて、このフィルタリングされた基準コンテナにスタイルを追加します。指定されている container-type
の値に応じて、以下のようにスタイルを振り分けています。
inline-size
の場合は、resize: horizontal
とoverflow-x: auto
を追加size
の場合は、resize: both
とoverflow: auto
を追加
これにより、基準コンテナの右下にリサイズハンドルが表示され、ドラッグしてサイズを変更することができるようになります。
さらに、インフォパネル内のセレクタ名に付与されているリンクの遷移先となるアンカー要素を生成し、不可視化したうえで、position: absolute
で配置しています。
Shadow DOM
見出し「Shadow DOM」インフォパネルは Shadow DOM として実装しています。
2024/06/18 追記
Shadow host(:host
)は、外側のスタイルの影響を受けるので、all: revert-layer
を !important
フラグとともに指定しています。
また、Shadow tree の内側のスタイルは、外側のスタイルから直接的には影響は受けませんが継承はされるため、影響を受けそうなプロパティに初期値を設定しています。
:host {
all: revert-layer !important;
}
@layer reset {
:host {
writing-mode: horizontal-tb;
font-style: initial;
font-weight: initial;
font-variant: initial;
line-height: initial;
letter-spacing: initial;
text-align: initial;
text-indent: initial;
text-transform: initial;
word-spacing: initial;
text-shadow: initial;
cursor: initial;
}
}
初期化の指定は少しやりすぎな気もしますが(とはいえ完璧でもないですが)、より適切な方法があれば調整していきます。
revert-layer
のテクニックは以下の記事で紹介しているものですが、どのような副作用があるのかを検証する目的も兼ねて試験的に導入しています。
おわりに
見出し「おわりに」今回作成した Bookmarklet は、当初は基準コンテナをリサイズできる機能だけを想定しており、プライベートでの使用しか考えていなかったのですが、コードを書いていくうちに機能が充実していったので、せっかくなので公開することにしました。
まだ、ほとんど自分のサイトでしか試せていないので、使っていくうちに予測していない不具合が見つかるかもしれません。コードは今後も適宜調整していきますが、もしお気づきの点がございましたらお知らせください。
脚注
-
親子要素がそれぞれ
inline-size
とsize
の場合にも子孫要素が除外されますが、まれなケースだと考えられるので、ひとまず対応は保留です。 ↩