メインコンテンツへスキップ

All Posts

News bits

Safari 17.2

2023 年 12 月 11 日に Safari 17.2 がリリースされました。

https://developer.apple.com/documentation/safari-release-notes/safari-17_2-release-notes

https://www.webkit.org/blog/14787/webkit-features-in-safari-17-2/

Safari 17.2 では多くの機能追加と、Interop 2023 による多くの修正や機能改善が含まれいています。Interop は、Google、Microsoft、Mozilla、Apple などによって全てのブラウザで全く同じように Web の機能を動作させるためのプロジェクトです。

#Exclusive accordions

Chrome 120 に続いて、details 要素の name 属性がサポートされました。同じ name 属性で複数の details 要素を紐づけることで、どれか 1 つだけを開ける UI を実装できます。

jsx
<details name="cookies">
  <summary>Chocolate chip</summary>
  Yum yum chocolate chip.
</details>
<details name="cookies">
  <summary>Snickerdoodle</summary>
   Yum yum snickerdoodle.
</details>
<details name="cookies">
  <summary>Maicenitas</summary>
   Yum yum maicenitas.
</details>
<details name="cookies">
  <summary>Sugar cookies</summary>
   Yum yum sugar cookies.
</details>

https://stackblitz.com/edit/js-bjwzi1?file=index.html

#One time code

SMS などで受信したワンタイムコードをサジェストしてくれる autocomplete 属性の one-time-code がサポートされました。

html
<label for="onetimecode">Enter code:</label>
<input
  name="onetimecode"
  id="onetimecode"
  type="text"
  autocomplete="one-time-code"
/>

#緩和された CSS ネスト

Chrome 120 に続いて、&is() を使用しなくても CSS をネストした状態で記述できるようになりました。

css
article {
  h1 {
    font-size: 1.8rem;
  }
  p {
    font-size: 1.2rem;
  }
}

#Custom Highlight API

Custom Highlight API がサポートされました。Custom Highlight API を利用すると、ドキュメント上にある任意の文字列にスタイルを適用することが出来ます。ほとんどの環境で利用できるようになりました。

https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API

#Import attributes

Chrome に続いて、Import 属性がサポートされました。これは、モジュールの import 文に型情報を追加する方法を提供します。

jsx
import json from "./foobar.json" with { type: "json" };
import("foobar.json", { with: { type: "json" } });

#Login cookies

ウェブサイトを PWA などでウェブアプリとして追加した場合に、クッキーがコピーされるようになりました。これにより追加後に再度ログインをする必要がなくなりました。

Safari 16.4

Safari 16.4 リリース。

#Web API

多くの Web API がサポートされるようになりました。モダンブラウザの中で Safari のみがサポートしていなかったものが多く、ブラウザ互換性を見て失望することや、冗長な記述を強いられること、ポリフィルを含める必要がなくなるのは大変ありがたいです。数が多いので、使う機会があるかもしれないと思うものを適当にピックアップして軽く紹介します。

#Fullscreen API

特定の要素を全画面モードで表示したり、それを解除したりできる API です。ブラウザゲームや、賃貸情報サイトの間取り図などの拡大などで有用かもしれません。ただし、これは離脱用の UI を作らない限り、ESC キーなどで離脱する必要があるため、ユーザーのことをよく考え利用した方が良いでしょう。

js
document.addEventListener(
  "keydown",
  (e) => {
    if (e.key === "Enter") {
      toggleFullScreen();
    }
  },
  false
);

function toggleFullScreen() {
  if (!document.fullscreenElement) {
    document.documentElement.requestFullscreen();
  } else if (document.exitFullscreen) {
    document.exitFullscreen();
  }
}

#Screen Orientation API

画面の向きを取得できる API です。

js
const orientation = screen.orientation;
orientation.addEventListerner("change", () => {
  console.log(orientation.type, orientation.angle);
});

rotateBtn.addEventListener("click", async () => {
  const oppositeOrientation = screen.orientation.type.startsWith("portrait")
    ? "landscape"
    : "portrait";
  scrren.orientation
    .lock(oppositeOrientation)
    .then(() => {
      console.log(`Locked to ${oppositeOrientation}`);
    })
    .catch((error) => {
      console.error(error);
    });
});

unlockBtn.addEventListener("click", () => {
  screen.otientation.unlock();
});

#Screen Wake Lock API

端末が自動で画面を暗くしたり、ロックしたりするのを防ぐ API です。アプリケーションの継続が必要なケースや、QR コードを表示しておく必要があるアプリなどで有用です。

js
const wakeLock = await navigator.wakeLock.request("screen");
wakeLock.addEventListener("release", () => {
  console.log("Happy release wakeLock!!");
});

await heavyTask();

await wakeLock.release();

ページのタブを切り替えるなどでドキュメントの可視性が変化すると解除されてしまう点には注意が必要です。必要に応じて、visibilityChange イベントを監視し、ロックを掛け直しましょう。

js
document.addEventListener("visibilitychange", async () => {
  if (wakeLock !== null && document.visibilityState === "visible") {
    wakeLock = await navigator.wakeLock.request("screen");
  }
});

#OffscreenCanvas

OffscreenCanvas は、端的に言えば DOM と Canvas API の分離です。

従来の Canvas を利用した高度なアニメーションの描画はメインスレッドを圧迫するため、ユーザーの阻害するという問題がありました。OffscreenCanvas では、アニメーションの描画を Web Worker の Worker スレッドで行うことで、この問題が解消されます。

今までも、DOM の操作は出来ないものの重いデータ処理を Web Worker などで行うことで、負荷を軽減することは出来ましたが、OffscreenCanvas は Transferable オブジェクトであり、実質オフスクリーンでレンダリングできる DOM から分離された Canvas であるため、描画までを Web Worker で担えます。

js
const offscreenCanvas = canvas.transferControllToOffscreen();
const worker = new Worker("worker.js");
worker.postMessage({ canvas: offscreenCanvas }, [offscreenCanvas]);

// worker.js
onmessage = (event) => {
  const offscreenCanvas = event.data.canvas;
  const ctx = offscreenCanvas.getContext("webgl");
  // ...
};

#Web Push API

今までは Android などでしかサポートされていなかった Web Push が、iOS 16.4 でサポートされました。 ホーム画面に追加された Web アプリから、Push API、Notifications API、Service Worker などを駆使して、ユーザーにプッシュ通知を送ることが可能になります。

またホーム画面に追加された Web アプリでカウントを表示できる Badging API などもサポートされ、今後 PWA の利用が広がると期待されます。

https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/

#ECMAScript features

Safari 16.4 は、非常に巨大なリリースです。JavaScritp の多くの機能も新たにサポートされました。

https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes

以下に、サポートされた機能のいくつかを軽く紹介します。

#Array.formAsync

Array.from が for 相当なのに対して、Array.fromAsync は for await と見れば理解しやすいと思います。 Async iterable を処理する方法として、Promise.all などがありますが、Promise.all が並列実行であるのに対して、 Array.fromAsync は for await 相当であり、順次実行されます。

https://github.com/tc39/proposal-array-from-async

#Array#group, Array#groupToMap

Array#group は、配列を指定した関数の戻り値でグルーピングしたオブジェクトを返します。 一方、Array#groupToMap は、配列を指定した関数の戻り値でグルーピングした Map を返します。

https://github.com/tc39/proposal-array-grouping

コードを見た方が理解しやすいと思います。

js
const animals = [
  { name: "たま", type: "猫" },
  { name: "みけ", type: "猫" },
  { name: "ぽち", type: "犬" },
];
console.log(animals.group((animal) => animal.type));
/* result
{
  猫: [
    { name: 'たま', type: '猫' },
    { name: 'みけ',  type: '猫' },
  ],
  犬: [
    { name: 'ぽち', type: '犬', },
  ],
}
*/
console.log(animals.groupToMap((animal) => animal.type).get("猫"));
/* result
[
  { name: 'たま', type: '猫' },
  { name: 'みけ',  type: '猫' },
]
*/

#Import Maps

ブラウザで実行される import によって読み込まれるパッケージの URL を指定する手段です。

https://github.com/WICG/import-maps#multiple-import-map-support

html
<head>
  <script type="importmap">
    {
      "imports": {
        "react": "https://unpkg.com/react@18/umd/react.development.js",
        "react-dom": "https://unpkg.com/react-dom@18/umd/react-dom.development.js"
      }
    }
  </script>
</head>
<body>
  <script type="module">
    import { createRoot } from "react-dom/client";
    import React from "react";
  </script>
</body>

Safari 16.4 beta

2023 年 2 月にリリースされた iOS 16.4 beta 1 及び Safari 16.4 beta について、いくつか紹介します。

#Web Push のサポート

今までは Android などでしかサポートされていなかった Web Push が、iOS 16.4 beta 1 でサポートされました。 ホーム画面に追加された Web アプリから、Push API、Notifications API、Service Worker などを駆使して、ユーザーにプッシュ通知を送ることが可能になります。

またホーム画面に追加された Web アプリでカウントを表示できる Badging API などもサポートされ、今後 PWA の利用が広がると期待されます。

https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/

#Many ECMAScript features supported

Safari 16.4 beta は、非常に巨大なリリースです。Web Push だけでなく ECMAScript の多くの機能が新たにサポートされました。

https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes

以下に、サポートされた機能のいくつかを軽く紹介します。

#Array.formAsync

Array.from が for 相当なのに対して、Array.fromAsync は for await と見れば理解しやすいと思います。 Async iterable を処理する方法として、Promise.all などがありますが、Promise.all が並列実行であるのに対して、 Array.fromAsync は for await 相当であり、順次実行されます。

https://github.com/tc39/proposal-array-from-async

#Array#group, Array#groupToMap

Array#group は、配列を指定した関数の戻り値でグルーピングしたオブジェクトを返します。 一方、Array#groupToMap は、配列を指定した関数の戻り値でグルーピングした Map を返します。

https://github.com/tc39/proposal-array-grouping

コードを見た方が理解しやすいと思います。

js
const animals = [
  { name: "たま", type: "猫" },
  { name: "みけ", type: "猫" },
  { name: "ぽち", type: "犬" },
];
console.log(animals.group((animal) => animal.type));
/* result
{
  猫: [
    { name: 'たま', type: '猫' },
    { name: 'みけ',  type: '猫' },
  ],
  犬: [
    { name: 'ぽち', type: '犬', },
  ],
}
*/
console.log(animals.groupToMap((animal) => animal.type).get("猫"));
/* result
[
  { name: 'たま', type: '猫' },
  { name: 'みけ',  type: '猫' },
]
*/

#Import Maps

ブラウザで実行される import によって読み込まれるパッケージの URL を指定する手段です。

https://github.com/WICG/import-maps#multiple-import-map-support

html
<head>
  <script type="importmap">
    {
      "imports": {
        "react": "https://unpkg.com/react@18/umd/react.development.js",
        "react-dom": "https://unpkg.com/react-dom@18/umd/react-dom.development.js"
      }
    }
  </script>
</head>
<body>
  <script type="module">
    import { createRoot } from "react-dom/client";
    import React from "react";
  </script>
</body>

#AV1 のサポート

AV1 コーデックが Safari 16.4 beta でサポートされました。 しかし、Apple の M1/M2 は AV1 用のハードウェアアクセラレータを搭載していないため、利用できる環境はほぼありません。

著者について

Hi there. I'm hrdtbs, a frontend expert and technical consultant. I started my career in the creative industry over 13 years ago, learning on the job as a 3DCG modeler and game engineer in the indie scene.

In 2015 I began working as a freelance web designer and engineer. I handled everything from design and development to operation and advertising, delivering comprehensive solutions for various clients.

In 2016 I joined Wemotion as CTO, where I built the engineering team from the ground up and led the development of core web and mobile applications for three years.

In 2019 I joined matsuri technologies as a Frontend Expert, and in 2020 I also began serving as a technical manager supporting streamers and content creators.

I'm so grateful to be working in this field, doing something that brings me so much joy. Thanks for stopping by.