wp_get_loading_optimization_attributes( string $tag_name, array $attr, string $context ): array

Gets loading optimization attributes.


Description

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:

  • loading attribute with a value of "lazy"
  • fetchpriority attribute with a value of "high"

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.


Top ↑

Parameters

$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.

Top ↑

Return

array Loading optimization attributes.


Top ↑

Source

File: wp-includes/media.php. View all references

function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
	global $wp_query;

	/*
	 * Closure for postprocessing logic.
	 * It is here to avoid duplicate logic in many places below, without having
	 * to introduce a very specific private global function.
	 */
	$postprocess = static function( $loading_attributes, $with_fetchpriority = false ) use ( $tag_name, $attr, $context ) {
		// Potentially add `fetchpriority="high"`.
		if ( $with_fetchpriority ) {
			$loading_attributes = wp_maybe_add_fetchpriority_high_attr( $loading_attributes, $tag_name, $attr );
		}
		// Potentially strip `loading="lazy"` if the feature is disabled.
		if ( isset( $loading_attributes['loading'] ) && ! wp_lazy_loading_enabled( $tag_name, $context ) ) {
			unset( $loading_attributes['loading'] );
		}
		return $loading_attributes;
	};
	// Closure to increase media count for images with certain minimum threshold, mostly used for header images.
	$maybe_increase_content_media_count = static function() use ( $attr ) {
		/** This filter is documented in wp-admin/includes/media.php */
		$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
		// Images with a certain minimum size in the header of the page are also counted towards the threshold.
		if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
			wp_increase_content_media_count();
		}
	};

	$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 ) {
		return $loading_attrs;
	}

	// For now this function only supports images and iframes.
	if ( 'img' !== $tag_name && 'iframe' !== $tag_name ) {
		return $loading_attrs;
	}

	// For any resources, width and height must be provided, to avoid layout shifts.
	if ( ! isset( $attr['width'], $attr['height'] ) ) {
		return $loading_attrs;
	}

	if ( isset( $attr['loading'] ) ) {
		/*
		 * While any `loading` value could be set in `$loading_attrs`, for
		 * consistency we only do it for `loading="lazy"` since that is the
		 * only possible value that WordPress core would apply on its own.
		 */
		if ( 'lazy' === $attr['loading'] ) {
			$loading_attrs['loading'] = 'lazy';
			if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
				_doing_it_wrong(
					__FUNCTION__,
					__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
					'6.3.0'
				);
			}
		}

		return $postprocess( $loading_attrs, true );
	}

	// An image with `fetchpriority="high"` cannot be assigned `loading="lazy"` at the same time.
	if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
		return $postprocess( $loading_attrs, true );
	}

	/*
	 * Do not lazy-load images in the header block template part, as they are likely above the fold.
	 * For classic themes, this is handled in the condition below using the 'get_header' action.
	 */
	$header_area = WP_TEMPLATE_PART_AREA_HEADER;
	if ( "template_part_{$header_area}" === $context ) {
		// Increase media count if there are images in header above a certian minimum size threshold.
		$maybe_increase_content_media_count();
		return $postprocess( $loading_attrs, true );
	}

	// The custom header image is always expected to be in the header.
	if ( 'get_header_image_tag' === $context ) {
		// Increase media count if there are images in header above a certian minimum size threshold.
		$maybe_increase_content_media_count();
		return $postprocess( $loading_attrs, true );
	}

	// Special handling for programmatically created image tags.
	if ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context || 'widget_media_image' === $context ) {
		/*
		 * Skip programmatically created images within post content as they need to be handled together with the other
		 * images within the post content.
		 * Without this clause, they would already be considered below 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 ( doing_filter( 'the_content' ) ) {
			return $loading_attrs;
		}

		// Conditionally skip lazy-loading on images before the loop.
		if (
			// 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' )
		) {
			// Increase media count if there are images in header above a certian minimum size threshold.
			$maybe_increase_content_media_count();
			return $postprocess( $loading_attrs, true );
		}
	}

	/*
	 * The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
	 * as they are likely above the fold. Shortcodes are processed after content images, so if
	 * thresholds haven't already been met, apply the same logic to those as well.
	 */
	if ( 'the_content' === $context || 'the_post_thumbnail' === $context || 'do_shortcode' === $context ) {
		// Only elements within the main query loop have special handling.
		if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
			$loading_attrs['loading'] = 'lazy';
			return $postprocess( $loading_attrs, false );
		}

		// Increase the counter since this is a main query content element.
		$content_media_count = wp_increase_content_media_count();

		// If the count so far is below the threshold, `loading` attribute is omitted.
		if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
			// The first largest image will still get `fetchpriority='high'`.
			return $postprocess( $loading_attrs, true );
		}
	}

	// Lazy-load by default for any unknown context.
	$loading_attrs['loading'] = 'lazy';
	return $postprocess( $loading_attrs, false );
}

Top ↑

Hooks



Top ↑

Changelog

Changelog
Version Description
6.3.0 Introduced.

Top ↑

User Contributed Notes

You must log in before being able to contribute a note or feedback.