javascriptjqueryimageurlsrcset

how to determine highest resolution image from srcset


Say i have an image as

<img srcset="large.jpg 1400w, medium.jpg 700w, small.jpg 400w"
     sizes="(min-width: 700px) 700px, 100vw"
     alt="A woman reading">


Now this can also be in a format like

<img srcset="large.jpg 2x, medium.jpg 1.3x, small.jpg 0.8x"
     sizes="(min-width: 700px) 700px, 100vw"
     alt="A woman reading">


Also, (Not a valid HTML)

<img srcset="large.jpg 1400w, medium.jpg 1.3x, small.jpg 0.8x"
     sizes="(min-width: 700px) 700px, 100vw"
     alt="A woman reading">


Now how do i extract URL that has the highest resolution image.
I thought of splitting the srcset and then extracting the size but the problem is that sizes can be in 1200w format or in 2x format or in both of them and i don't know how to distinguish whether this is the 1200w format or 2x format and even if i do find out what's what, how do i compare them for determining the biggest size.

I wish i could show what i've tried but there is nothing coming to my head about how to do this!


Solution

  • Well, first of all your HTML code is invalid because you can not mix x and w descriptors for srcset. Pick one but not both.

    1) You can't know which image is bigger unless you download them all, of course you may - when supported server side - perform an HEAD request for each image and check Content-Length header property. It's not easy and what you get is just image size in bytes, not its dimensions but it's a good approximation. Roughly something like this:

    var sizes = [];
    
    // Peform this for each UR...
    $.ajax({
        type: "HEAD",
        url: imageUrl,
        success: function(data, textStatus, request) {
            sizes.push({
                url: imageUrl,
                size: parseInt(request.getResponseHeader("Content-Length"));
            });
        }
    });
    

    You can obtain URLs simply splitting srcset attribute and ignoring size part (I'm sure someone with better knowledge about regex can do something better):

    var urls = srcset.replace(/\s+[0-9]+(\.[0-9]+)?[wx]/g, "").split(/,/); 
    

    Calls may be done in parallel or - more easily IMO - sequentially using queues and $(document).queue() jQuery method.

    This can't be always done (maybe because server doesn't accept HEAD or image sizes are too similar because of compression and/or aspect ratios) then you have to parse srcset attribute.

    2) Let's first see a proof of concept (read as: it's not complete nor efficient) of what you may do for pixel density descriptor x: split with over white space .split(/ /) and then pick biggest one.

    // Read value from tag...this is just for testing
    var srcset = "small.jpg 0.8x, large.jpg 1.5x, medium.jpg 1.3x";
    
    var biggestImage = "";
    var highestPixelDensity = 1;
    
    for (var descriptor of srcset.split(/,/)) {
        var parts = descriptor.trim().split(/ /);
    
        // Here we check only for pixel density, see later for
        // width descriptor but code is straightforward
        var pixelDensity = parseFloat(parts[1].substring(-1));
        if (pixelDensity > highestPixelDensity) {
            biggestImage = parts[0];
            highestPixelDensity = pixelDensity;
        }
    }
    

    Things are different for width descriptor w because it relates to sizes then you first need to determine which one is applied. It's little bit more convoluted but it's easy to do with window.matchMedia() (again it's not complete code but an illustrative proof of concept):

    // Read this from tag...
    var sizes = "(min-width: 800px) 700px, (min-width: 700px) 300px";
    
    for (var descriptor of sizes.split(/,/)) {
        // Love this from https://stackoverflow.com/a/651646/1207195
        var imageSize = descriptor.split(/[, ]+/).pop();
    
        // You still need to handle last item special case (where there is not media query).
        var mediaQuery = descriptor.substring(-imageSize.length);
    
        // You can stop with first matched media query, you have your required imageSize
        var isMatch = window.matchMedia(mediaQuery).matches;
    }
    

    Now you have required size (check its unit and convert it appropriately, for example this post) and you're done to use it with width descriptor extracted from srcset.

    Note that you don't know which image is bigger unless you download it. What you can assume is that higher pixel density x or higher width descriptor w (but it must be calculated in relation to sizes attribute content, bigger screen uses bigger image) will give you the bigger one. Of course it's an assumption then it may fail in some cases. To determine which one must be used is simply:

    var isPixelDensity = srcset.trim().substr(-1) === "x";
    

    Just...put those samples together, handle missing corners cases, basic error handling and browser specific issues and you're done...