Page profiler never stores above-the-fold background images: `b` is always sent with empty selector maps
维护者通常 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 回调。启用背景懒加载并使用 ?optml_debug=1 重现问题,然后检查背景选择器的 payload。完成标准是:每种设备的首屏背景元素及其 URL 均已存储,并具有相应的懒加载排除项。
由索引模型根据 Issue 内容生成。
描述
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
- Optimole 4.2.17, connected, with Lazy load and background lazy load on.
- 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.
- In a private window (logged out), open the page with
?optml_debug=1appended. Keep the console open. - In the console, look at
Sending background selectors:. Or look at the payload of the request to/wp-json/optml/v1/optimizations. - Load the page in a mobile-width window too, so both devices are profiled. Then open it with
?optml_debug=trueand 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 小时
- 30 天内合并 PR
- 39
环境准备
- 提供 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
Codeinwp/optimole-wp 的其他 Issue
-
customer report feature-request-triage
难度 2/5 1-3 小时 新手友好度 72/100
Codeinwp/optimole-wp#1239 ·
维护者通常 1 天内回复
-
customer report feature-request-triage
难度 5/5 一周以上 新手友好度 35/100
Codeinwp/optimole-wp#1231 ·
维护者通常 1 天内回复
-
bug-report customer report
难度 4/5 3-5 天 新手友好度 56/100
Codeinwp/optimole-wp#1211 ·
维护者通常 1 天内回复
-
customer report feature-request-triage
难度 5/5 一周以上 新手友好度 35/100
Codeinwp/optimole-wp#1179 ·
维护者通常 1 天内回复
-
customer report feature-request-triage
难度 4/5 3-5 天 新手友好度 54/100
Codeinwp/optimole-wp#1178 ·
维护者通常 1 天内回复
查看 Codeinwp/optimole-wp 的全部 Issue
相似的 Issue
-
难度 1/5 1 小时以内 新手友好度 72/100
维护者通常 1 天内回复
-
claude
难度 2/5 1-3 小时 新手友好度 74/100
sprain/sprainocchio#38 ·
维护者通常 1 天内回复
-
sync-en
难度 2/5 1-3 小时 新手友好度 76/100
维护者通常 1 天内回复
-
sync-en
难度 2/5 1-3 小时 新手友好度 68/100
维护者通常 1 天内回复
-
Перевод устарел
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复