WordPress.org

Make WordPress Core

Changeset 48170


Ignore:
Timestamp:
06/25/2020 06:43:25 PM (5 months ago)
Author:
flixos90
Message:

Media: Ensure images have dimensions to reduce layout shift and facilitate lazy-loading.

This changeset ensures that attachment images which are inserted without width and height attributes still receive them in the frontend, to reduce cumulative layout shift. Adding the dimensions happens as part of the logic for adding srcset and sizes attributes, which already assume the specific width and height of the respective image.

Images are now only lazy-loaded if they have width and height attributes present. While missing these attributes itself is what causes layout shifts, lazy-loading such images can make this problem more apparent to the user.

Props adamsilverstein, westonruter.
Fixes #50367. See #44427.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/media.php

    r48110 r48170  
    14961496 *
    14971497 * @since 4.4.0
     1498 * @since 5.5.0 `width` and `height` are now added if not already present.
    14981499 *
    14991500 * @see wp_calculate_image_srcset()
     
    15251526        return $image;
    15261527    }
     1528
     1529    $attr = '';
    15271530
    15281531    $width  = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0;
     
    15481551            }
    15491552        }
    1550     }
    1551 
    1552     if ( ! $width || ! $height ) {
    1553         return $image;
     1553
     1554        if ( ! $width || ! $height ) {
     1555            return $image;
     1556        }
     1557
     1558        // Add width and height if not present.
     1559        $attr .= ' ' . trim( image_hwstring( $width, $height ) );
    15541560    }
    15551561
     
    15681574    if ( $srcset && $sizes ) {
    15691575        // Format the 'srcset' and 'sizes' string and escape attributes.
    1570         $attr = sprintf( ' srcset="%s"', esc_attr( $srcset ) );
     1576        $attr .= sprintf( ' srcset="%s"', esc_attr( $srcset ) );
    15711577
    15721578        if ( is_string( $sizes ) ) {
    15731579            $attr .= sprintf( ' sizes="%s"', esc_attr( $sizes ) );
    15741580        }
    1575 
    1576         // Add 'srcset' and 'sizes' attributes to the image markup.
    1577         $image = preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image );
    1578     }
    1579 
    1580     return $image;
     1581    }
     1582
     1583    if ( empty( $attr ) ) {
     1584        return $image;
     1585    }
     1586
     1587    // Add extra attributes to the image markup.
     1588    return preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image );
    15811589}
    15821590
     
    17131721        if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
    17141722            $value = 'lazy';
     1723        }
     1724
     1725        // Images should have dimension attributes for the `loading` attribute
     1726        // to be added.
     1727        if ( false === strpos( $image, ' width=' ) || false === strpos( $image, ' height=' ) ) {
     1728            return $image;
    17151729        }
    17161730
  • trunk/tests/phpunit/tests/media.php

    r48121 r48170  
    19641964    /**
    19651965     * @ticket 33641
     1966     * @ticket 50367
    19661967     */
    19671968    function test_wp_filter_content_tags() {
     
    19831984        $img_html5            = str_replace( ' />', '>', $img );
    19841985
     1986        $hwstring = image_hwstring( $size_array[0], $size_array[1] );
     1987
    19851988        // Manually add srcset and sizes to the markup from get_image_tag().
    19861989        $respimg                  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img );
    19871990        $respimg_no_size_in_class = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size_in_class );
    1988         $respimg_no_width_height  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_width_height );
     1991        $respimg_no_width_height  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $hwstring . $srcset . ' ' . $sizes . ' />', $img_no_width_height );
    19891992        $respimg_with_sizes_attr  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' />', $img_with_sizes_attr );
    19901993        $respimg_xhtml            = preg_replace( '|<img ([^>]+)/>|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_xhtml );
     
    19982001            %2$s
    19992002
    2000             <p>Image, no width and height attributes. Should have srcset and sizes (from matching the file name).</p>
     2003            <p>Image, no width and height attributes. Should have width, height, srcset and sizes (from matching the file name).</p>
    20012004            %3$s
    20022005
     
    25312534    /**
    25322535     * @ticket 44427
     2536     * @ticket 50367
    25332537     */
    25342538    function test_wp_lazy_load_content_media() {
    2535         $img       = get_image_tag( self::$large_id, '', '', '', 'medium' );
    2536         $img_xhtml = str_replace( ' />', '/>', $img );
    2537         $img_html5 = str_replace( ' />', '>', $img );
    2538         $iframe    = '<iframe src="https://www.example.com"></iframe>';
     2539        $image_meta = wp_get_attachment_metadata( self::$large_id );
     2540        $size_array = $this->_get_image_size_array_from_meta( $image_meta, 'medium' );
     2541
     2542        $img                 = get_image_tag( self::$large_id, '', '', '', 'medium' );
     2543        $img_xhtml           = str_replace( ' />', '/>', $img );
     2544        $img_html5           = str_replace( ' />', '>', $img );
     2545        $img_no_width_height = str_replace( ' width="' . $size_array[0] . '"', '', $img );
     2546        $img_no_width_height = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height );
     2547        $iframe              = '<iframe src="https://www.example.com"></iframe>';
    25392548
    25402549        $lazy_img       = wp_img_tag_add_loading_attr( $img, 'test' );
     
    25522561            <p>Image, HTML 5.0 style.</p>
    25532562            %3$s
    2554             <p>Image, with pre-existing "loading" attribute.</p>
     2563            <p>Image, with pre-existing "loading" attribute. Should not be modified.</p>
     2564            %4$s
     2565            <p>Image, without dimension attributes. Should not be modified.</p>
    25552566            %5$s
    25562567            <p>Iframe, standard. Should not be modified.</p>
    2557             %4$s';
    2558 
    2559         $content_unfiltered = sprintf( $content, $img, $img_xhtml, $img_html5, $iframe, $img_eager );
    2560         $content_filtered   = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $iframe, $img_eager );
     2568            %6$s';
     2569
     2570        $content_unfiltered = sprintf( $content, $img, $img_xhtml, $img_html5, $img_eager, $img_no_width_height, $iframe );
     2571        $content_filtered   = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $img_eager, $img_no_width_height, $iframe );
    25612572
    25622573        // Do not add srcset and sizes.
Note: See TracChangeset for help on using the changeset viewer.