wp_get_loading_optimization_attributes() │ WP 6.3.0
Gets loading optimization attributes.
This function returns an array of attributes that should be merged into the given attributes array to optimize loading performance. Potential attributes returned by this function are:
loadingattribute with a value of "lazy"fetchpriorityattribute with a value of "high"decodingattribute with a value of "async"
If any of these attributes are already present in the given attributes, they will not be modified. Note that no element should have both loading="lazy" and fetchpriority="high", so the function will trigger a warning in case both attributes are present with those values.
Hooks from the function
Returns
Array. Loading optimization attributes.
Usage
wp_get_loading_optimization_attributes( $tag_name, $attr, $context );
- $tag_name(string) (required)
- The tag name.
- $attr(array) (required)
- Array of the attributes for the tag.
- $context(string) (required)
- Context for the element for which the loading optimization attribute is requested.
Notes
- Global. WP_Query.
$wp_queryWordPress Query object.
Changelog
| Since 6.3.0 | Introduced. |
| Since 7.0.0 | Support fetchpriority=low and fetchpriority=auto so that loading=lazy is not added and the media count is not increased. |
wp_get_loading_optimization_attributes() wp get loading optimization attributes code WP 7.0
function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
global $wp_query;
/**
* Filters whether to short-circuit loading optimization attributes.
*
* Returning an array from the filter will effectively short-circuit the loading of optimization attributes,
* returning that value instead.
*
* @since 6.4.0
*
* @param array|false $loading_attrs False by default, or array of loading optimization attributes to short-circuit.
* @param string $tag_name The tag name.
* @param array $attr Array of the attributes for the tag.
* @param string $context Context for the element for which the loading optimization attribute is requested.
*/
$loading_attrs = apply_filters( 'pre_wp_get_loading_optimization_attributes', false, $tag_name, $attr, $context );
if ( is_array( $loading_attrs ) ) {
return $loading_attrs;
}
$loading_attrs = array();
/*
* Skip lazy-loading for the overall block template, as it is handled more granularly.
* The skip is also applicable for `fetchpriority`.
*/
if ( 'template' === $context ) {
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}
// For now this function only supports images and iframes.
if ( 'img' !== $tag_name && 'iframe' !== $tag_name ) {
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}
/*
* Skip programmatically created images within content blobs as they need to be handled together with the other
* images within the post content or widget content.
* Without this clause, they would already be considered within their own context which skews the image count and
* can result in the first post content image being lazy-loaded or an image further down the page being marked as a
* high priority.
*/
if (
'the_content' !== $context && doing_filter( 'the_content' ) ||
'widget_text_content' !== $context && doing_filter( 'widget_text_content' ) ||
'widget_block_content' !== $context && doing_filter( 'widget_block_content' )
) {
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}
/*
* Add `decoding` with a value of "async" for every image unless it has a
* conflicting `decoding` attribute already present.
*/
if ( 'img' === $tag_name ) {
$loading_attrs['decoding'] = $attr['decoding'] ?? 'async';
}
// For any resources, width and height must be provided, to avoid layout shifts.
if ( ! isset( $attr['width'], $attr['height'] ) ) {
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}
/*
* The key function logic starts here.
*/
$maybe_in_viewport = null;
$increase_count = false;
$maybe_increase_count = false;
// Logic to handle a `loading` attribute that is already provided.
if ( isset( $attr['loading'] ) ) {
/*
* Interpret "lazy" as not in viewport. Any other value can be
* interpreted as in viewport (realistically only "eager" or `false`
* to force-omit the attribute are other potential values).
*/
if ( 'lazy' === $attr['loading'] ) {
$maybe_in_viewport = false;
} else {
$maybe_in_viewport = true;
}
}
// Logic to handle a `fetchpriority` attribute that is already provided.
$existing_fetchpriority = ( $attr['fetchpriority'] ?? null );
$is_low_fetchpriority = ( 'low' === $existing_fetchpriority );
if ( 'high' === $existing_fetchpriority ) {
/*
* If the image was already determined to not be in the viewport (e.g.
* from an already provided `loading` attribute), trigger a warning.
* Otherwise, the value can be interpreted as in viewport, since only
* the most important in-viewport image should have `fetchpriority` set
* to "high".
*/
if ( false === $maybe_in_viewport ) {
_doing_it_wrong(
__FUNCTION__,
__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
'6.3.0'
);
/*
* Set `fetchpriority` here for backward-compatibility as we should
* not override what a developer decided, even though it seems
* incorrect.
*/
$loading_attrs['fetchpriority'] = 'high';
} else {
$maybe_in_viewport = true;
}
} elseif ( $is_low_fetchpriority ) {
/*
* An IMG with fetchpriority=low is not initially displayed; it may be hidden in the Navigation Overlay,
* or it may be occluded in a non-initial carousel slide. Such images must not be lazy-loaded because the browser
* has no heuristic to know when to start loading them before the user needs to see them.
*/
$maybe_in_viewport = false;
// Preserve fetchpriority=low.
$loading_attrs['fetchpriority'] = 'low';
} elseif ( 'auto' === $existing_fetchpriority ) {
/*
* When a block's visibility support identifies that the block is conditionally displayed based on the viewport
* size, then it adds `fetchpriority=auto` to the block's IMG tags. These images must not be fetched with high
* priority because they could be erroneously loaded in viewports which do not even display them. Contrarily,
* they must not get `fetchpriority=low` because they may in fact be displayed in the current viewport. So as
* a signal to indicate that an IMG may be in the viewport, `fetchpriority=auto` is added. This has the effect
* here of preventing the media count from being increased, so that images hidden with block visibility do not
* affect whether a following IMG gets `loading=lazy`. In particular, `loading=lazy` should still be omitted
* on an IMG following any number of initial IMGs with `fetchpriority=auto` since those initial images may not
* be displayed.
*/
// Preserve fetchpriority=auto.
$loading_attrs['fetchpriority'] = 'auto';
}
if ( null === $maybe_in_viewport ) {
$header_enforced_contexts = array(
'template_part_' . WP_TEMPLATE_PART_AREA_HEADER => true,
'get_header_image_tag' => true,
);
/**
* Filters the header-specific contexts.
*
* @since 6.4.0
*
* @param array $default_header_enforced_contexts Map of contexts for which elements should be considered
* in the header of the page, as $context => $enabled
* pairs. The $enabled should always be true.
*/
$header_enforced_contexts = apply_filters( 'wp_loading_optimization_force_header_contexts', $header_enforced_contexts );
// Consider elements with these header-specific contexts to be in viewport.
if ( isset( $header_enforced_contexts[ $context ] ) ) {
$maybe_in_viewport = true;
$maybe_increase_count = true;
} elseif ( ! is_admin() && in_the_loop() && is_main_query() ) {
/*
* Get the content media count, since this is a main query
* content element. This is accomplished by "increasing"
* the count by zero, as the only way to get the count is
* to call this function.
* The actual count increase happens further below, based
* on the `$increase_count` flag set here.
*/
$content_media_count = wp_increase_content_media_count( 0 );
$increase_count = true;
// If the count so far is below the threshold, `loading` attribute is omitted.
if ( $content_media_count < wp_omit_loading_attr_threshold() ) {
$maybe_in_viewport = true;
} else {
$maybe_in_viewport = false;
}
} elseif (
// Only apply for main query but before the loop.
$wp_query->before_loop && $wp_query->is_main_query()
/*
* Any image before the loop, but after the header has started should not be lazy-loaded,
* except when the footer has already started which can happen when the current template
* does not include any loop.
*/
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
) {
$maybe_in_viewport = true;
$maybe_increase_count = true;
}
}
/*
* If the element is in the viewport (`true`), potentially add
* `fetchpriority` with a value of "high". Otherwise, i.e. if the element
* is not in the viewport (`false`) or it is unknown (`null`), add
* `loading` with a value of "lazy" if the element is not already being
* de-prioritized with `fetchpriority=low` due to occlusion in
* Navigation Overlay, non-initial carousel slides, or a collapsed Details block.
*/
if ( $maybe_in_viewport ) {
$loading_attrs = wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr );
} elseif ( ! $is_low_fetchpriority ) {
// Only add `loading="lazy"` if the feature is enabled.
if ( wp_lazy_loading_enabled( $tag_name, $context ) ) {
$loading_attrs['loading'] = 'lazy';
}
}
/*
* If flag was set based on contextual logic above, increase the content
* media count, either unconditionally, or based on whether the image size
* is larger than the threshold. This does not apply when the IMG has
* fetchpriority=auto because it may be conditionally displayed by viewport
* size.
*/
if ( 'auto' !== $existing_fetchpriority ) {
if ( $increase_count ) {
wp_increase_content_media_count();
} elseif ( $maybe_increase_count ) {
/** This filter is documented in wp-includes/media.php */
$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
wp_increase_content_media_count();
}
}
}
/**
* Filters the loading optimization attributes.
*
* @since 6.4.0
*
* @param array $loading_attrs The loading optimization attributes.
* @param string $tag_name The tag name.
* @param array $attr Array of the attributes for the tag.
* @param string $context Context for the element for which the loading optimization attribute is requested.
*/
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}