5

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!

Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41

3 Answers3

5

Here is what I use :

static getHighestResImg(element) {
  if (element.getAttribute("srcset")) {
    return element
      .getAttribute("srcset")
      .split(",")
      .reduce(
        (acc, item) => {
          let [url, width] = item.trim().split(" ");
          width = parseInt(width);
          if (width > acc.width) return { width, url };
          return acc;
        },
        { width: 0, url: "" }
      ).url;
  }

  return element.getAttribute("src");
}
amirhe
  • 2,186
  • 1
  • 13
  • 27
Sarasranglt
  • 3,819
  • 4
  • 23
  • 36
2

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

Community
  • 1
  • 1
Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • Thanks a lot for this
    But sorry i didn't get the last line. What i understood is if my screen size matches with the `mediaQuery` then that's the one i need. And if i got it right then i'm afraid i don't need that but the LARGEST/BIGGEST image irrespective of the `mediaQuery`. How do i do that?
    – Tushar Shukla Apr 11 '16 at 08:25
  • You don't know _exact_ image size unless you download them all but you can **assume** that bigger image will be used for bigger screens (width...) then when you have a candidate size (from sizes attribute) you will pick the one with bigger width (from srcset attribute). For width descriptors those attributes work together (but sizes can be ignored for pixel density) – Adriano Repetti Apr 11 '16 at 08:28
  • Was wondering if you can help me [this question](http://stackoverflow.com/questions/36930597/how-pinterest-extension-store-temporarily-images-from-a-web-page-and-then-acce) as well...? – Tushar Shukla Apr 29 '16 at 11:16
  • Give a chance to DelightedD0D to post an answer but you may want to take a look to this other question: http://stackoverflow.com/q/4177803/1207195. Not _so_ easy but possible without extensions (unless it's OK for you to manage their deploy and to limit to Chrome) – Adriano Repetti Apr 29 '16 at 11:19
1

I needed to do the same for wordpress images generated and relevant srcset

Wordpress creates the srcset in a way that the smaller image comes first and then it keeps on increasing until it gets the biggest image in srcset.

Sample image code generated by wordpress:

<img width="300" height="221" src="http://www.example.com/wp-content/uploads/2017/01/Sharon-Ron-Chip-300x221.jpg" class="vc_single_image-img attachment-medium" alt="" srcset="http://www.example.com/wp-content/uploads/2017/01/Sharon-Ron-Chip-300x221.jpg 300w, http://www.example.com/wp-content/uploads/2017/01/Sharon-Ron-Chip-768x566.jpg 768w, http://www.example.com/wp-content/uploads/2017/01/Sharon-Ron-Chip-1024x755.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px">

also, in some cases when the image is too small, then wordpress does not create srcset attribute at all.

So, this is what i did: (CHANGE my_image_class_to_target in the code to your required target class)

jQuery('img.my_image_class_to_target').each(function () {
    var mysrcset = jQuery(this).attr('srcset');
    if (typeof mysrcset !== 'undefined'){ //checking if the srcset attribute is defined
        lastsrcset = mysrcset.split(',');
        lastsrc = lastsrcset[(lastsrcset.length - 1)];
        lastpuresrc = lastsrc.split(' ');
        mysrc = lastpuresrc[1];

    } else { //when srcset is not defined in case of small images
    var mysrcset = jQuery(this).attr('src');
        mysrc = mysrcset;
    }

    // display or use src of biggest image src
    console.log(mysrc);

});

I am new to Javascript/jQuery, so please forgive if i have taken a longer route. ;)

Any additions/ updates to code are welcome.

Rao Abid
  • 510
  • 4
  • 6