Why use Srcset?

Episerver CMS 11
Image Processor 5.8

Over the last couple of years, usage of srcset has picked up traction with clients as an easy way to implement responsive images. You can empower the requesting browser to select an image from a list you specify. This example contains two images the browser can pick between depending on if the viewport has hit at least 480px or 800px.

<img srcset="ImageOne.jpg 480w,
             ImageTwo.jpg 800w"
     sizes="auto"
     src="ImageFallBack.jpg">

Using Image Processor we can optimize the editor experience. Optimizely editors can upload a single image but specify different widths which is generated on the fly.

<img srcset="ImageExample.jpg?width=480 480w,
             ImageExample.jpg?width=800 800w"
     sizes="auto"
     src="ImageFallBack.jpg">

Srcset via Display Template

Use this markup to render images so Optimizely can do it through a display template @Html.PropertyFor(x => x.Image) This code goes into your Shared\DisplayTemplates folder.


var imgUrl = @Url.ContentUrl(Model);
<img srcset="@imgUrl?width=480 480w, @imgUrl?width=800 800w"
sizes="auto"
src="@imgUrl">

Srcset via Extension Method

Call this extension method on each url in your razor template


    public static string SrcSet(this string imageUrl)
    {
        return string.IsNullOrEmpty(imageUrl) ?
            imageUrl :
            $"{imageUrl}?width=320 320w, " +
            $"{imageUrl}?width=640 640w, " +
            $"{imageUrl}?width=780 780w, " +
            $"{imageUrl}?width=960 960w, " +
            $"{imageUrl}?width=1280 1280w, " +
            $"{imageUrl}?width=1440 1440w, " +
            $"{imageUrl}?width=1600 1600w, " +
            $"{imageUrl}?width=1920 1920w";
        }
    }

Srcset via TinyMCE xhtmlstring

Create a TinyMCE plugin and then register it. Credit to Ynze for pointing me in the right direction in this blog post.

edit 14/03/21: Modified the example markup to now support images being dragged in from assets pane or into the browser from an external source via drag and drop.

"use strict";
var tinymce = tinymce || {};

tinymce.PluginManager.add('addsrcset', function (editor, url) {
editor.on('NodeChange',
function (e) {
if (e.element.tagName.toLowerCase() === "p") {
var imgElements = e.element.getElementsByTagName("img");
if (imgElements !== undefined) {
Array.from(imgElements).forEach(img => {
var src = img.attributes.getNamedItem("src").value;
var srcFriendlyUrl = src.replace("/EPiServer/CMS/Content", "").split(",,")[0];
var srcset = getSrcSetMarkup(srcFriendlyUrl);
img.setAttribute("srcset", srcset);
img.setAttribute("sizes", "auto");
img.setAttribute("width", "100%");
img.setAttribute("height", "");
});
}
}
});

function getSrcSetMarkup(src) {
return src + '?width=320 320w, ' +
src + '?width=640 640w, ' +
src + '?width=780 780w, ' +
src + '?width=1280 1280w, ' +
src + '?width=1440 1440w, ' +
src + '?width=1600 1600w, ' +
src + '?width=1920 1920w';
}

return {
getMetadata: function () {
return {
name: 'Add SrcSet to Img',
url: 'https://www.hiddenfoundry.com'
};
}
};
});

Disable Automatic Upscaling

Image Processor will resize an image using the width you specify. If you have an image where the width is smaller than the requested width then it will be upscaled.You can disable this default behaviour by using the following initialization module.


    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ImageProcessorInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            ImageProcessingModule.ValidatingRequest += DisableUpscaling;
        }

        public void Uninitialize(InitializationEngine context)
        {
            ImageProcessingModule.ValidatingRequest -= DisableUpscaling;
        }

        private void DisableUpscaling(object sender, ValidatingRequestEventArgs args)
        {
            if (string.IsNullOrWhiteSpace(args.QueryString))
            {
                return;
            }

            var queryCollection = HttpUtility.ParseQueryString(args.QueryString);
            queryCollection.Set("upscale", "false");
            args.QueryString = queryCollection.ToString();
        }
    }