好きなものだけ書く。ポジティブに。

好きなことを楽しく。プログラミング、写真、音楽、ガジェットとか。

Lightning Web Components で外部ライブラリを使っている時に Illegal invocation が出た時は this.template.querySelector から見ていくと特定しやすいかもしれない

タイトルで言い切ったやつなんですけど、詳細を書いていきます。

f:id:noblejasper:20191214115235j:plain
最近スクーバダイビングにハマっていて12月に潜った時の写真です。内容とは一切関係ありません。

Salesforce上の Lightning Web Components(以下LWC)で、FullCalendar を使ったものを作ろうとしていて詰まった&あんまりドキュメント見つけられなかったのでやったことを忘れないようにまとめておきます。

起きたこと

現状の結論としては、LWCの this.template.querySelector は Element じゃないっぽいのでpollyfillでこんな感じに書いていると Illegal invocation が出るぽい

const matchesMethod =
  Element.prototype.matches ||
  (Element.prototype as any).matchesSelector ||
  (Element.prototype as any).msMatchesSelector

const closestMethod = Element.prototype.closest || function (selector) {
  // polyfill
  let el = this
  if (!document.documentElement.contains(el)) {
    return null
  }
  do {
    if (elementMatches(el, selector)) {
      return el
    }
    el = el.parentElement || el.parentNode
  } while (el !== null && el.nodeType === 1)
  return null
}

細かく検証出来ていないのですが、LWC側でこういった感じでFullCalendarを使おうと思った時に私の環境では発生しました

let calendarEl = this.template.querySelector('[data-id="ScheduleAdjustmentCalendar"]');
new FullCalendar.Calendar(calendarEl, {
  plugins: ['dayGrid']
}).render();

こうやって対策してみた

私の場合は元ライブラリをforkしてブランチを切ってそこから静的リソースに持ってくるようにしてしまいました。 compareこんな感じです

if (typeof el.matches === 'function') {
  return el.matches(selector)
}

みたいに回避しちゃいました。もっといい解決策ありそうだけど。。。誰か教えて!

細かく見ていないけどきっとこうなんじゃないか仮設

Illegal invocation が出る時って「不正な呼び出し」なので違うクラスの同じメソッドをcallで呼ぼうとした時とかに出るぽいので、 今回の場合 Element.prototype.closest とか Element クラスのメソッドだと思って呼んでいるけど、呼んだクラスが Element じゃない時に発生するんじゃないかなと。

事象の項にも書きましたが、おそらくですが、普通にWebサイト作ってJS呼んだときの document.querySelectorElement とか HTMLElement が返ってくるけど、LWCの this.template.querySelector はLWC独自のなにかしらのProxy的なクラスが返ってくる(HTMLElementと同等のAPIを持っているか移譲しているかはわからない)から、今回のような pollyfillで書いたコードでエラーが発生してしまうのではないかなと思っております。(これ参考文献探したけど見つけられなかったので、あくまでも推測です。)

とはいえ直接呼べるなら呼んじゃえばいいじゃん。と思って typeof el.closestfunction だったらそれをそのまま呼んじゃえって書いてます。

いくつもの環境で試すとかはまだ出来ていないのですが今回はSalesforce上のLightningWebComponent上に限った話なのでこのまま行こうかなって気持ちです。

この辺りを参考にして作りました