PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.7
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.7
1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 33 releases
← All changes | includes/modules/Lazy/LazyModule.php +59 -15 1.3.5 → 1.3.7 View file →
@@ -41,9 +41,10 @@
41 41 return array(
42 42 'label' => __( 'Media Optimization', 'xspeed' ),
43 43 'tab_label' => __( 'Lazy Loading', 'xspeed' ), // its own tab on the Media Optimization page
44 44 'icon' => 'Image',
45 - 'description' => __( 'Control how images, iframes, and videos load — lazy-loading, missing dimensions, and format optimization.', 'xspeed' ),
45 + 'description' => __( 'Loads images, videos and embeds only when visitors scroll to them.', 'xspeed' ),
46 + 'group' => 'performance',
46 47 // Host page: Lazy Loading (this module) + Image Optimization (Pro)
47 48 // + AI Suggestions (Pro) as tabs — everything a page loads on one
48 49 // page instead of separate rows (FBS-83633). Fonts is NOT here: it
49 50 // has its own Optimization card (#86).
@@ -55,49 +56,64 @@
55 56 return array(
56 57 'lazy_images' => array(
57 58 'type' => 'bool',
58 59 'default' => true,
59 - 'label' => __( 'Lazy-load Images', 'xspeed' ),
60 - 'description' => __( 'Add loading="lazy" + decoding="async" to <img> tags in post content. The first images on the page get loading="eager" so the LCP image is not deferred.', 'xspeed' ),
60 + 'label' => __( 'Lazy-load images', 'xspeed' ),
61 + 'description' => __( 'Images load when the visitor scrolls near them. The first image on the page still loads right away.', 'xspeed' ),
61 62 ),
62 63 'lazy_iframes' => array(
63 64 'type' => 'bool',
64 65 'default' => true,
65 - 'label' => __( 'Lazy-load Iframes', 'xspeed' ),
66 - 'description' => __( 'Add loading="lazy" to <iframe> tags. Useful for YouTube / Vimeo embeds + map widgets that pull a lot of bytes.', 'xspeed' ),
66 + 'label' => __( 'Lazy-load embeds', 'xspeed' ),
67 + 'description' => __( 'Embedded content such as YouTube videos and maps loads when the visitor scrolls near it.', 'xspeed' ),
67 68 ),
68 69 'video_facade' => array(
69 70 'type' => 'bool',
70 71 'default' => false,
71 - 'label' => __( 'Click-to-Play Video Facade', 'xspeed' ),
72 - 'description' => __( 'Replace YouTube and Vimeo embeds — and self-hosted <video> tags that have a poster — with the poster image and a play button. The video only loads when a visitor clicks it, so a page with embeds no longer pays ~1MB of third-party JavaScript, or the full weight of a hosted video file, for visitors who never press play. Autoplaying videos are left alone. Falls back to the normal embed when JavaScript is off.', 'xspeed' ),
72 + 'label' => __( 'Click-to-play videos', 'xspeed' ),
73 + 'description' => __( 'Shows a preview image with a play button in place of YouTube, Vimeo and your own videos that have a poster image. The video loads only when clicked. Autoplaying videos are left alone.', 'xspeed' ),
73 74 ),
74 75 'lazy_videos' => array(
75 76 'type' => 'bool',
76 77 'default' => true,
77 - 'label' => __( 'Lazy-load HTML5 Videos', 'xspeed' ),
78 - 'description' => __( 'Set preload="none" on self-hosted <video> tags, overriding a player\'s own preload="auto"/"metadata". Autoplaying videos are left alone — they need their bytes regardless. Browsers do not yet support loading="lazy" on video; preload="none" is the closest equivalent.', 'xspeed' ),
78 + 'label' => __( 'Lazy-load your own videos', 'xspeed' ),
79 + 'description' => __( 'Videos uploaded to your site download only when played. Autoplaying videos are left alone.', 'xspeed' ),
79 80 ),
81 + 'lazy_background_images' => array(
82 + 'type' => 'bool',
83 + 'default' => false,
84 + 'label' => __( 'Lazy-load background images', 'xspeed' ),
85 + 'description' => __( 'Hold back background images set in an element\'s inline style (a Cover or Group block, a page-builder section) until the element is near the screen. Uses a small script; visitors without JavaScript get every background as usual. The first N backgrounds on the page load straight away, like images. Backgrounds set in a stylesheet are not affected.', 'xspeed' ),
86 + ),
80 87 'eager_first_n' => array(
81 88 'type' => 'int',
82 89 'default' => 1,
83 90 'min' => 0,
84 91 'max' => 10,
85 - 'label' => __( 'Eager-load First N Images', 'xspeed' ),
86 - 'description' => __( 'How many images at the top of the post get loading="eager". 1 is usually right (the LCP hero image). 0 to lazy-load everything.', 'xspeed' ),
92 + 'label' => __( 'Images to load right away', 'xspeed' ),
93 + 'description' => __( 'How many images at the top of the page skip lazy loading. Only the first of them also loads at high priority, and images in hidden or closed sections are not counted. Lazy-loaded background images use the same number. 1 suits most sites; 0 lazy-loads every image.', 'xspeed' ),
94 + 'advanced' => true,
95 + // Both counters read this number (class-lazy-loader.php), so it
96 + // shows while either kind of lazy loading is on.
97 + 'dependsOn' => array(
98 + 'any' => array(
99 + array( 'field' => 'lazy_images' ),
100 + array( 'field' => 'lazy_background_images' ),
101 + ),
102 + ),
87 103 ),
88 104 'add_missing_dimensions' => array(
89 105 'type' => 'bool',
90 106 'default' => true,
91 - 'label' => __( 'Add Missing Image Dimensions', 'xspeed' ),
92 - 'description' => __( 'When an <img class="wp-image-N"> has no width/height, look the values up from the media library and inject them. Prevents the page-layout shift that hurts CLS scores.', 'xspeed' ),
107 + 'label' => __( 'Add missing image sizes', 'xspeed' ),
108 + 'description' => __( 'Adds width and height to images that lack them, using the media library. This stops the page from jumping as images load.', 'xspeed' ),
93 109 ),
94 110 'excluded_images' => array(
95 111 'type' => 'list',
96 112 'default' => array(),
97 113 'item_type' => 'string',
98 - 'label' => __( 'Excluded Images', 'xspeed' ),
99 - 'description' => __( 'Substring patterns that, if found anywhere in the <img> / <iframe> tag (typically a class or filename), exempt that element from lazy-loading. Useful for hero / logo / sprite images. Or add data-skip-lazy to the tag directly.', 'xspeed' ),
114 + 'label' => __( 'Excluded images', 'xspeed' ),
115 + 'description' => __( 'Images and embeds whose tag contains a line here, such as a class or file name, are never lazy-loaded. Useful for logos and hero images.', 'xspeed' ),
100 116 ),
101 117 );
102 118 }
103 119
@@ -155,8 +171,9 @@
155 171 $any_enabled = ! empty( $opts['lazy_images'] )
156 172 || ! empty( $opts['lazy_iframes'] )
157 173 || ! empty( $opts['lazy_videos'] )
158 174 || ! empty( $opts['video_facade'] )
175 + || ! empty( $opts['lazy_background_images'] )
159 176 || ! empty( $opts['add_missing_dimensions'] );
160 177 if ( ! $any_enabled ) {
161 178 return;
162 179 }
@@ -193,8 +210,15 @@
193 210 // script sets a src and commits the fetch.
194 211 add_action( 'wp_head', array( $this, 'print_observer_script' ), 1 );
195 212 }
196 213
214 + // Head, because the rule that holds a background back has to apply
215 + // before first paint, or the browser has already requested it.
216 + if ( ! empty( $opts['lazy_background_images'] ) ) {
217 + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_background_style' ) );
218 + add_action( 'wp_head', array( $this, 'print_background_script' ), 1 );
219 + }
220 +
197 221 // Same conditional-footer treatment for the autoplay restorer: it is
198 222 // only printed on a response that actually deferred one.
199 223 if ( ! empty( $opts['lazy_videos'] ) ) {
200 224 /*
@@ -225,8 +249,28 @@
225 249 public function enqueue_facade_style(): void {
226 250 wp_register_style( 'xspeed-video-facade', false, array(), XSPEED_VERSION );
227 251 wp_enqueue_style( 'xspeed-video-facade' );
228 252 wp_add_inline_style( 'xspeed-video-facade', \XSpeed\Video_Facade::facade_style() );
253 + }
254 +
255 + public function enqueue_background_style(): void {
256 + wp_register_style( 'xspeed-lazy-bg', false, array(), XSPEED_VERSION );
257 + wp_enqueue_style( 'xspeed-lazy-bg' );
258 + wp_add_inline_style( 'xspeed-lazy-bg', Lazy_Loader::background_style() );
259 + }
260 +
261 + /**
262 + * data-xs-nodelay: Delay JS would otherwise hold this back until the
263 + * first interaction, and every held background would stay blank.
264 + */
265 + public function print_background_script(): void {
266 + wp_print_inline_script_tag(
267 + Lazy_Loader::background_script(),
268 + array(
269 + 'id' => 'xspeed-lazy-bg',
270 + 'data-xs-nodelay' => true,
271 + )
272 + );
229 273 }
230 274
231 275 /**
232 276 * Emit the click-to-play handler inline. Inline (not enqueued) because