Chrome122
Chrome122 がリリースされました!
以前も書きましたが Iterator Helpers が再度使えるようになりました

Array(5).keys().map(x => `(${x})`).drop(2).toArray()
// ['(2)', '(3)', '(4)']

色々なところで [...values] を使って一旦配列化する手間が減ります

また Set の追加メソッドも使えるようになりました
結構便利です

const a = new Set([1, 2])
const b = new Set([2, 3])
const c = a.union(b)

a // Set(2) {1, 2}
b // Set(2) {2, 3}
c // Set(3) {1, 2, 3}

他にもありますが基本的な Set のメソッドです

intersection → 重複する要素の Set
symmetricDifference → どちらかにある要素の Set
a.difference(b) → a から b を除外した要素の Set
a.isSupersetOf(b) → a が b の要素すべてを含むと true
a.isSubsetOf(b) → a の要素すべてが b に存在すると true
isDisjointFrom → 重複する要素がないと true

Set を返すメソッドはどれも新規に Set オブジェクトを作るもので破壊的なものではないです
AVIF を使おう
画像フォーマットの AVIF 形式ですが Edge 121 でやっと使えるようになったようです
https://caniuse.com/avif

Chrome では数年前から使えて Safari も 1 年以上前から使えたのですが Edge だけが使えなかったです
Chrome で表示できるのだから Edge でも表示できていいのに無効化されていました
それがとうとう使えるようになりました

前から JPEG はそろそろ捨てたいと思って WebP を使ったりもしましたが どうせなら更に新しい AVIF にしたいなと思って期待していたので待ちに待った機能です

ただし今はまだ表示できるだけ
Canvas からの出力で image/avif を指定しても対応してないです
WebP みたいにブラウザ上で作れるともっと身近になるのですけどね

あとライブドアブログは対応してないので 手元で AVIF 画像で管理していてもページに貼り付けるときは JPEG/PNG に変換するしかないです
WebP すら対応してないのであまり期待はできないです
この辺はそろそろどうにかしてほしいところです

ところで AVIF より新しい形式に JPEG XL というのもあるそうです
JPEG という名前の時点で悪いイメージしか無いですし使おうと思いません (一応性能的には AVIF よりも上という情報も見かけます)
AVIF のときは WebP より AVIF がいいなと思いましたが JPEG XL は別にいいかなってところです
それに Chrome は以前 JPEG XL を実験的にサポートしていたものの現在ではサポートをやめたそうです
AVIF で良さそうですね
ウェブページ内で Chrome の DevTools を使う
Solid.js の Playground では ページ内に Chrome の DevTools が埋め込まれています(右下)
https://playground.solidjs.com/

どうなってるのと思ってソースコードを見てました
https://github.com/solidjs/solid-playground

chii というプロジェクトで DevTools の画面を作ってるようです
ウェブフロントエンドで動くようにパッチを当てていますが DevTools のソースコードが使われてます
また DevTools と接続するページ側では chobitsu というライブラリを使って DevTools との通信を管理してるようです
chobitsu は CDP の JavaScript 実装らしく CDP ライブラリなら他にも類似のものは色々ありそうですが chii との通信専用に独自の部分があるのかもです

Vue にしてもそうですが中国のライブラリって日本アニメの名前が使われる傾向があるみたいですね

Solid.js の Playground のページは Solid.js が前提になっていたり外部から更新できる画面になっていて複雑だったのでシンプルに最低限の機能で動くページを作ってみました
Solid.js の Playground のページから多くコードを流用してます
https://nexpr.gitlab.io/public-pages/chrome-devtools/

仕組みとしてはメインのページがあってその中に 2 つの iframe があります
片方が DevTools の画面で もう片方が DevTools と接続する画面です
それぞれの画面が親フレームにメッセージを送るようになっているので メインのページではメッセージを相手側に送信するようします

Elements タブで要素にマウスを乗せると対応する部分に色がついたり要素サイズが表示されたり ちゃんと DevTools の動きをしています
Console タブで JavaScript コードを実行できますし DOM を更新すればそれに対応して画面も変わります

ほとんどいつもの DevTools と変わらないですが 制限も色々あります
コンソールから Sources タブに飛んでエラー箇所を確認できなかったり
デバッグ実行ができなかったり
Elements タブで一部のスタイルが適用されているのに確認できなかったり(外部 CSS がだめなのかも)

すごいライブラリではあるのですが 制限があることで中途半端になりますし普通に DevTools を出せばいいかなと思うので今後使うかというと使わないかもです
DevTools が日本語だとコマンドが辛すぎる
もう結構前からですが Edge で DevTools を起動すると日本語になってます
Chrome では上の方に日本語にもできますよって通知が出るだけでデフォルトは英語です

以前は英語に戻してましたが 環境ごとに毎回設定変更は面倒なので最近はそのままのことも増えました
ボタンなどの場所は変わらないので ネットで見かけた設定等を試す時以外は日本語でも困ってなかったです……が コマンドを使うと辛かったです

Ctrl-Shift-P で出るやつです
VSCode と同じ感じ

ここも日本語になってるので コマンドを日本語で打たないといけないです
選択肢の候補すら日本語です
漢字に変換しないと出てこないですし ひらがなカタカナも区別されています

最終的なコマンドが日本語なのは別にいいとしても フィルタして選択肢を出すところでは 元の英語版のコマンドからも検索してほしいですし ひらがなでカタカナと漢字も検索してほしいです
Microsoft あるあるかもですが必要以上のローカライズって不便になるだけだと思うんですよね
過去バージョンの Chrome の動作を確認する
npx @puppeteer/browsers install chrome@115

みたいな感じで 指定バージョンの Chromium をインストールできる
現時点では 113 以降がインストールできる
112 や 111 は 404 エラー

Puppeteer が使用してるデータソースがこの辺で 113 以降しかデータがない
https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone.json
https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.json

最新が 123 だし 最新+過去 10 件だけ残して古いのは消されてるのかも

それ以前なら この辺からダウンロード
https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html

Windows 用
https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win_x64/

ここでダウンロードする場合は Chrome のバージョンじゃなくて通しのビルド番号で指定する必要あり
バージョンとの対応の参考

{
"113": {
"milestone": "113",
"version": "113.0.5672.63",
"revision": "1121455"
},
"114": {
"milestone": "114",
"version": "114.0.5735.133",
"revision": "1135570"
},
"115": {
"milestone": "115",
"version": "115.0.5790.170",
"revision": "1148114"
},
"116": {
"milestone": "116",
"version": "116.0.5845.96",
"revision": "1160321"
},
"117": {
"milestone": "117",
"version": "117.0.5938.149",
"revision": "1181205"
},
"118": {
"milestone": "118",
"version": "118.0.5993.70",
"revision": "1192594"
},
"119": {
"milestone": "119",
"version": "119.0.6045.105",
"revision": "1204232"
},
"120": {
"milestone": "120",
"version": "120.0.6099.109",
"revision": "1217362"
},
"121": {
"milestone": "121",
"version": "121.0.6167.85",
"revision": "1233107"
},
"122": {
"milestone": "122",
"version": "122.0.6261.6",
"revision": "1250580"
},
"123": {
"milestone": "123",
"version": "123.0.6265.0",
"revision": "1252026"
}
}

これの revision の数値を使う

Chromium だと開発中バージョンで機能が全部入ってないケースがあるので どの Chrome バージョンで変更があったかを確認するのに注意が必要
例えば 110 で動かなくて 111 で動いたら 111 で追加された機能かと思うけど 試した 110 のバージョンより後の 110 のリリースで追加される場合もありえる
マイルストーンに記載されてる version と revision は その Chrome バージョンの最後のビルドになってるのでこのバージョンなら大丈夫かも
ただこの情報が取れない古いバージョンだと そのバージョンの最後のビルドの revision を探すのが大変
Array.fromAsync
来週に stable リリースの Chrome 121 で使えるようになる機能です
https://chromestatus.com/feature/5069575759069184

Array.from みたいなものですが非同期用です
返ってくるのは配列ではなく配列をラップした Promise です

Array.fromAsync([1, 2])
// Promise {<fulfilled>: Array(2)}

await Array.fromAsync([1, 2])
// [1, 2]

同期処理も非同期処理も対応してますが結果は Promise になります

await Array.fromAsync(function*() {
let i = 0
while (i < 3) yield i++
}())
// [0, 1, 2]

await Array.fromAsync(async function*() {
let i = 0
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 500))
yield i++
}
}())
// [0, 1, 2]

Array.from と for-of みたいな関係で Array.fromAsync は for-await-of で配列に入れるのと同じです

const it = async function*() {
let i = 0
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 500))
yield i++
}
}()
for await (const item of it) {
console.log(item)
}
// 0
// 1
// 2

Promise の配列に使うと Promise.all と同じ感じです

const promises = [1, 2, 3].map(x => Promise.resolve(x))
await Array.fromAsync(promises)
// (3) [1, 2, 3]
await Promise.all(promises)
// (3) [1, 2, 3]

ただし順番に await されるので thenable オブジェクトで then の中の処理がある場合 実行タイミングがずれます
それぞれに sleep のような処理がある場合は順番にスリープしていくので遅くなります
Promise.all の場合は全部の then を一気に呼び出すので一番長いものの待ち時間で済みます

const thenables = [1, 2, 3].map(x => {
return {
then: (onFulfilled, onRejected) => {
console.log(Date.now() % 10000)
setTimeout(onFulfilled, x * 1000, x)
}
}
})

Array.fromAsync(thenables)
.then((values) => console.log(Date.now() % 10000, values))
// 3842
// 4852
// 6867
// 9868 [1, 2, 3]

Promise.all(thenables)
.then((values) => console.log(Date.now() % 10000, values))
// 4857
// 4858
// 4858
// 7867 [1, 2, 3]

これまでのジェネレーターと同じですが 無限に続くところで使うと終わらないので注意が必要です
同期処理だと画面が固まったり早い段階でエラーになったりして気づきやすかったですが非同期になると少し分かりづらくなります
画面が固まらないですし Promise が解決される間隔がある程度あれば CPU 負荷もそこまでじゃないです

for-await-of ならイベントリスナのかわりみたいな使い方もありかなと思うのですが Array.fromAsync だとそういう使い方もできませんし どこで使えるのかはあまりイメージできてないです
Iterator Helpers が Chrome 122 で復活予定
期待してた機能の Iterator Helpers ですが 使えるようになってすぐにウェブ互換問題で使えなくなりました
🔗 Iterator helpers が使えなくなった

Chrome 122 (現 dev) から再度使えるようになるようです
https://chromestatus.com/feature/5102502917177344

ウェブ互換問題に対応するため 少し特別な扱いをしてるようです
toStringTag に違いがあるみたいです

https://github.com/tc39/proposal-iterator-helpers/pull/287
https://github.com/tc39/test262/pull/3970/files

実際に getOwnPropertyDescriptor で他のクラスと比較してみると

Object.getOwnPropertyDescriptor(Map.prototype, Symbol.toStringTag)
// {value: 'Map', writable: false, enumerable: false, configurable: true}

Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag)
// {enumerable: false, configurable: true, get: ƒ, set: ƒ}

ほかは Map みたいに writable が false で value に値が入ってます
Iterator では getter/setter になっています

通常利用では気にする必要ないかと思いますが 特殊なもののようです



気になったので prototype 構造がどうなってるか少し調べてみました

Iterator.prototype.__proto__ === Object.prototype
// true

const i1 = [].values()
const i2 = i1.take(1)
const i3 = (function*(){})()
const i4 = i3.take(1)
const i5 = i3.map(x => x)

// Iterator Helpers で得られるオブジェクトの prototype は
// 元やメソッドが違っても同じ

i2.__proto__ === i4.__proto__
// true

i2.__proto__ === i5.__proto__
// true

// Iterator Helpers とそれ以外は別

i1.__proto__ === i2.__proto__
// false

// いずれも Iterator を継承してる

i1.__proto__.__proto__ === Iterator.prototype
// true

i2.__proto__.__proto__ === Iterator.prototype
// true

i3.__proto__.__proto__.__proto__ === Iterator.prototype
// true

Iterator を継承したオブジェクトの toStringTag は他と同じで writable が false で value に値が入ってる

Object.getOwnPropertyDescriptor(i1.__proto__, Symbol.toStringTag)
// {value: 'Array Iterator', writable: false, enumerable: false, configurable: true}

Object.getOwnPropertyDescriptor(i2.__proto__, Symbol.toStringTag)
// {value: 'Iterator Helper', writable: false, enumerable: false, configurable: true}

Object.getOwnPropertyDescriptor(i3.__proto__.__proto__, Symbol.toStringTag)
// {value: 'Generator', writable: false, enumerable: false, configurable: true}
Chrome 120 で CSS のネスト時にセレクタをタグ名から始められるようになる
https://chromestatus.com/feature/5070369895743488

h1 {
a {
color: dodgerblue;
text-decoration: none;
}
}

これができるようになります

これまでは 内側のセレクタは記号から始める必要がありました
ID やクラスなどのセレクタは記号から始まるので問題ないですが タグ名を書くときは簡単に書けませんでした

上記の CSS だと a のセレクタは無効です
:is() を使ったり & を使う必要があります

h1 {
:is(a) {
color: dodgerblue;
text-decoration: none;
}
}
/* or */
h1 {
& a {
color: dodgerblue;
text-decoration: none;
}
}

事前に CSS を変換するツールだと 基本的にこの一手間が不要だったので少し不便です
事前に変換するなら速度がそこまで重要視されないので 先の方まで読んでしまって セレクタなのかプロパティ名なのか判断できます
しかし ブラウザだと先まで読むことを基本許可しないので タグ名から始められないのは仕方ないものでした
この辺は実装されるまで仕様を決める段階でも色々議論されてたようです

なので無理なものと思っていたのですが Chrome 120 からはそれが緩和されてタグ名から始められるようになったようです
パフォーマンス面ではやはり劣るみたいですが できる限り高速になるようしているそうです
基本は declaration (color: red; みたいの) としてパースし始めて プロパティでないと判断でき次第 rule (h1 {} みたいの) として再パースするようです
詳細な説明は仕様の Implementation note のところに書かれてました
https://drafts.csswg.org/css-syntax/#consume-block-contents

ShadowDOM を使ってるとスコープが小さくなるので タグ名だけのセレクタを使うことが多くなります
毎回 :is() を書くのに疲れてきていたので これはとても嬉しいですね
何年も前の Chrome を使い続けてる会社がある?
アクセスログを眺めてたときのこと 異常にアクセス数が増えてるページがあって 生のログを見てみると同じユーザーから 10 回以上の連続したリクエストが来てました
詳細を見るとリファラなしの直接アクセスみたいで UA を見ると古い Chrome (80 くらい)
クローラーとか自動でアクセスしてきてるやつで UA をそのときの Chrome に設定したままなんだねーと思ってました

そんなこともあるかーで流しておこうとしたら 一番最後だけ UA が変わってるのに気づきました
ほぼ最新の iPhone から同じページへのアクセスです
見直してみると連続アクセスは機械的にリクエストしたにしてはタイムスタンプが奇妙です
1 秒に何回もあるわけではなく数秒おきでアクセスされていました
アクセス間隔は等間隔ではなくばらつきがあります

もしかして本当に古い Chrome を使って人がアクセスしていたのでしょうか
80 だと 3 年半ほど前のバージョンなので たぶんページが正しく表示できないと思います
表示されないのをサーバー負荷などの問題と思って何度もリクエストを繰り返して 最終的に諦めて iPhone からアクセスしたということなんでしょうか

アクセス元のドメインを見るとお堅そうな日本の会社でした
割りとあり得るかもしれない……

今の時代ブラウザは自動更新されるものなのに古いバージョンに固定なんてありえないですよね
IE が消えて 世の中のページも最新機能を取り入れるところが増えているので見れないページが多くなります
インターネットに接続せず社内システムを見るために使うというならまだわかりますが こっちのサイトにアクセスしてる以上 インターネットにつながってます
古いバージョンは脆弱性のパッチも当たってないのにそれでネットしてるって セキュリティ的にどうなの?って思います
開発者ツールで要素を確認しようとしたら要素が消えるときの対処方法
ポップアップで表示される部分の HTML 構造を確認したいとかありますよね
でも右クリックの「検証」を押したり devtools にフォーカスするとポップアップが閉じてしまうということがあります

原因は blur や focusout イベントを使って 要素がフォーカスを失ったらポップアップが閉じるようになっているから
devtools にフォーカスをあてるとページからはフォーカスが失われて このイベントが起きてポップアップが消えてしまいます

このためにけっこうな力技で対処していました

devtools の Elements タブですべての blur と focusout イベントのリスナを削除してから ポップアップを開きます
ポップアップの要素を消す処理が行われないので ポップアップは開いたままになります
本来の動作が行われなくなるので 確認後にリロードが必要です

別の手段では setTimeout で遅延して debugger を実行します
3 秒後に debugger を実行するようコンソールで実行してから 3 秒以内にポップアップを開きます
debugger が実行されると JavaScript の処理は実行されず固まるので ポップアップが開いたまま devtools を操作できます

こんな方法を使ってましたが 正当な方法がありました

devtools で Rendering タブを開きます
出てないならメニューの More tools から選択します
リストから「Emulate a focused page」を探してチェックを入れます
これでページにフォーカスが残るので devtools にフォーカスを当ててもポップアップは消えずに残ります

デフォルトで有効になってて欲しいくらいの機能です
Chrome 117 で JSON を開いたときにフォーマット表示する機能が増えてた
新機能一覧に載ってなかったので気づいてませんでしたが Chrome で JSON ファイルを直接開いたときにヘッダーが表示されるようなっていました
ヘッダーのメニューには 「プリティ プリント」 のチェックボックスがあります

これにチェックを入れると JSON がきれいにフォーマットされて読みやすくなります

以前はこんなのなかった気がすると思って Chrome 116 で開いてみるとありませんでした
Chrome 117 の新機能のようです
これまでは JSON のフォーマットのために VSCode に貼り付けたり devtools のレスポンスで表示したりしていたので この機能があると便利になりますね

ちなみに Edge は 117 でもこの機能はありません
というか Edge ではデフォルトできれいにフォーマットされて色付けもされています
生のレスポンスを見たいときに Ctrl-U でソースコードを表示したり devtools のレスポンスで確認しないといけないです
これはこれで少し不便だったりもします

ちなみに JSON しか確認していませんが他のファイル形式でもフォーマット表示してくれるのかもしれません
max-age が効かない
キャッシュ設定を変更して動作確認していたら max-age を設定しているのに毎回サーバーに問い合わせが発生して困っていました
devtools の Network タブを見たときに (disk cache) や (memory cache) となっていてほしいのですがなってくれません
リクエストが発生して 304 が返ってきています
そのレスポンスのヘッダーを見るとちゃんと max-age が付いているのですけどリロードするとまたリクエストが発生します

色々試したところ max-age でキャッシュからロードしてくれるのはリソース扱いのときだけのようです
ブラウザで直接 HTML ファイルや JavaScript ファイルを開いた場合 これらのファイルはキャッシュを見ずにリクエストが発生するようです
HTML から script タグで JavaScript をロードしたり fetch で HTML ファイルを取得するようなケースではキャッシュが使われています

これのせいで無駄に時間を使うことになりました
前からこんな動きでしたっけ
というかこれは共通の仕様? Chrome の独自仕様?
MDN を軽く見た感じではアクセスするコンテキストで動作の違いとかはなさそうでした

◯ 確認用

require("http").createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, {
"Content-type": "text/html",
"Cache-Control": "max-age=3600",
})
res.end(`<script src="/a.js"></script><h1>HTML</h1>`)
return
}
if (req.url === "/a.js") {
res.writeHead(200, {
"Content-type": "text/javascript",
"Cache-Control": "max-age=3600",
})
res.end(`console.log(1)`)
return
}
res.writeHead(404)
res.end(`NOT FOUND`)
}).listen(8000)

「http://localhost:8000」 にアクセスして devtools を開いてリロードします
a.js のほうだけキャッシュが使われてるはずです
「fetch("/")」で / にアクセスすると HTML ファイルもキャッシュが使われます

こういう動きだと HTML もキャッシュさせたい場合は E-Tag や LastModified を使って 304 を返せるようにしておいたほうが良さそうですね

SPA を作るときは HTML ページだけはエントリポイントとして更新するために max-age 付けたらダメなので例外にする工夫が必要だったのですが それがいらないという点では便利ですね



リロードで確認すると max-age が無視されるみたいです
https://blog.jxck.io/entries/2023-11-05/reload-and-cache.html

リンクを用意して移動してきた場合はページを開くリクエストでもキャッシュが使われてました
ページを開くリクエストでキャッシュが無効化されるなら SPA のエントリポイントなどの HTML もまとめて max-age をつけておいても大丈夫かもと思ったのに そうはいかなそうです
HTML にも max-age をつけると外部から移動してきたときだけキャッシュが使われて最新版にならない問題が起きてしまいます
CSS の position:sticky で固定されたときだけスタイルを当てれるようになる
Chrome の現バージョン 116 で DevTrial です
https://chromestatus.com/feature/5072263730167808

詳しい説明
https://lilles.github.io/explainers/state_container_queries.html

position: sticky でスクロールしたときに要素を簡単に固定できるようになりましたが 固定されてるときだけスタイルを変えたいということはよくありました
それができるようになります

使い方はコンテナクエリを使って

@container state(stuck: top) {
#sticky-child { font-size: 75% }
}

のようになるそうです

HTML の例だと

<style>
#sticky {
container-name: my-menu;
container-type: sticky;
position: sticky;
top: 0px;
height: 100px;
}

#sticky-child {
background-color: orange;
color: white;
height: 100%;
}

@container my-menu state(stuck: top) {
#sticky-child { width: 50%; }
}
</style>
<div id="sticky">
<div id="sticky-child">
Sticky
</div>
</div>

できるようになるのは嬉しいのですが 少し手間なのが難点ですね
コンテナクエリが必須ですし contaner-type の指定も必要です

お手軽に擬似クラスで :stuck でいいと思うのですが パフォーマンスの懸念など色々あってコンテナクエリになったようです
CSS に相対カラーの構文が追加されるみたい
CSS Relative Color Syntax (RCS) というのが Chrome 118 で DevTrial のようです
https://chromestatus.com/feature/5205844613922816
https://www.w3.org/TR/css-color-5/#relative-colors

こういう感じです

div {
background: rgb(from var(--bg-color) r g b / 80%);
}

rgb や hsl などの色空間の関数に from で元になる色を指定してから 次に各値を指定します
構文的には

rgb(from 元の色 R G B / 透過率)

rgb 関数なので R と G と B を指定します
透過率は省略可です

R と G と B には これまでの rgb 関数のように 0~255 の数値や%で指定できます
相対と言いつつもこの%は 100% が 255 に相当するもので 元の色に対する割合ではありません

rgb(from red 100% 100% 100%)

と書けば rgb(255, 255, 255) = #ffffff と同じです
red は意味のないものになります

from の色を使うには 数値と%以外の特殊なもので r と g と b を使います
これを使うと元の色の R と G と B の値になります

rgb(from red r g b)

と書くと元の色そのままです

rgb(from red r r r)

と書くと G と B も R の値 (255) になるので rgb(255, 255, 255) です
元の色を使って計算するときは calc を使います

rgb(from rgb(100,100,100) calc(r * 1.1) calc(g * 1.5) calc(b * 2))

これの結果は rgb(110, 150, 200) になります
足し算で直接数値を足したい場合は小数値で扱うことになります
1 が 255 に相当します

rgb(from rgb(100,100,100) calc(r + 0.5) calc(g + 0.1) calc(b + 1))

これの結果は rgb(228, 126, 255) になります
0.5 は 128 なので 100 + 128 = 228 です
1 を足すと 255 を超えてしまうので 255 になります

rgb で使うよりも hsl で hue だけを変えたいみたいなケースのほうが使うかもですね
Chrome 118 で HTML の search 要素に対応するみたい
https://chromestatus.com/feature/5126108151808000

以前話題になっていた HTML の search タグですが Chrome 118 から対応するみたいです

機能としては特になくアクセシビリティのためのものらしいです
「<div role=search>」 と書くのと同じらしいです

検索ボックスが表示されたりはしないので アクセシビリティ系を気にしない人にとっては 無視していいタグです
main とか aside とか header とか nav とか こういう系統のタグです
コンストラクタを見ても main などと同じで HTMLElement になっています

document.createElement("search").constructor.name
// 'HTMLElement'
zstd という圧縮アルゴリズムを知った
ブラウザとサーバー間でデータを送るときに圧縮するアルゴリズム
有名なのは gzip です
brotli が出てきてからもファイルサイズを示すときによく minify+gzip のサイズが書かれているのを見ます

それ以外に新しいものは特に聞いたことがなかったのですが つい先日 zstd というのを知りました
正式名は Zstandard
Z なので zip 系の標準を目指してるということでしょうか
Meta 製のようです
相変わらず Meta って開発者向けなものを色々作ってますよね
Facebook とかの SNS サービスよりこっちがメインの企業じゃないのと思うほど

Wikipedia によれば zstd は
2016 年に 1.0.0 リリース
2018 年に RFC8478
2021 年に RFC8878

のようで できたばかりの新しいものというわけではないようです
全く知らなかったですけど

ブラウザで使えるなら聞いたことくらいありそうだけど と思って調べてみるとまだブラウザでサポートされていないようです
Chrome では 118 (10/11,12 リリース) で使えるようになるそうです
https://chromestatus.com/feature/6186023867908096

後から追加でサポートするものですし brotli より圧縮率や速度が優れてるのでしょうか?
比較してるサイトを見てみましたが 圧縮率やサイズによって変わってきて 常に zstd の方が良いというわけではなさそうです
unload イベントが将来的に削除予定らしい
https://chromestatus.com/feature/5579556305502208

Chrome 117 で Developer trial として unload イベントを削除するための取り組みが始まるようです
まずは unload イベントリスナを追加するための Permission-Policy を追加するみたいです
最初はデフォルトで許可されていて 徐々にデフォルトで拒否に移行していくそうです
そして最終的に unload イベントのサポートを削除するのが目的だそうです

理由は現状でちゃんと動いてなくて信頼性が低いかららしいです
ブラウザによっては動かないものもあり 環境次第となっていて実質標準とは言えない状態みたいですね

特に BFCache との相性が悪いです
BFCache が有効だとページ遷移でページが表示されなくなっても復元される可能性があるので unload されたとは言えないです
それでページ遷移のときに unload を呼び出さなければ そのままそのページに戻って来ないと永遠に unload されないです
なので Chrome は unload リスナがついていたら BFCache は無効になるような挙動だったはずです

Safari は以前からずっと unload イベントが起きないです
以前これで困ったことがあります
でも Chrome だけでも動いてほしいことがあったので 削除されるのはやっぱり困りそうです

代替候補はやっぱり pagehide でしょうか
beforeunload もありますが ユーザーによるキャンセルが可能なので キャンセルされるかもしれないです
pagehide はタブを閉じたり リロードしたり 進む戻るで移動をしたり 新規にナビゲーションしたりでページが見えなくなったときに起きるイベントです
BFCache などで復元されるされない関係なしです
タブの切り替えや ウィンドウの最小化で見えなくなる場合はイベントが起きないです
基本はこれで良さそうですが unload ではないので JavaScript の状態がそのままで復元される可能性があるというのは気をつけないといけないです
あとは pagehide の理由としてリロードとかタブを閉じるとか戻るとかが受け取れればいいのですけどね

const win = window.open(location.href)
win.addEventListener("load", () => console.log("event load"))
win.addEventListener("unload", () => console.log("event unload"))
win.addEventListener("pageshow", () => console.log("event pageshow"))
win.addEventListener("pagehide", () => console.log("event pagehide"))
event pagehide
event unload
event load
event pageshow
(開いたタブを閉じる)
event pagehide
event unload
Chrome 118 で @scope 機能が追加される予定
https://chromestatus.com/feature/5100672734199808
https://drafts.csswg.org/css-cascade-6/#scope-atrule

CSS に @scope という機能が増えるようです

@scope (.foo) to (.bar) {
a { color: red; }
}

と書けば DOM のツリー上で .foo の内側で .bar の外側を対象に a タグの色を変えるということになります
ShadowDOM みたいなスタイルのスコープ機能が追加されるということですね
WebComponents を使うなら ShadowDOM でいいですが React 等のフレームワークの場合はコンポーネントごとに ShadowDOM を使わないのでスコープを制御し辛いです
なのでこういうのがあると便利になりそうです

これに合わせて Scope Proximity という新しい概念が追加されてスタイルの優先度にも変更があるようです
詳細度が同じ場合 セレクタの対象要素とスコープのルート要素間の距離が近いほど優先度が高くなります
スコープが指定されないこれまでのスタイル定義だと距離が無限(優先度最小)として扱われるようです

例えばタブがあって その中にフォームがあって タブとフォームにスコープが設定されていて フォームパーツは両方のスコープに入る場合 まずフォームスコープのスタイルが優先されて次にタブスコープのスタイルで 最後にページ全体のということのようです
優先度的には良さそうです
ただ詳細度の方が優先度が上なので やっぱり詳細度には悩まされそうです

構文的には

@scope [(<scope-start>)]? [to (<scope-end>)]? {
<rule-list>
}

なので scope-start や scope-end は省略できるみたいです
scope-end を省略すると

@scope (.dark-scheme) {
a { color: plum; }
}

CSS のネスト機能が使えるようになる前なら ネスト機能としても使えるので需要が高かったのかもしれません
でも今では先にネスト機能が使えるようになっているので あまりこのケースは使わなそうです

ネスト機能と同じようなことができますが 全く同じではなく少しだけ違いがあります
@scope に指定するセレクタ (上の場合の .dark-scheme) の部分は詳細度に含まれません
ネストの場合に詳細度に影響しないよう :where() を使うような挙動です
その場合でもスコープによる優先度があるので :where() を使うより @scope の方が優先度が高くなります

scope-start を省略する場合は その style タグの親要素がスコープのルートになります

<div>
<style>
@scope {
p { color: red; }
}
</style>
<p>ここは赤色</p>
</div>
<p>ここは赤色じゃない</p>

昔 style タグに scoped 属性がありましたが それに近いことができますね
popover 属性使ってみた
来週 Stable リリース予定の Chrome 114 で Popover API がリリースされる予定

div など好きな要素に popover 属性をつけれる

<div popover>
あいうえお
</div>

popover 属性をつけた要素は表示されない
UA のスタイルで display: none されてる

[popover]:not(:popover-open):not(dialog[open]) {
display: none;
}

popover 要素は showPopover メソッドで表示できる

document.querySelector("div").showPopover()

dialog と同じ感じで中央に表示される
表示は最上位になるので position: fixed で z-index 大きめの要素があってもさらにその上に来る

popover の要素の外側の適当なところをクリックすると自動で閉じてくれる
プログラムから閉じるときは hidePopover()

属性で popover="manual" を指定すると自動で閉じなくなる
デフォルトの value 省略形は popover="auto" を指定するのと一緒

popover 属性がついてない要素に showPopover を実行するとエラーになる

Failed to execute 'showPopover' on 'HTMLElement': Not supported on elements that do not have a valid value for the 'popover' attribute.

表示中に showPopover を呼び出してもエラーになる

Failed to execute 'showPopover' on 'HTMLElement': Invalid on popover elements which aren't hidden.

この辺はもっとゆるくていいと思うのに
togglePopover() メソッドもあって 引数の true/false で表示・非表示を指定できる
こっちだと表示中に表示しようとしてもエラーは出ないのでこっちのほうが扱いやすいかも

複数の popover 要素があって 表示中の要素があるときに 別の要素を表示させようとした場合は showPopover でエラーにならない
すでに表示していた popover 要素は自動で閉じられる
popover="manual" の場合は閉じられないので 複数の popover 要素が表示できる

popovertarget と popovertargetaction 属性もあって これを使うと JavaScript なしで HTML だけでも表示・非表示を制御できる
属性名が長い

<button popovertarget="foo" popovertargetaction="show">
ここをクリックすると POPOVER を表示
</button>

<div popover id="foo">
<h1>POPOVER</h1>
<button popovertarget="foo" popovertargetaction="hide">
ここか外側をクリックすると POPOVER を隠す
</button>
</div>

マニュアルの場合

<button popovertarget="foo" popovertargetaction="show">
SHOW
</button>
<button popovertarget="foo" popovertargetaction="hide">
HIDE
</button>
<button popovertarget="foo" popovertargetaction="toggle">
TOGGLE
</button>

<div popover="manual" id="foo">
<h1>POPOVER</h1>
</div>

popover のネストもできる
popover で表示される要素の中に popover 要素を配置する

<button popovertarget="foo" popovertargetaction="show">SHOW</button>

<div popover id="foo">
<h1>POPOVER1</h1>

<button popovertarget="bar" popovertargetaction="show">SHOW</button>
<div popover id="bar">
<h1>POPOVER2</h1>
</div>
</div>

POPOVER2 を表示すると POPOVER1 は後ろで表示されたまま
POPOVER2 の外側かつ POPOVER1 の内側のエリアをクリックすると POPOVER2 だけを閉じれる
POPOVER1 の外側をクリックすると両方とも閉じられる

現状の機能だと位置は中央にしか来ないみたい
表示するボタンのすぐ下に持ってきたりする機能はなさそう
中央に来てるのはデフォルトの UA スタイルで margin: auto だからなので margin 等を JavaScript で調整して期待の位置に持っていくことになりそう

中身まで見てないけど Chrome 116 の機能で CSS Anchor Positioning というのがあるので これでアンカーを指定できるようになるのかも?
display: none をアニメーションできるようになるらしい
https://chromestatus.com/feature/5154958272364544

これまでだと display: none があるとアニメーションできず すぐ消えてしまっていました
それが Chrome 115 でアニメーションできるようになるみたいです
徐々に薄くなったり小さくなったりするわけではなく 完全に none になるまでの間は表示されてる扱いで その他のアニメーションが有効になるというものです
薄くしたり 小さくしたり は自分で別の CSS プロパティをアニメーションさせて実装する必要があります

これまでだと そういうアニメーションを実行させてから 終わったのを JavaScript で検知して display: none にする必要がありました
今回の変更で アニメーション後に自動で display: none になるので少し楽になるという感じです

個人的には display: none ではなくて remove してしまいたいことのほうが多いので意外と使い所がなかったです
画面端に出てくる通知みたいなものだと 任意個数になるので事前に要素を作っておかずに その場で要素を作って使い終わると削除しますからね
ずっと実体が残ったままだとポップアップやダイアログでしょうか


少し使った感じだと

@keyframes hide {
0% { height: 100px; display: block }
100% { height: 0; display: none }
}

div {
height: 100px;
background: red;
}

.hidden {
animation: hide 2s linear 0s;
display: none;
height: 0;
}

のように .hidden に非表示状態のスタイルも書いておくとアニメーションしてくれませんでした
非表示にするときに hidden クラスをつけます

他のスタイルだと アニメーション中はアニメーションのスタイルが優先されて アニメーションが終わると直接指定されたスタイルに戻るというものです

@keyframes color {
0% { color: red; }
100% { color: pink; }
}

.color {
animation: color 2s linear 0s;
color: blue;
}

だと color クラスをつけると 赤からピンクに文字色が変わり アニメーションが終わると青色になります
そう考えると .hidden に display: none があっても問題ないように思うのですが .hidden の中に display: none があるとアニメーションされないです

display: none をキープするには アニメーション後にスタイルを保持する設定が必要です

.hidden {
animation: hide 2s linear 0s forwards;
}

のように forwards を指定するとアニメーションの最後のフレームのスタイルのままになります
でも直接書かれたスタイルじゃないので分かりづらく好きじゃない方法です
100% と同じ内容をスタイルに直接指定して動くようなって欲しいですね
CSS の zoom プロパティがなくなるらしい
https://chromestatus.com/feature/6535859207143424

非標準で Firefox だとサポートされてないものみたいです
event.path のときと同じ感じみたいですね

実際ズームしたいときは transform の scale 関数や scale プロパティで指定していたので zoom は特に使った覚えもないです

.zoom {
transform: scale(1.2);
/* or */
scale: 1.2;
}

代替があるなら無くても良さそうと思いましたが 全く同じ挙動ではないみたいで zoom を正式な機能にしたいという要望もあるようでした
違いは拡大のタイミングで レイアウトの前にズームするか後にズームするかが異なってるようです

scale だとレイアウト計算後の拡大・縮小なので 倍率が変わっても実体の大きさは変わらないとして扱われて 周りのレイアウトを壊しません
なので アニメーションに向いています
ですが レイアウトが決まった後に拡大するので 画面幅 100% いっぱいのページに対して全体を拡大するとはみ出ます
スクロールバーが出てきます
そういうことがあるので レイアウトを計算する段階で拡大を有効にしたい場合には使えません

これに対して zoom だと拡大・縮小されたものとしてレイアウトが計算されるのでスクロールバーは出ません
固定サイズのものを折り返しなしで並べるなど スタイルによっては出るケースもありますが scale と違って確実に出るわけではないです
Ctrl-+ などブラウザの機能としてのズームと同じ扱いです

こういう違いがあると zoom がなくなると困る人もいそうです
Safari (Webkit) もこの機能を実装していて Chrome に合わせて削除するかの議論がこの issue です
https://github.com/WebKit/standards-positions/issues/170

壊れるページが出てくると言ってますし 本当に削除できるのか疑問もあります
Chrome も壊れるサイトが多いとやっぱりやめるとかあるのかもしれません
現状の予定では Chrome 114 (次のバージョン) で Developer trial として削除されて 117 で正式に削除の予定のようです

また zoom には現在の倍率を知るという使い方もあったようで これができなくなるそうです

getComputedStyle(document.documentElement).zoom
// '1'

のように html 要素の計算済みスタイルの zoom を参照します
Ctrl-+ などブラウザの機能でズーム倍率を変更して 150% にすると '1.5' になります

Windows の機能で拡大縮小した場合も反映されます
150% 設定なら '1.5' になります
Windows の設定で 150% で さらにブラウザで 150% にズームすると掛け算されて '2.25' になります

倍率がわかって便利です

しかし html 要素に zoom スタイルがあたってると この機能がなくなります
スタイルとして指定された値が固定で返ってきます

html { zoom: 2 }

というスタイルがあると Windows の設定やブラウザのズーム倍率は無視され ずっと '2' になります
また Windows の設定で 150% なら デフォルトが '1.5' のはずなのに

html { zoom: 1.5 }

というスタイルを適用すると そのままの倍率ではなく さらに 150% の拡大が行われます
非標準というだけあってわかりづらく一貫性のない挙動です

設定せず参照のみであれば使えそうな気はしますが 現在は window.devicePixelRatio で同じ値が取れるのでこれはなくてもよさそうです



その後
Chrome 113
Chrome 113 の stable リリース日が今日なので そろそろリリースされそうですね

112 の CSS Nesting に比べるとあまり気になる追加機能のないバージョンですが WebGPU は一部開発者の人には嬉しい機能なのかもです
個人的には Canvas の WebGL ですら使わないので 使うことはなさそうですけど
ゲームとか AI 処理で活用されるのでしょうか
アクセスにユーザー許可がいらないなら以前流行ったマイニングに利用されてまた流行ったり?

他には CSS 機能がいくつか増えてるようです
メディア機能 (@media) に update と overflow-block と overflow-inline が追加されます
update では 更新される画面 (PC) か更新されない画面 (印刷) かでスタイルを変えられるようです
電池残量が少なく画面の更新頻度が低い場合や 電子書籍リーダーみたいな画面更新が遅い環境向けの更新できるけど遅いというパターンも指定できるようです
overflow では はみ出たときに見えないか スクロールできるか ページに分かれるかなので これも画面か印刷かで分かれるようです

また animation の easing 関数で linear 関数が追加されてます
linear とか ease-out みたいに指定していたところで linear(0, 0.25, 1) みたいなものをかけるようになります

JavaScript では fetch の Header に getSetCookie メソッドが追加されます
fetch では Cookie を扱えません

fetch(url, {
headers: {
cookie: "foo=1; bar=2",
},
})

と書いても cookie は送信できませんし

const res = await fetch(url)
console.log([...res.headers.entries()])

と書いても 取得できるヘッダー情報に set-cookie が含まれずサーバーから保存するよう指定された Cookie の情報が見れません

セキュリティ上 仕方ないのかなと思いますが 今回の更新では set-cookie ヘッダーの情報が取れるようになるようです

const res = await fetch(url)
console.log(res.headers.getSetCookie())

getset って続く分かりづらい名前です

set-cookie の情報を取れたところでセットはできないですし あまり使いどころが思いつきません
fetch のオプションでは渡せませんが 現ドメインへのリクエスト限定なら document.cookie を書き換えれば送ることはできます
ただ set-cookie が返ってきてたら自動でセットされてるはずですし 意味がないです
そのまま使わず書き換えるにしても HttpOnly があると自動でセットされたデータを消せません
credentials を omit にすれば set-cookie で Cookie が保存されないので JavaScript でセットできるかもです
ですがそもそも現ドメインに限るなら サーバーを自身で管理してるはずなのでサーバー側で対応すればよく JavaScript で set-cookie を取得したいとなることもなさそうです

最近はサーバーサイドでも fetch が使われるので Node.js や Deno 向け仕様かなと思いましたが それなら送信時のセットもできないと不便です
試してみると Node.js 18 の LTS では Cookie が特別扱いされておらず set-cookie は headers のデータに含まれていました
やっぱりなんのために追加されたのかよくわからない機能です



気になったのでもう少し調べてみると Node.js などサーバー向け機能であってたようです
https://developer.mozilla.org/en-US/docs/Web/API/Headers/getSetCookie

set-cookie で複数の Cookie をセットする場合 HTTP header に set-cookie が複数回現れます
しかし ヘッダーのデータを JavaScript で取得するときには 1 つの文字列しか取得できません
Headers には URLSearchParams の getAll みたいなメソッドは無いようです
なので複数の set-cookie が 1 つにまとめられるのですが 「,」 区切りになります
期限の日付表記など 1 つの Cookie の情報のなかにも 「,」 は現れることがあるのでパースが大変です
「,」 で split するような簡単な方法で済ませられません

そこで getSetCookie があると便利で配列で取ってこれます
最初から MDN 見てればよかったです

console.log(process.version)
// v20.0.0

const res = await fetch("https://github.com/")

console.log(res.headers.get("set-cookie"))
// _gh_sess=7a略%3D; Path=/; HttpOnly; Secure; SameSite=Lax, _octo=GH1.1.143815131.1683069288; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; Secure; SameSite=Lax, logged_in=no; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; HttpOnly; Secure; SameSite=Lax

console.log(res.headers.getSetCookie())
// [
// '_gh_sess=7a略%3D; Path=/; HttpOnly; Secure; SameSite=Lax',
// '_octo=GH1.1.143815131.1683069288; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; Secure; SameSite=Lax',
// 'logged_in=no; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; HttpOnly; Secure; SameSite=Lax'
// ]
Chrome のリリースチャンネルに Early Stable が増えてた
Chrome Platform Status によれば Chrome 112 の Stable リリースの予定日は 2023/3/29 です
https://chromestatus.com/roadmap

ですが 3/29 時点では更新をチェックしても更新なしでした
時差を考慮して 1 日待ってみても更新なしです
各言語版は更に遅れるのかもとさらに数日待ってみても更新がないです
新規インストールを試してみても 111 です

遅延でもしたのかなと調べてみたら 110 からリリーススケジュールに変更があったようです
Stable リリースの前に一部のユーザーだけに公開される Early Stable というものが追加されたみたいです
https://developer.chrome.com/blog/early-stable/
すでに Beta/Dev/Canary といろいろあるのにまだ増えるのか

Chrome Platform Status の Stable リリース日はこの Early Stable の日付になってるようです
Chromium Dash の方のスケジュールだと Early Stable と Stable の日付が別々に記載されていました
https://chromiumdash.appspot.com/schedule

一般の Stable リリースは 4/4 で ほぼ一週間ずれるようです
Beta 版があるといっても 積極的にこれを使って既存ページへの影響をテストしてる人は少ないでしょうし いつも Stable 版が出てから色々問題に気づいてるので 一部ユーザーにだけ公開して問題があったら本リリースを遅らせるというのはわからなくないです
ただ新しくバージョンを増やさず Beta 版でそれやればいいのではとも思います

Chrome 112 では待望の CSS Nesting が使えるようになります
CSS のビルドはほぼこれのためだけにやっているようなものだったので これで CSS はビルド不要になりますね
ビルド不要だと ESM から直接ロードしてモジュール単位で使えるのもいいところです
ただ残念ながら Safari はまだのようです
1 月に TP で CSS Nesting がリリースされていて ちょっと前に 16.4 のリリースがあったので Chrome に合わせて CSS Nesting が含まれてるのだろうなと思ってたのに含まれてなかったです
Safari 対応するならもうしばらくは ネストなしかビルドする必要があります
Chrome 112 で FormData のコンストラクタに引数が追加される
2 つめの引数に submitter が追加されるみたい

https://chromestatus.com/feature/5066604734316544
https://xhr.spec.whatwg.org/#interface-formdata

これまでの FormData ではサブミットボタンは対象外でした

<form id="form1">
<input name="text" value="a">
<button id="submit" type="submit" name="submit" value="1">submit</button>
</form>

という HTML があるときに このフォームを FormData 化して中身を見ると

const fd = new FormData(form1)
console.log(Array.from(fd.entries()))
// [
// ['text', 'a']
// ]

となっていて submit は含まれません

2 つめの引数の submitter に submit ボタンを渡すと

const fd = new FormData(form1, submit)
console.log(Array.from(fd.entries()))
// [
// ['text', 'a']
// ['submit', '1']
// ]

という感じで含まれるようになります

true/false じゃなくて HTML 要素を渡すのはフォームの挙動に合わせるためです
フォームにはサブミットボタンを複数配置できます
その中で押されたボタンの情報だけが送信されます
このフォームの動きに合わせて どのサブミットボタンを押されたとするかを指定するためです

<form id="form1">
<input name="text" value="a">
<button id="submit1" type="submit" name="submit" value="1">1</button>
<button id="submit2" type="submit" name="submit" value="2">2</button>
<button id="submit3" type="submit" name="submit" value="3">3</button>
</form>
const fd = new FormData(form1, submit2)
console.log(Array.from(fd.entries()))
// [
// ['text', 'a']
// ['submit', '2']
// ]

個人的には フォームから FormData をつくることはほぼなくて JavaScript の変数上に持っているデータから作ることばかりな上に フォームに複数のサブミットボタンを配置することもほぼないので使う機会は特になさそうです
従来のフォームで作られた画面でサブミットは fetch を使ってバックグラウンドでやるように変更したい みたいな場合には便利になるのかもですね
Chrome のヘッドレスモードが新しくなるみたい
https://developer.chrome.com/articles/new-headless/

これまでの Chrome のヘッドレスモードって Chrome の 1 機能だったので 普通に Chrome を使うのと同じ仕組みで動いていて ただ画面が無いだけだと思っていたのですが違ったようです
ヘッドレスとヘッドフルで別実装のブラウザになっていたそうです
そのせいで片方でのみ起きるバグがあったりメンテコストがあったりと問題があり Chrome 112 では統合されるようになったそうです

どっちを使うかはオプションで切り替えれて 今年後半に古い方は削除される予定だとか

chrome --headless=new
chrome --headless=old

実装が違っていたわけですし 新しい方にすると結果が変わってしまう可能性もあると思いますが そこはスタンドアロンバイナリとして古い方を使えるようにしてくれるそうです

個人的にはそのスタンドアロンバイナリのほうが使い道あったりと思ってたりします
ヘッドレス Chrome って Linux の CUI 環境とか画面が無いところで使ってることが多くて Chrome 全体はいらないのに全体を入れるしかなくて重たくなります
これがデメリットだったのですが ヘッドレスだけの実装のスタンドアロンバイナリとなれば軽量そうです
ですがもう開発されないバージョンなわけですし 今後の追加機能は実装されないでしょう
そうなると実際に使う選択肢には入れられなそうで残念です
Event の path プロパティがなくなるらしい
Chrome 109 の変更で Event の path プロパティがなくなるようです
https://chromestatus.com/feature/5726124632965120

イベントが起きた場所までのパスを取得できるプロパティです

window.addEventListener("click", event => console.log(event.path))
(11) [
0: header.side-header
1: section.side-item
2: div.side-column-inner
3: aside.side-column
4: div.columns.main-right
5: div.container
6: section.page-body
7: body
8: html
9: document
10: Window {…}
length: 11
]

割と便利なので以前に何度か使っていた覚えがあります
マイナーどころや追加されたばかりのものと違い これは使っているページが多そうですが消えてしまって互換性的に大丈夫なんでしょうか
配列の group メソッドが追加されただけで一部のサイトで互換性が壊れたといって Canary で一旦リリースされていた機能をとりやめにしたくらいですがもっと多くの壊れた報告が来そうです
ただしこれは Firefox にはない機能なので Firefox 対応もしているサイトなら使ってないはずです
といっても最近は Chrome 系しかサポートしないページも増えていますし Chrome でしか使えない機能だからといって実際に使われてないとは言えなそうです

削除されたあとも event.path 相当のものが必要なら composedPath() メソッドで取得できます
Shadow DOM の中や slot を使った場合の挙動も完全に同じみたいです

<script type="module">
const handler = (event) => {
console.log({
elem: event.currentTarget,
path: event.path,
composedPath: event.composedPath(),
})
}

window.addEventListener("click", handler)

customElements.define("foo-bar", class extends HTMLElement {
constructor() {
super()
const root = this.attachShadow({ mode: "open" })
root.innerHTML = `
<div class="c">
<div class="d">
<slot></slot>
</div>
</div>
`
root.querySelector(".d").addEventListener("click", handler)
}
})
</script>

<div class="a">
<div class="b">
<foo-bar>
<div class="e">
<div class="f">
<button>button</button>
</div>
</div>
</foo-bar>
</div>
</div>

こういうページで button をクリックしたときの path と composedPath() を見ると同じ結果でした

Array(14) [
0: button
1: div.f
2: div.e
3: slot
4: div.d
5: div.c
6: document-fragment
7: foo-bar
8: div.b
9: div.a
10: body
11: html
12: document
13: Window {…}
length: 14
]

closed の Shadow DOM は中身が見えないので Shadow DOM の外側のリスナでは foo-bar の内側は div.e になり document-fragment などは省かれます
Shadow DOM の中の要素に付けたリスナでは div.c なども含まれます
この辺も含めて composedPath と得られる配列は一緒でした
配列の group メソッドが延期になりそう?
配列のグループ化メソッドが使えるようになるのはもうすぐだったかなーと思って Chrome status のページを開いてみると リストに出てきませんでした
全体から探すと項目が見つかりましたがリリースバージョンの記載がありません

なにかあったのかなと探すとこういうのがありました
https://bugs.chromium.org/p/v8/issues/detail?id=12499

ウェブの互換性に問題が出たので一旦 unship するようで TC39 の決定待ちのようです

リンクの issue を見た感じだと 配列の group プロパティにアクセスしていてそれが関数を返すことでエラーが起きてるようです
これまでは undefined でしたが group メソッドが実装されれば関数が返ってくるようになりますからね
ただこれはページ側の実装が悪いとしか言えないと思います
今存在しないメソッドは将来的に作られる可能性がありますし それが group みたいな一般的でありそうな名前ならなおさらです
それを考慮せず手抜きで undefined になるだろうしで作っていればそうなるでしょうとしか思えません
hasOwnProperty でチェックするとかしてればメソッドの追加による問題は防げたはずですし

そんなサイトのために便利機能の実装が遅れるとか迷惑でしかないんですけど
古いサイトを見たいなら Edge の IE モードを使えばいいとして互換性重視はそろそろやめてもいいと思うんです
Chrome 106 で自己署名証明書の https 接続がエラーになる
また Chrome が改悪しました
自己署名の証明書を使ってローカルネットワーク内のサーバにアクセスしていたのですが 急にエラーがでるようになりました

証明書期限やサーバとクライアントの時間を確認しましたが問題なしです
バージョンは 106 からですが アップデート直後には問題なかったと思うので マイナーアップデートのどこからかだと思います
107 が来ていたのでアップデートしましたが改善しません
エラー内容はいつもの無効な証明書エラーと少し違っていて 無視して続行するボタンが出ません
同バージョンの Chrome で証明書とドメインが一致しないようにして適当なサイトに繋いでみるとこれまでどおりのエラー画面なので 原因がなにか違うようです

Edge で試してみると 107 でもエラーはなく正常として扱われます

Chrome はこういう仕様変更をたまにするのが嫌なんですよね
変に古いページの互換性にこだわって JavaScript などの仕様は変えないのにこういうところでは普通に破壊的変更をしてきます
Edge では起きてないですし もう全部 Edge に移そうかとも思うほど
ただ Edge は Edge で余計なことをするところもあるので 迷う部分もあります

設定でホワイトリスト登録したドメインは証明書エラーを無視とかできるといいのですけどね

一応ググった感じは同様の報告はありました
https://support.google.com/chrome/thread/184774585/with-the-latest-106-0-5249-119-chrome-update-our-internal-ssl-site-gets-a-privacy-error
HTML に popup 属性が追加されるみたい
Chrome 110 予定
https://chromestatus.com/feature/5463833265045504

select タグのメニューとかで一番上のレイヤーに出てくる UI
標準の機能として簡単に使えるようになるみたい
最初の提案は popup タグだったらしいけど 今は属性になったらしい
最上位に表示してブラウザのウィンドウから出れるとかキーボード制御はわかるけど 属性だけだと表示位置の調整はどうするのでしょう
id で基準の要素を指定したり?

この辺が関連する whatwg の issue ぽいので探せば色々書いてそうだけど長いのでパス
詳しくは dev や canary で使えるようになった頃に調べるつもり

https://github.com/whatwg/html/issues/7785
https://github.com/whatwg/html/pull/8221

(その後)

Chrome 110 が出たけど有効になってなくて 現在の予定だと Chrome 114 かららしい
back/forward cache が効かない理由を調べる
以前 bfcache が Chrome に実装されたときにいくつかのページを試すと bfcache が使われてるページとそうでないページがあって なぜ使われたり使われなかったりするのか はっきりとした原因はわかりませんでした

その後いつのころからか devtools の Application タブに 「Back/forward cache」 という項目が増えていました
「Test back/forward cache」 というボタンがあるので確認したいページを開いて押します
ページがリロードされて bfcache が有効化どうかを表示してくれます
無効の場合は原因も教えてくれます
ときどき原因が表示されないこともありますけど

このブログだと成功になって bfcache が有効みたいですが メインの方のブログでは成功が出たかと思ったらすぐ失敗に切り替わってます

失敗になる原因のひとつは unload ハンドラが設定されてることで 結構見かけます
他にも拡張機能で JavaScript や CSS を挿入してる場合も無効になるようです
ページのカスタムや広告ブロック系の拡張機能とは相性が悪いですね

あと HTTP のレスポンスのヘッダーに 「Cache-Control: no-store」 が付いてる場合も bfcache が無効になるようです
自分で作ったページで unload などを使ってないのに bfcache が動いてなくて調べるとこれでした
Cache-Control なんてつけた覚えはなかったのですが PHP のレスポンスではデフォルトでついてるようでした
つく場所とつかない場所があって少し困りましたが session を使うと追加されるようです

戻るボタンで戻ったときでもサーバにアクセスしてログイン状態を確認したいのならいいのかもですが 戻るだけならログインチェックなどはしなくていいと思うんです
これまで見えていた画面ですし 新規に検索したり画面を移動するとそこでログインチェックが入りますし

PHP では

session_cache_limiter('');

で自動で Cache-Control ヘッダーを出力するのを防げるようです
Chrome 105 で import.meta.resolve() が使えるようになる
https://chromestatus.com/feature/5086507990777856

import.meta に resolve が増えるみたいです
resolve は関数で 引数に渡したモジュールをロードする URL を返してくれるようです

つまりこういうことを自分でやらなくてよくなったということですね

const module_path = "foo/bar.js"
const module_url = new URL(module_path, import.meta.url)

自分の URL からの相対パスの解決以外にも import map を使ったモジュールの URL を知るためにも使えるのかも
console の入力で window の error イベントが発生してた問題は修正されたみたい
少し前から一部のページでコンソールに入力するとエラーが多数表示されて邪魔だった問題
原因は window のエラーイベントが入力中に発生するから

window.onerror = console.error

これを実行してから適当にコンソールに文字を入力すると どのページでも入力のたびにエラーログが出力されるようになる
不便だし迷惑だけどこれは仕様変更で仕方ないものかなと思ってたけど エラーを収集するサイトではかなりのリクエストが送信されてる
実際 Edge の新しいタブではエラー時にリクエストを送信するので ネットワークタブでログを見たらすごいことになってた
新しいタブはちょっと動作確認したいってときによくコンソールを使う場所だからね

これを仕様とするのは問題多そうと思って探したら すでに修正されてたみたい
https://chromium-review.googlesource.com/c/v8/v8/+/3660253

ただリリースされるのはまだ先だろうからしばらくはエラーが出る状態が続きそう
Chrome 104 で CSS の transform プロパティを個別に指定できるようになる
https://chromestatus.com/feature/5705698193178624

CSS の transform は translate や rotate などが混ざっていて少し特殊です

div {
transform: translate(200px, -100px) rotate(90deg) scale(1.2);
}

一つのプロパティが長くなって書きづらいですし JavaScript から動的に変更したいときや animation の設定時にとても面倒です
CSS のプロパティとして個別に設定したいと思ってましたがそれができるようになるようです

div {
translate: 200px -100px;
rotate: 90deg;
scale: 120%;
}

とても助かりますね
ちなみに Safari では結構前からサポートされてるようです
https://webkit.org/blog/11420/css-individual-transform-properties/
CSS 系に関しては Safari の方が Chrome 系よりも進んでますね
Chrome 104 で setTimeout の動きが少し変わるみたい
https://chromestatus.com/feature/4889002157015040

setTimeout にはネストレベルが 5 より大きいと最低でも 4ms になるみたいな仕様がありますが それとは別に Chrome では 0ms が指定されたときに 1ms として扱うようにしていたようです
0 を指定するとできる限り最速で実行されることを期待しますが 最低でも 1ms 待たされていたようです
仕様で決まってるものではなくて Safari はこの制限を受けないので Chrome も合わせるようです

これによってパフォーマンスが向上するそうですが 1ms ではそんなに変わらない気がします
性能が高い PC でもなければ自然と 1ms 程度かかっていそうですし

これまで 1ms 待たされていたことで処理順がうまく動いてたみたいな場合は 1ms 未満で多くの処理ができる性能が高い PC でのみ 正常に動かなくなるなんてこともあるかもしれませんね
タイムアウトに指定するミリ秒を 0 の代わりに 1 にすればこれまでどおりの動きになるようです
最近 Chrome の証明書無視期間が短くなった?
https 通信で証明書が自己署名だったりドメインが違ってたり期限切れだったりのときに出てくるブラウザのエラー画面
無視してアクセスできるから 自分管理のサーバなら基本無視
頻繁に使うページでは毎回エラー画面で無視するボタンを押すのも面倒だから 自己署名の証明書をインポートしておくけど 全部のサーバでするのも面倒だから 時々しか使わないのは毎回エラーを無視する方針

以前は一度無視したら数時間~ 1 日程度はアクセスできてたはず
とりあえず最初に無視したらしばらく使う分には困らないくらいの期間
それが最近使ったら数分レベルの短時間でまたエラー画面が出るようになってた
間隔が毎回同じじゃないような気もしたので 時間以外の要因もあるのかもしれないけど
なんにしてもまともに使えない
一度無視したらブラウザ閉じるまでは無視したままでいいのに

信頼してるサーバにしかアクセスしないし 仮に Google などが偽物になっていても困らないようなことしかしない PC だしと証明書全無視でいいので --ignore-certificate-errors オプションを付けて解決した
タブやウィンドウだけに指定はできないので Chrome 全体を終了してからこのコマンドラインオプション付きで起動すると無視できる
エラー画面が出ないだけで URL の左側のアイコン表示ではエラーになってるからデフォでもいいようなと思ったけど バックグラウンド通信ではわからないし 偽ページに置き換わってたとしてそこの JavaScript が動いたり Cookie の送信の問題もあるから 流石にデフォはだめそう

Edge が標準で入ってるし 外部は Edge で内部は Chrome でと使い分けて Chrome は証明書エラー無視にすると楽かも
接続先が localhost なら証明書エラーを無視する機能はあるらしいけど 基本ローカルのネットワーク内の別ホストだから使えない
Chrome のロゴが新しくなるらしい
https://www.itmedia.co.jp/news/articles/2202/05/news046.html

どこが違うの?間違い探し?って思ったくらいだけど 真ん中の◯が大きくなってシャドウが消えたみたい
デスクトップやタスクバーのアイコンだと小さすぎて気づかないけど 拡大して見たときはシャドウが気になってからこれは良い変更だと思う

そもそも最近はデフォルトの Edge で十分で 新規の PC には Chrome を入れてないことも増えてきたけど
devtools の文字列がシングルクオートになってる
いつのまにか devtools で文字列を評価したときの結果表示がシングルクオートになってます

"a"+"b"

を入力すると表示されるのは

'ab'

昔はダブルクオートだっと思うのですけどね
プログラミング言語的にはダブルクオートのほうが一般的ですし JSON はダブルクオートのみなのでダブルクオートをデフォルトにしておいてほしいものです

なぜそんな変更をしたのかと思って調べたら文字列表現が JavaScript として有効な表示になるように変更があったようです
文字列の内部で使われていたら別のクオートに置き換えて全て使われていたらエスケープするようです

"'"
// "'"

"'\""
// `'"`

"'\"`"
// '\'"`'

コピペできるので便利といえば便利ですが エスケープされず直接表示してくれる昔のもののほうが読みやすくて好きでした
console.log を通せば今でも文字列にクオートがされずエスケープが行われないので エスケープなしでそのまま表示してほしい場合は console.log を通す必要があります
Chrome 92
Chrome 92 がリリースされた

新しいタブにファビコンがついた
Chrome アイコンの影みたいなの


開発者向け機能だと JavaScript に .at() や crypto.randomUUID() が増えた

"foo".at(-1)
// "o"

[1,2].at(-1)
// 2

crypto.randomUUID()
// "90b7eb58-6640-4ad4-a546-532783d17513"
Chrome もデフォルト noopener か
https://forest.watch.impress.co.jp/docs/serial/yajiuma/1291549.html

Firefox に続いて Chrome もか
別オリジンならまぁいいけど同一オリジンなら noopener いらないと思うけどなぁ

むしろ opener つけるってどうするの?
デフォルトで有効で 無効化するものって扱いだから有効化するオプションを見たことない
ないと window.open の返り値と window.opener を使ったウィンドウ間でのやりとりができなくなる
Chrome の更新ができない
以前から時々起きる Chrome が更新できない現象
これがまた起きていて しばらく 85 のままで やっと 86 に上げられました

Chrome で右上のメニューボタンのところに出てくる更新通知やバージョンを表示するページからの再起動では更新できずに 同じバージョンで起動します
クラッシュした結果更新できてないみたいで 再起動後にはクラッシュレポートみたいなプロセスが起動して しばらく CPU 使用率が高くなってファンがうるさいです

一向に直らないのでイベントビューアのログを見ると

ソース "gupdate" からのイベント ID 0 の説明が見つかりません。このイベントを発生させるコンポーネントがローカル コンピューターにインストールされていないか、インストールが壊れています。ローカル コンピューターにコンポーネントをインストールするか、コンポーネントを修復してください。

なんか壊れているんだとか
これが何度もでていました

前回は PC ごと再起動したときにアップデートできてた気がするので仕方なくアップデートすると 86 になってました
Chrome のアップデートに再起動が必要なのはさすがに面倒すぎです
Chrome をアクティブにすると CPU 使用率が上がる
少し重めの処理を実行中にタスクマネージャを見ると
CPU の使用率が 40% 前後 速度が 1.89 GHz 固定

この状態で Chrome のウィンドウをクリックしてアクティブにすると
CPU の使用率が 60% ちょっと 速度が 3.22 GHz 前後
まで上がる

少し重めの処理は Chrome とは関係ないプログラムだし Chrome では特に重い処理はしてない
実際にタスクマネージャでの使用率はほぼ 0%
一番使用率が高いのは少し重めの処理のプロセス
なんで関係ない Chrome のウィンドウをアクティブにするかどうかで変わるんだろう?
Chrome 以外に少し重めの処理をしてるプログラムのウィンドウやエクスプローラやタスクマネージャなどをアクティブにしても変化はなかった
今のところは Chrome だけ

Chrome 以外をアクティブにすると CPU 使用率は下がって また Chrome をアクティブにすると上がる
Chrome にタブグループが追加されるみたい
https://japanese.engadget.com/chrometabgroups-134519001.html

Vivaldi で使ってみたことあるけど 個人的にはいらないかな
一時期ほしいと思ってたことはあったけど 対して使いやすくないし ウィンドウ分けたほうが見やすい
ウィンドウならサブディスプレイに持っていったり 別のデスクトップに持っていったりもできるし

それよりもタブエリアを横に持ってきたい
Chrome でたまにあるアップデートできない現象
何が悪いんだろう?

chrome://settings/help

でアップデートありになってアップデート完了して再起動ボタン押したのに同じバージョンで起動される
一度起きると何回繰り返しても一緒
手動再起動で Chrome を終了して Chrome のプロセスがないのを確認してから再起動してもだめ
タスクスケジューラから GoogleUpdate を手動実行してもだめ

とりあえず new_chrome.exe 使えば新しいバージョンを使えるけど 普通に起動していようにアップデート完了させたい

C:\Program Files (x86)\Google\Chrome\Application\new_chrome.exe
Chrome で表示されるインスタンスの型名
Chrome だと

const x = class {}
new x()
// x {}

のように型名が変数名から推測されて表示されます
プロパティ名を使えば変数名に使えない文字列も使えます

const tmp = {}
tmp["a123@*+<>%$#ABC"] = class {}
const c = tmp["a123@*+<>%$#ABC"]
new c()
// tmp.a123@*+<>%$#ABC {}

オブジェクトの名前の tmp も入ってしまいますが結構便利です
やりたいことは動的に名前をつけることで いざやってみると

const tmp = {}
const random = "c" + ~~(Math.random() * 100)
tmp[random] = class {}
const c = tmp[random]
new c()
// tmp.<computed> {}

静的に判断しているようで変数になっていればすべて <computed> になりました

この機能を活かせるところは少なそうです
そもそも Chrome のみで Firefox では全部 Object 表示なので そこまで積極的に使っていくべきとも言えないです
他のタブを閉じるが消えてたらしい
https://forest.watch.impress.co.jp/docs/serial/yajiuma/1216629.html

78 で消えて canary 80 で復活してるとか
見覚えはあったけど使ったこと無いし消えてること気づかなかった
要らないと思うんだけど すぐ復活するほど需要あったんだ
「右側を消す」っていうのがあるし、ほしいタブを最左に持ってきてから右側消せばいいような

ヘビーユーザーになると残すタブがいっぱいで後で見そうならとりあえずおいとく
全消しなんてありえないってのが多いと思う
ライトユーザーだと最低限のタブだけにして見終わったらどんどん消していきたいという気持ちなのかな?
私の場合は調べ物の用途ごとにウィンドウ分けてるから作業終わったらウィンドウ単位で閉じてる
Chrome78 のタブの詳細表示がなんか嫌
Chrome をアップデートしたらタブにマウスを乗せた瞬間にタイトルとURLがポップアップ表示されるようになってた
なんかこれじゃない感があって嫌 というか邪魔
Edge や Vivaldi みたいなサムネが出るやつはそんなに気にならないけどなんでだろ
乗せた瞬間じゃなくて少しマウスを乗せたままにしたら出る tooltip みたいなのだからかな
Chrome 76 で設定画面がちょっと変わった
chrome://settings/help (about:chrome で表示されるところ) がちょっと変わった
75 だと左側にメニューはなしで ヘッダーのハンバーガーメニューで表示された
76 はハンバーガーメニューはなしでデフォルトで左側にメニューがでてる