forked from WordPress/performance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hooks.php
115 lines (109 loc) · 3.29 KB
/
hooks.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<?php
/**
* Hook callbacks used for oEmbed Optimizer.
*
* @since n.e.x.t
* @package performance-lab
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Filter the oEmbed HTML.
*
* Add loading="lazy" to any iframe tags.
* Lazy load any script tags.
*
* @since n.e.x.t
*
* @param string $html The oEmbed HTML.
* @return string
*/
function perflab_optimize_oembed_html( string $html ): string {
$p = new WP_HTML_Tag_Processor( $html );
/**
* Determine how to lazy load the embed.
*
* - If there is only one iframe, set loading="lazy".
* - Prevent making scripts lazy if there is an inline script.
* - Only make script lazy if there is a single external script (since if there are
* multiple they may not get loaded in the right order).
* - Ensure that both the iframe and the script are made lazy if both occur in the same embed.
*/
$iframe_count = 0;
$script_count = 0;
$has_inline_script = false;
// Locate the iframes and scripts.
while ( $p->next_tag() ) {
if ( 'IFRAME' === $p->get_tag() ) {
$loading_value = $p->get_attribute( 'loading' );
if ( empty( $loading_value ) ) {
++$iframe_count;
$p->set_bookmark( 'iframe' );
}
} elseif ( 'SCRIPT' === $p->get_tag() ) {
if ( ! $p->get_attribute( 'src' ) ) {
$has_inline_script = true;
} else {
++$script_count;
$p->set_bookmark( 'script' );
}
}
}
// If there was only one non-inline script, make it lazy.
if ( 1 === $script_count && ! $has_inline_script ) {
add_action( 'wp_footer', 'perflab_optimize_oembed_lazy_load_scripts' );
$p->seek( 'script' );
$p->set_attribute( 'data-lazy-embed-src', $p->get_attribute( 'src' ) );
$p->remove_attribute( 'src' );
}
// If there was only one iframe, make it lazy.
if ( 1 === $iframe_count ) {
$p->seek( 'iframe' );
$p->set_attribute( 'loading', 'lazy' );
}
return $p->get_updated_html();
}
add_filter( 'embed_oembed_html', 'perflab_optimize_oembed_html' );
/**
* Add a script to the footer if there are lazy loaded embeds.
* Load the embed's scripts when they approach the viewport using an IntersectionObserver.
*
* @since n.e.x.t
*/
function perflab_optimize_oembed_lazy_load_scripts() {
?>
<script type="module">
const lazyEmbedsScripts = document.querySelectorAll( 'script[data-lazy-embed-src]' );
const lazyEmbedScriptsByParents = new Map();
const lazyEmbedObserver = new IntersectionObserver(
( entries ) => {
for ( const entry of entries ) {
if ( entry.isIntersecting ) {
const lazyEmbedParent = entry.target;
const lazyEmbedScript = lazyEmbedScriptsByParents.get( lazyEmbedParent );
const embedScript = document.createElement( 'script' );
for ( const attr of lazyEmbedScript.attributes ) {
embedScript.setAttribute(
attr.nodeName === 'data-lazy-embed-src' ? 'src' : attr.nodeName,
attr.nodeValue
);
}
lazyEmbedScript.replaceWith( embedScript );
lazyEmbedObserver.unobserve( lazyEmbedParent );
}
}
},
{
rootMargin: '100% 0% 100% 0%',
threshold: 0
}
);
for ( const lazyEmbedScript of lazyEmbedsScripts ) {
const lazyEmbedParent = lazyEmbedScript.parentNode;
lazyEmbedScriptsByParents.set( lazyEmbedParent, lazyEmbedScript );
lazyEmbedObserver.observe( lazyEmbedParent );
}
</script>
<?php
}