Controlling Responsive Images in WordPress: Breaking Down the Code

Controlling Responsive Images in WordPress: Breaking Down the Code

When you add an image to a WordPress post or page, whether as a featured image or one in the content, the CMS outputs markup for responsive images. This means WordPress generates a series of different sized versions of the image, and the actual image file served up will be the one most suited for the browser width and screen resolution of the visitor's device. Responsive images is a significant leap forward in web technologies, and the automation provided by WordPress makes this rather complex standard as easy as just uploading an image and letting the application do the rest.

The Problem

For the browser to be able to pick the most appropriate image size, it needs to know how big the area the image is being displayed in really is. And this size is defined by the current active theme, not WordPress itself. As a result, unless the theme clearly states how big the display area for an image is, WordPress will safe it by setting the default width of the image to the width of the image file itself. This in turn means if you upload a large image, say 2600px wide, WordPress will say the maximum displayed width of the image is 2600px. This is rarely true, and as a result the good intentions of responsive images are being somewhat squandered, particularly on wider displays.

The Solution

The sizes attribute was created specifically to deal with this issue. Using sizes in conjunction with srcset allows us to use simple media queries to tell the browser how wide the display area for an image is before the actual page layout has been painted, and the browser can pick the right size image file to download. The challenge is to get WordPress to provide the correct sizes attribute for the current theme.

The good news is we can build custom sizes attributes for both featured images and regular displayed content into our themes, and make them conditional on whatever crazy layout configurations we can think of.

The good people who developed the Twenty Sixteen theme put together just such a solution, and I had the privilege of picking it apart and reworking it for the upcoming Twenty Seventeen theme.

While it is fresh in my mind, I want to break down the original code for you so you can see how it works and how you can implement the same feature in your own themes (and plugins).

The Code

Here's what you'll find in Twenty Sixteen's functions.php file. There are two functions at play. First the function for general images displayed in posts and pages, aka "content images":

/**
 * Add custom image sizes attribute to enhance responsive image functionality
 * for content images
 *
 * @since Twenty Sixteen 1.0
 *
 * @param string $sizes A source size value for use in a 'sizes' attribute.
 * @param array  $size  Image size. Accepts an array of width and height
 *                      values in pixels (in that order).
 * @return string A source size value for use in a content image 'sizes' attribute.
 */
function twentysixteen_content_image_sizes_attr( $sizes, $size ) {
	$width = $size[0];

	840 <= $width && $sizes = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px';

	if ( 'page' === get_post_type() ) {
		840 > $width && $sizes = '(max-width: ' . $width . 'px) 85vw, ' . $width . 'px';
	} else {
		840 > $width && 600 <= $width && $sizes = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px';
		600 > $width && $sizes = '(max-width: ' . $width . 'px) 85vw, ' . $width . 'px';
	}

	return$sizes;
}
add_filter( 'wp_calculate_image_sizes', 'twentysixteen_content_image_sizes_attr', 10 , 2 );

Second, a function targeting only featured images:

/**
 * Add custom image sizes attribute to enhance responsive image functionality
 * for post thumbnails
 *
 * @since Twenty Sixteen 1.0
 *
 * @param array $attr Attributes for the image markup.
 * @param int   $attachment Image attachment ID.
 * @param array $size Registered image size or flat array of height and width dimensions.
 * @return string A source size value for use in a post thumbnail 'sizes' attribute.
 */
function twentysixteen_post_thumbnail_sizes_attr( $attr, $attachment, $size ) {
	if ( 'post-thumbnail' === $size ) {
		is_active_sidebar( 'sidebar-1' ) && $attr['sizes'] = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 60vw, (max-width: 1362px) 62vw, 840px';
		! is_active_sidebar( 'sidebar-1' ) && $attr['sizes'] = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 88vw, 1200px';
	}
	return$attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'twentysixteen_post_thumbnail_sizes_attr', 10 , 3 );

Featured Images

Let's look at the second one first since it's more straight forward.

We start off with a function that grabs a bunch of attributes ($attr, $attachment, $size) from the Featured Image. This function runs as a callback to the wp_get_attachment_image_attributes hook which we use to filter the output of the featured image markup. So in plain english, we get all the attributes for the Featured Image of the current post or page, then use a custom function to alter these attributes before returning them to WordPress for display on the front end:

functiontwentysixteen_post_thumbnail_sizes_attr( $attr, $attachment, $size ){
	// do stuff to the attributes
}
add_filter( 'wp_get_attachment_image_attributes', 'twentysixteen_post_thumbnail_sizes_attr', 10 , 3 );

Inside the function, a conditional statement is used to target only a specific image size, in this case the post-thumbnail size (I tested for different conditions in Twenty Seventeen):

if ( 'post-thumbnail' === $size ) {
	// happens only if the condition is met
}

And finally we alter the sizes attribute based on the current layout (I've broken this down a bit to make it easier to read):

is_active_sidebar( 'sidebar-1' ) && $attr['sizes'] = '
	?(max-width: 709px) 85vw, 
	?(max-width: 909px) 67vw, 
	?(max-width: 984px) 60vw, 
	?(max-width: 1362px) 62vw, 
	?840px';
! is_active_sidebar( 'sidebar-1' ) && $attr['sizes'] = '
?	(max-width: 709px) 85vw, 
	?(max-width: 909px) 67vw, 
	?(max-width: 1362px) 88vw, 
	?1200px';

There are two conditions here, the first if a sidebar is present, the second if there is no sidebar. In each case the sizes attribute is updated to fit the specified list which reads something like this (for the first condition):

  • If the viewport width is 709px or lower, the width of the area the image will be displayed in will be 85% of the total viewport width.
  • If the viewport width is over 709px but at or lower than 909px, the width of the area the image will be displayed in will be 67% of the total viewport width.
  • Etc

The last property, which only lists a width, is the fallback that stipulates the maximum width the image will ever be displayed as regardless of the width of the viewport.

So, if the current post has a sidebar and the viewport width is 2000px, the browser will still only download an image that is either 1x, 2x, or 3x of the displayed width of 840px.

To wrap things up, the $attr attribute is returned to the callback and applied to the output of the featured image:

return$attr;

Pretty cool.

Content Images

The content image function is a bit more complex, but contains many of the same elements. To refresh your memory it looks like this:

/**
 * Add custom image sizes attribute to enhance responsive image functionality
 * for content images
 *
 * @since Twenty Sixteen 1.0
 *
 * @param string $sizes A source size value for use in a 'sizes' attribute.
 * @param array  $size  Image size. Accepts an array of width and height
 *                      values in pixels (in that order).
 * @return string A source size value for use in a content image 'sizes' attribute.
 */
function twentysixteen_content_image_sizes_attr( $sizes, $size ) {
	$width = $size[0];

	840 <= $width && $sizes = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px';

	if ( 'page' === get_post_type() ) {
		840 > $width && $sizes = '(max-width: ' . $width . 'px) 85vw, ' . $width . 'px';
	} else {
		840 > $width && 600 <= $width && $sizes = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px';
		600 > $width && $sizes = '(max-width: ' . $width . 'px) 85vw, ' . $width . 'px';
	}

	return$sizes;
}
add_filter( 'wp_calculate_image_sizes', 'twentysixteen_content_image_sizes_attr', 10 , 2 );

Like before, we start with getting a hook, this time wp_calculate_image_sizes which calculates the sizes attribute for images, and filtering its output with a custom function:

functiontwentysixteen_content_image_sizes_attr( $sizes, $size ){
	// do stuff to the attributes
}
add_filter( 'wp_calculate_image_sizes', 'twentysixteen_content_image_sizes_attr', 10 , 2 );

Inside the function, a bunch of things happen:

First, we populate grab the $size property which holds an array containing the width and height of the image, and set the $width to the first array item (the image width):

$width = $size[0];

Next we use a bit of a convoluted PHP logic chain to check for a condition and set the $sizes attribute accordingly:

840 <= $width && $sizes = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px';

In plain english this says "If $width is larger than or equal to 840, set $sizes to the new custom string on the right. Computer logic markup can get pretty absurd.

The next several lines are variations on this theme, wrapped in different conditionals and providing various conditions of their own:

if ( 'page' === get_post_type() ) {
	840 > $width && $sizes = '(max-width: ' . $width . 'px) 85vw, ' . $width . 'px';
} else {
	840 > $width && 600 <= $width && $sizes = '(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px';
	600 > $width && $sizes = '(max-width: ' . $width . 'px) 85vw, ' . $width . 'px';
}

And again, to wrap things up, the $sizes attribute is returned to the callback to be applied to all images displayed in the post or page:

return$sizes;

So why all these conditions? Well, different templates and layouts put different limits on the total displayed width of content images and these conditions account for all of them.

The conditions will look different depending on the theme and its layout. In Twenty Seventeen they look like this:

740 <= $width && $sizes = '(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px';

if ( is_active_sidebar( 'sidebar-1' ) || is_archive() || is_search() || is_home() || is_page() ) {
	if ( ! ( is_page() && 'one-column' === get_theme_mod( 'page_options' ) ) ) {
		767 <= $width && $sizes = '(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px';
	}
}

Enhance the Responsive Images in Your Themes

Like with everything else WordPress, you can rely on the default functionality or enhance it to get the most out of the application. Spending the time to customize the sizes attributes for the images displayed in your theme will drastically improve the performance of your site and reduce server load because the images loaded will fit the displayed size, not the total width of the viewport.

In the upcoming 2016/17 edition of my popular LinkedIn Learning and Lynda.com course WordPress: Building Themes from Scratch Using Underscores I will go into great detail on how to figure out all the breakpoints and conditionals necessary to get this to work. In the meantime I hope this detailed code breakdown will help you on your way.



要查看或添加评论,请登录

Morten Rand-Hendriksen的更多文章

  • After WordPress

    After WordPress

    Today, the head of the WordPress Open Source Project Matt Mullenweg unilaterally locked the gates to wordpress.org, the…

    60 条评论
  • As the Mask Drops, It's Time to Face the Politics of Tech

    As the Mask Drops, It's Time to Face the Politics of Tech

    "Is it really?" She gestured at my hoodie and the bold text across my chest reading "Code is Political." "Profoundly…

    22 条评论
  • Rubicon

    Rubicon

    On Saturday October 12, 2024, a line was crossed in the WordPress open source project that I fear will have a lasting…

    24 条评论
  • As We Break Surface – The AI Transmutation of Web Dev

    As We Break Surface – The AI Transmutation of Web Dev

    "Hey AI, build me a website." It's a matter of time - probably months, before we get here.

    10 条评论
  • It’s time to abandon reckless oil propagandists

    It’s time to abandon reckless oil propagandists

    A response to Dan McTeague’s Financial Post opinion piece “It’s time to abandon reckless EV mandates” published July…

    13 条评论
  • AI Training and the Slow Poison of Opt-Out

    AI Training and the Slow Poison of Opt-Out

    Asking users to opt-out of AI training is a deceptive pattern. Governments and regulators must step in to enforce…

    7 条评论
  • GPT-4o, OpenAI, and Our Multimodal Future

    GPT-4o, OpenAI, and Our Multimodal Future

    OpenAI held up a big shining arrow pointing towards our possible future with AI and asked us to follow them. Beyond the…

    12 条评论
  • Ten Questions for Matt Mullenweg Re: Data Ownership and AI

    Ten Questions for Matt Mullenweg Re: Data Ownership and AI

    Dear Matt. 404 Media tells me you're in the process of selling access to the data I've published on WordPress.

    11 条评论
  • AI Coding Assistants Made Me Go Back to School

    AI Coding Assistants Made Me Go Back to School

    The introduction of graphing calculators didn't remove the need to understand math; they removed the need to do rote…

    13 条评论
  • The Challenge, and Opportunity, of OpenAI's GPT Store

    The Challenge, and Opportunity, of OpenAI's GPT Store

    If you make a useful GPT in the GPT Store, someone else will publish a more popular copy and make yours look like a…

    8 条评论

社区洞察

其他会员也浏览了