diff --git a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php index 2bd0726468..54dab6006d 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php @@ -216,14 +216,19 @@ private function lazy_load_videos( ?string $poster, OD_Tag_Visitor_Context $cont if ( 'auto' !== $initial_preload ) { $processor->set_attribute( 'preload', 'auto' ); } + $processor->remove_attribute( 'loading' ); return; } // If the element is visible in any viewport, do not lazy-load it. if ( $max_intersection_ratio > 0 ) { + $processor->remove_attribute( 'loading' ); return; } + // Add native lazy loading for browsers that support it. + $processor->set_attribute( 'loading', 'lazy' ); + if ( 'none' !== $initial_preload ) { $processor->set_attribute( 'data-original-preload', null !== $initial_preload ? $initial_preload : 'default' ); $processor->set_attribute( 'preload', 'none' ); diff --git a/plugins/image-prioritizer/lazy-load-video.js b/plugins/image-prioritizer/lazy-load-video.js index c569fa42be..59b6f5e2f2 100644 --- a/plugins/image-prioritizer/lazy-load-video.js +++ b/plugins/image-prioritizer/lazy-load-video.js @@ -1,38 +1,50 @@ -const lazyVideoObserver = new IntersectionObserver( - ( entries ) => { - for ( const entry of entries ) { - if ( entry.isIntersecting ) { - const video = /** @type {HTMLVideoElement} */ entry.target; +const restoreVideo = ( /** @type {HTMLVideoElement} */ video ) => { + const poster = video.getAttribute( 'data-original-poster' ); + if ( poster ) { + video.setAttribute( 'poster', poster ); + } - const poster = video.getAttribute( 'data-original-poster' ); - if ( poster ) { - video.setAttribute( 'poster', poster ); - } + if ( video.hasAttribute( 'data-original-autoplay' ) ) { + video.setAttribute( 'autoplay', 'autoplay' ); + } - if ( video.hasAttribute( 'data-original-autoplay' ) ) { - video.setAttribute( 'autoplay', 'autoplay' ); - } + const preload = video.getAttribute( 'data-original-preload' ); + if ( preload ) { + if ( 'default' === preload ) { + video.removeAttribute( 'preload' ); + } else { + video.setAttribute( 'preload', preload ); + } + } +}; - const preload = video.getAttribute( 'data-original-preload' ); - if ( preload ) { - if ( 'default' === preload ) { - video.removeAttribute( 'preload' ); - } else { - video.setAttribute( 'preload', preload ); - } - } +const videos = document.querySelectorAll( 'video.od-lazy-video' ); - lazyVideoObserver.unobserve( video ); +// When the browser natively supports lazy-loading on video, restore the original +// attributes immediately and rely on loading="lazy" to defer the video load. +if ( 'loading' in HTMLMediaElement.prototype ) { + for ( const video of videos ) { + restoreVideo( /** @type {HTMLVideoElement} */ ( video ) ); + } +} else { + const lazyVideoObserver = new IntersectionObserver( + ( entries ) => { + for ( const entry of entries ) { + if ( entry.isIntersecting ) { + restoreVideo( + /** @type {HTMLVideoElement} */ ( entry.target ) + ); + lazyVideoObserver.unobserve( entry.target ); + } } + }, + { + rootMargin: '100% 0% 100% 0%', + threshold: 0, } - }, - { - rootMargin: '100% 0% 100% 0%', - threshold: 0, - } -); + ); -const videos = document.querySelectorAll( 'video.od-lazy-video' ); -for ( const video of videos ) { - lazyVideoObserver.observe( video ); + for ( const video of videos ) { + lazyVideoObserver.observe( video ); + } } diff --git a/plugins/image-prioritizer/tests/test-cases/multiple-videos-on-all-breakpoints/expected.html b/plugins/image-prioritizer/tests/test-cases/multiple-videos-on-all-breakpoints/expected.html index 7a1458bba1..9c09c446e5 100644 --- a/plugins/image-prioritizer/tests/test-cases/multiple-videos-on-all-breakpoints/expected.html +++ b/plugins/image-prioritizer/tests/test-cases/multiple-videos-on-all-breakpoints/expected.html @@ -10,11 +10,11 @@
- - - + + +
- + diff --git a/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/buffer.html b/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/buffer.html new file mode 100644 index 0000000000..991f9d0477 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/buffer.html @@ -0,0 +1,13 @@ + + + + ... + + + + +
+ +
+ + diff --git a/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/expected.html b/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/expected.html new file mode 100644 index 0000000000..526abbd89f --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/expected.html @@ -0,0 +1,16 @@ + + + + ... + + + + +
+ +
+ + + + + diff --git a/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/set-up.php b/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/set-up.php new file mode 100644 index 0000000000..e785d410ce --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-hidden-on-all-breakpoints/set-up.php @@ -0,0 +1,30 @@ +store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'elements' => array( + array( + 'isLCP' => false, + 'xpath' => '/HTML/BODY/DIV[@id=\'page\']/*[1][self::VIDEO]', + 'boundingClientRect' => $test_case->get_sample_dom_rect(), + 'intersectionRatio' => 0.0, + ), + ), + ) + ) + ); + } +}; diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/buffer.html b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/buffer.html new file mode 100644 index 0000000000..586a78c916 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/buffer.html @@ -0,0 +1,13 @@ + + + + ... + + + + +
+ +
+ + diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/expected.html b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/expected.html new file mode 100644 index 0000000000..2dbf76fce8 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/expected.html @@ -0,0 +1,15 @@ + + + + ... + + + + +
+ +
+ + + + diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/set-up.php b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/set-up.php new file mode 100644 index 0000000000..7eb4dae9b4 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-already-set-and-is-visible/set-up.php @@ -0,0 +1,30 @@ +store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'elements' => array( + array( + 'isLCP' => false, + 'xpath' => '/HTML/BODY/DIV[@id=\'page\']/*[1][self::VIDEO]', + 'boundingClientRect' => $test_case->get_sample_dom_rect(), + 'intersectionRatio' => 0.5, + ), + ), + ) + ) + ); + } +}; diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/buffer.html b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/buffer.html new file mode 100644 index 0000000000..586a78c916 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/buffer.html @@ -0,0 +1,13 @@ + + + + ... + + + + +
+ +
+ + diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/expected.html b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/expected.html new file mode 100644 index 0000000000..2950908bb9 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/expected.html @@ -0,0 +1,16 @@ + + + + ... + + + + + +
+ +
+ + + + diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/set-up.php b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/set-up.php new file mode 100644 index 0000000000..ffcf87e56f --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-loading-lazy-on-lcp-element/set-up.php @@ -0,0 +1,30 @@ +store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'elements' => array( + array( + 'isLCP' => true, + 'xpath' => '/HTML/BODY/DIV[@id=\'page\']/*[1][self::VIDEO]', + 'boundingClientRect' => $test_case->get_sample_dom_rect(), + 'intersectionRatio' => 1.0, + ), + ), + ) + ) + ); + } +};