Islands in the byte stream

Technical notes by a software engineer

SafariにはMediaQueryList.prototype.addEventListenerがない

ないと困るので、 polyfillをつくりました。MediaQueryList すら実体がないので、やむをえず mathMedia() を上書きしています。

// MediaQueryList.prototype.addEventListener.ts 

if (typeof matchMedia !== "undefined" && !matchMedia("all").addEventListener) {
  console.log('installing polyfill: MediaQueryList.prototype.addEventListener');

  const originalMatchMedia = matchMedia;
  self.matchMedia = function matchMedia(mediaQuery: string): MediaQueryList {
    const mql = originalMatchMedia(mediaQuery);
    mql.addEventListener = function (eventName: "change", listener: (event: MediaQueryListEvent) => void) {
      // tslint:disable-next-line
      this.addListener(listener);
    };
    return mql;
  };
}

なぜないかというと、このAPIを定めたCSSOMで、最初は addListener というメソッドでイベントリスナを登録していたのが、途中からDOM標準の EventTarget interface を実装するものとして addEventListener を利用可能にしたから、ということのようです。

https://drafts.csswg.org/cssom-view/#mediaquerylist

Note: This specification initially had a custom callback mechanism with addListener() and removeListener(), and the callback was invoked with the associated media query list as argument. Now the normal event mechanism is used instead. For backwards compatibility, the addListener() and removeListener() methods are basically aliases for addEventListener() and removeEventListener(), respectively, and the change event masquerades as a MediaQueryList.

こういう経緯で TypeScript の lib.dom.d.ts では addListener がdeprecated扱いではあるものの、素直に addEventListener を使えない、と。こういうのはpolyfillするのがいいですね。npmにもありそうですが、実装するのは簡単なので自前実装ということにしました。