Matomoヒートマップでスティッキーヘッダーが繰り返し表示される(または固まる)原因と修正方法

fixedおよびstickyヘッダーは、Matomoのヒートマップスクリーンショットでページを繰り返し流れたり、中央で固まったりします。Matomoがキャプチャ済みDOMを、ヘッダーが基準にできる一貫したビューポートなしに一枚の縦長画像としてレンダリングするためです。発生原因と対処方法を解説します。

Matomoのヒートマップを開いたら、ヘッダーが消印のようにページに2回、3回、ときには6回も押されていた、あるいは1枚だけ中央で凍りついて下のコンテンツを覆い隠していた、という経験があれば、クリックデータ自体は問題ありません。Matomoは正しい座標に記録しています。壊れているのはその下のスクリーンショットです。スティッキーヘッダーがブラウザ上と同じようにレンダリングされないため、スクロール位置ごとに繰り返されるか、レンダラーがたまたま見ていた場所に固定されます。どちらの場合でも、その下のクリックがナビリンクの上にプロットされ、ヒートマップの一部が大きなデッドゾーンになります。

最も手軽な修正方法は、私たちが公開している無料のChrome拡張機能 Matomo Heatmap Helper です。ページ上のfixedおよびstickyヘッダーをすべて検出し、キャプチャ中は position: relative に同じ高さのプレースホルダーとともに切り替え、キャプチャ後に元に戻します。この記事では、拡張機能なしで同じことをする方法と、ヒートマップを定期的にキャプチャするなら導入する価値のある恒久的なCSSパターンを紹介します。

拡張機能なしで修正する方法

キャプチャ直前に問題を回避するコンソールスニペットと、サイトに組み込める恒久的なCSSブランチがあります。制約に合ったほうを選んでください。

ページ上のfixed・stickyヘッダーをすべて見つける

変更を加える前に、何が固定されているか確認しましょう。ChromeでページをF12で開き、DevToolsのConsoleに切り替えてから貼り付けます:

js
// Logs every header-like element and its computed position value
document.querySelectorAll('header, nav, [role=banner], [role=navigation], .header, .navbar, .nav, .sticky')
  .forEach(el => console.log(getComputedStyle(el).position, el));

fixed または sticky と表示された要素が怪しい候補です。マーケティングサイトではたいてい複数見つかります。メインヘッダーがfixed、クッキーバーがfixed、「トップへ戻る」ボタンがfixed、サブナビがstickyといった具合で、それぞれが独自のやり方でスクリーンショットを壊します。

キャプチャ直前にすべてのヘッダーを解除する

ヒートマップキャプチャをトリガーする前にこれを貼り付けてください。拡張機能の sticky-header-fixer が行うことを再現しています。fixedおよびstickyのヘッダーを position: relative に切り替え、fixed ヘッダー(ドキュメントフローから切り離されている)には見えない同じ高さのプレースホルダーを挿入してスクロール位置とクリック座標がずれないようにします。

js
// Paste into the browser console. Unsticks every fixed/sticky header so it
// doesn't repeat or float across the heatmap, and inserts a placeholder
// with the same height/width to preserve the rest of the layout.
(() => {
  const SELECTOR = 'header, nav, [role=banner], [role=navigation], .header, .navbar, .nav, .sticky';
  let unstuck = 0;
 
  document.querySelectorAll(SELECTOR).forEach(el => {
    const cs = getComputedStyle(el);
    if (cs.position !== 'fixed' && cs.position !== 'sticky') return;
 
    // Capture original dimensions before changing position
    const rect = el.getBoundingClientRect();
 
    // Insert invisible placeholder for fixed (sticky already takes layout space)
    if (cs.position === 'fixed' && el.parentElement) {
      const ph = document.createElement('div');
      ph.style.height = rect.height + 'px';
      ph.style.width = rect.width + 'px';
      ph.style.visibility = 'hidden';
      ph.dataset.heatmapPlaceholder = 'true';
      el.parentElement.insertBefore(ph, el);
    }
 
    // Convert to relative so it joins document flow and stops repeating
    el.style.position = 'relative';
    el.style.top = 'auto';
    el.style.bottom = 'auto';
    el.style.left = 'auto';
    el.style.right = 'auto';
    el.style.zIndex = 'auto';
    el.style.width = 'auto';
    unstuck++;
  });
 
  console.log(`Unstuck ${unstuck} headers. Now trigger your Matomo heatmap capture.`);
  console.log('To revert: location.reload(), or remove the placeholders by hand.');
})();

実行してカウントを確認し、キャプチャをトリガーします。Unstuck 0 headers と表示されているのにページで繰り返しヘッダーが見える場合、上のセレクターリストが実際に使われているクラスを拾えていません。要素を検証して position: fixed が付いているラッパー要素を探し、そのセレクターを SELECTOR に追加してください。

キャプチャ中はヘッダーを非表示にする

そもそも、スティッキーヘッダーのヒートマップはほとんど役に立ちません。訪問者がスクロールするとヘッダーも一緒に動き、ナビへのクリックは異なるページY座標に散らばります。結果はページ全体の高さにまたがって引き伸ばされたナビのヒートマップになります。ヘッダー自体を調べる必要がなければ、最もシンプルな解決策は完全に除外することです:

js
// Hides every header during the capture
document.querySelectorAll('header, nav, [role=banner], .navbar, .sticky')
  .forEach(el => el.style.display = 'none');

ヘッダーがないものとしてキャプチャが返ってきます。その下の実際のクリックは正しい要素に落ちます。クリック座標は変わらないので。ナビ自体の(ほぼ無意味な)ヒートマップデータは失われますが。

キャプチャモード専用の恒久的なCSS修正

同じページを繰り返しキャプチャするなら、コンソールへの貼り付け作業はすぐ面倒になります。?matomo_heatmap=1 のようなフラグ付きURLにアクセスしたときだけ有効になるCSSブランチが現実的です。フラグが存在するときに <html> にクラスを追加します:

js
// Add to your site (or your tag manager)
if (new URLSearchParams(location.search).has('matomo_heatmap')) {
  document.documentElement.classList.add('heatmap-mode');
}

そのクラス内でヘッダーを解除するCSSを組み込みます:

css
/* Edit selectors to match your site's header markup */
html.heatmap-mode header,
html.heatmap-mode nav,
html.heatmap-mode [role=banner],
html.heatmap-mode .navbar {
  position: relative !important;
  top: auto !important;
  left: auto !important;
  right: auto !important;
  bottom: auto !important;
  width: auto !important;
  z-index: auto !important;
}

キャプチャのワークフローは ?matomo_heatmap=1 にアクセスしてヒートマップをトリガーするだけになります。コンソールへの貼り付けも、忘れるリスクもありません。デメリットは、position: fixed ヘッダーに対してレイアウトを保つプレースホルダートリックを純CSSで表現するのが難しいため、クラスを切り替えたときにページが若干ずれることです。ほとんどのサイトではこれで問題ありません。重要なのはクリックデータであり、ビジュアルは実際のレイアウトと十分近いので読み取れます。

Matomoがスティッキーヘッダーを正しくレンダリングできない理由

MatomoのヒートマップレンダラーはシリアライズされたDOMを受け取り、<body> の先頭からページ最下部まで、ページの全高に相当する一枚の縦長画像としてラスタライズします。そのパイプラインにスクロールはありません。実際のブラウザが持つようなビューポートもありません。一枚の大きなキャンバスです。

position: fixed のヘッダーはドキュメントフローから切り離され、現在のビューポートに固定されます。ビューポートのない一枚の縦長キャンバスをレンダラーが生成するとき、結果は未定義です。スクロールステップごとにヘッダーを押印するレンダラーもあり、600〜1000ピクセルおきに繰り返されます。任意のy座標に固定してそれで終わりにするものもあります。position: sticky は逆方向から同じ問題に直面します。スティッキーの境界はスクロールする祖先要素を基準に定義されますが、ページが一枚の画像としてレンダリングされるとスクロールする祖先が存在しません。

いずれにせよ、ビジュアル出力は正しくなく、(ブラウザ内トラッカーによって実際のページ座標に記録された)クリックが正しい位置にないヘッダーのピクセルの上にプロットされます。

実際に起きていること

よく遭遇するパターンをいくつか挙げます:

  • メインサイトヘッダーが position: fixed で、レンダラーがページを600〜1000ピクセルおきに押印する。ヒーロー、バリュープロップ、テスティモニアル、そのすべての上に同じナビが浮かんでいる。
  • クッキーの同意バーが position: fixed でビューポートの下部に固定されている。スクリーンショットの途中に現れ、料金表や商品グリッドを覆い隠す。
  • 「トップへ戻る」ボタンやフローティングチャットウィジェットが右下に position: fixed で配置されている。セクションごとに押印され、そのボタンとは無関係なコンテンツ全体にクリックマーカーが散らばる。
  • サブナビが高い z-indexposition: sticky で、レンダラーが任意のスクロール位置に固定してしまう。そのy座標より下のクリックがページ全体でサブナビの上にプロットされる。
  • 訪問者がクリック前に閉じたモーダルやバナーがスクリーンショットに残っている。閉じる状態がJavaScriptに存在していて、レンダラーがシリアライズされたDOMから新規に始めるためです。

ヒートマップで繰り返しナビが表示されている、オーバーレイが凍りついている、クリック座標がヘッダーのピクセルの上にプロットされているなら、これらのどれかに当たっています。メインヘッダーとクッキーバーが両方あるサイトでは、2つ同時に当たっています。

これはスクリーンショットでフォント、画像、スクロールコンテナが壊れる問題と同じ種類です。残りの問題を解説したMatomoヒートマップスクリーンショットが壊れる原因の詳細記事があります。またフォント画像についても個別の記事があります。どちらもほぼ同じくらい頻繁に発生します。

私たちが実際にやること

同じページで2〜3回以上ヒートマップをキャプチャするなら、?matomo_heatmap=1 CSSブランチを組み込みましょう。数行のCSSを一度設定すれば、その後のワークフローはクエリパラメータを付けるだけです。プレースホルダーを省いたことによる若干のレイアウトずれは、ほぼ問題になりません。

テンプレートもCSSも触れない場合は、キャプチャのたびに解除スニペットを貼り付けてください。見栄えは悪いですが、DevToolsを開けるページならどこでも機能します。

多くのページをまたいで複数クライアントの作業をしていて、他のスタックにコードを追加したくない場合、Matomo Heatmap Helper Chrome拡張機能が自動でこれを行います。fixedとstickyのヘッダーをすべて検出し、同じ高さのプレースホルダーとともに position: relative に切り替えてキャプチャを実行し、その後すべてを元に戻します。フォント、画像、スクロールコンテナも同じロジックで一括対応します。無料・オープンソースで、コードはGitHubで公開しています。

Martez はこの拡張機能が生まれた上位プロジェクトです。Matomoを Meta Ads および Google Ads と接続し、ROAS、CLV、アトリビューションをウェブ解析の隣に並べます。別のスプレッドシートではなく。現在プライベートベータ中です。関心があればウェイトリストに参加してください

ほとんどの場合、CSSブランチ一つで解決します。一度やる価値は十分あります。

Matomoヒートマップでスティッキーヘッダーが繰り返し表示される(または固まる)原因と修正方法 - Martez Blog