Page profiler never stores above-the-fold background images: `b` is always sent with empty selector maps

オープン 初心者向け
#1,216 コメント 0 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@girishpanchal30 がすでに取り組んでいます。

2026年11月6日 から。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
77/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
javascript
領域
performance

調査の方向性

assets/js/modules/main.jsの110~139行付近を、assets/js/modules/image-detector.jsの82~104行付近にあるobserver callbackとあわせて読みます。背景のlazy loadingを有効にし、?optml_debug=1を指定して再現した後、背景セレクターのpayloadを調べます。各デバイスについて、above-the-foldの背景要素とそのURLが保存され、それに対応するlazy-load除外が設定されれば完了です。

索引モデルが issue の本文から書いたものです。

説明

bug
Description

On a normal page visit, the page profiler sends every background selector with an empty object, for example "b":{"[style*=\"background-image:url(\"]":{}}. This happens even when a background element is above the fold and its URL has been found. The stored profile ends up with "bg":{"<selector>":[]} for every device. Optimole therefore never learns which background images are above the fold, and the lazy-load CSS can only exempt the LCP background.

Cause

In assets/js/modules/main.js, the IntersectionObserver is created with the first selectorMap instance. Shortly after, selectorMap is replaced with a new Map:

// main.js:110-111
let selectorMap = new Map();
const observer = optmlImageDetector.createIntersectionObserver(aboveTheFoldImages, selectorMap);
...
// main.js:135
selectorMap = backgroundResult.selectorMap;

The observer callback (image-detector.js:100) checks selectorMap.has(baseSelector) against the original Map, which is always empty. Above-the-fold background elements are therefore never recorded. Meanwhile _processBackgroundSelectors (main.js:174) builds b from the new Map, which has the selector keys but no entries. With ?optml_debug=1, the log never prints Element with selector "…" is above the fold for background elements.

Expected: each matched selector lists the above-the-fold elements and their URLs, e.g. {"[style*=\"background-image:url(\"]":{"#content > … > div.wp-block-cover__image-background…":["https://…/qa-hero-bg.jpg"]}}.

Verification of the cause

I changed one line in the served script so it fills the existing Map instead of replacing it (backgroundResult.selectorMap.forEach((v, k) => selectorMap.set(k, v))). With that change:

  • The payload contained the selector and its URL.
  • The server stored them for mobile and desktop.
  • The lazy-load CSS on the page added a :not(...) exclusion for each above-the-fold background element, not only the LCP one.

I tested this on a page with two background groups side by side.

Impact

On pages with a single hero, nothing visible changes, because the hero is also the LCP element and is exempted and preloaded anyway. On pages with several background images above the fold, the ones that aren't the LCP element are still lazy-loaded. They can appear late and cost LCP/CLS points, which is what the profiler is meant to prevent.

Possible fix

Fill the existing Map, or pass the Map from processBackgroundSelectors to the observer, instead of reassigning selectorMap.

A related timing detail: extractBackgroundImageUrls runs right after processBackgroundSelectors (main.js:139), before the 300/600 ms wait. A background that gets optml-bg-lazyloaded during the wait would be sent with null URLs.

Not related to #1204 / #1214: it appears on both the released 4.2.17 build and the #1214 build. Found while testing #1204.

Step-by-step reproduction instructions
  1. Optimole 4.2.17, connected, with Lazy load and background lazy load on.
  2. Create a page whose first block has a background image in an inline style. For example, a Cover block with "Fixed background" on, or a Group block with a background image.
  3. In a private window (logged out), open the page with ?optml_debug=1 appended. Keep the console open.
  4. In the console, look at Sending background selectors:. Or look at the payload of the request to /wp-json/optml/v1/optimizations.
  5. Load the page in a mobile-width window too, so both devices are profiled. Then open it with ?optml_debug=true and read the <!-- plugin=optimole-wp measurement#… --> comment after </html>.

Result: b contains each matched selector with {}, and the stored profile shows "bg":{"…":[]} for both devices. Only the LCP background (lcp.bgUrls) is stored.

Screenshots, screen recording, code snippet or Help Scout ticket

Code links:
https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/assets/js/modules/main.js#L110-L135
https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/assets/js/modules/image-detector.js#L82-L104

Environment info

Optimole 4.2.17 (same code on master), WordPress + Neve, PHP 8.5, Chromium desktop 1440px / mobile 390px

Is the issue you are reporting a regression

No

主要言語
PHP
スター
74
フォーク
14
平均マージ
1日 20時間
マージ済み PR(30日)
39

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Codeinwp/optimole-wp のほかの issue

Codeinwp/optimole-wp の issue をすべて見る

似ている issue

PHP の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。