Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add srcset support #175

Closed
hatsumatsu opened this issue Jan 14, 2015 · 22 comments
Closed

Add srcset support #175

hatsumatsu opened this issue Jan 14, 2015 · 22 comments

Comments

@hatsumatsu
Copy link

Using the recommended markup (http://www.filamentgroup.com/lab/to-picturefill.html) for <img> with srcset while using picturefill seems to break imagesLoaded on browsers with native support for srcset. Let's say I have an image like

<img srcset="image-400.jpg 400w,
             image-800.jpg 800w,
             image-1600.jpg 1600w"
>

We don't use src here to avoid loading two images.
Then we use use picturefill for browsers that don't support srcset natively.

In both browsers with and without native support (thanks to picturefill) the right image is loaded, but imagesLoaded reports the image broken on browsers with native support (resulting in not being able to fade the image in smoothly when loaded)

Check out this reduced test case on latest Firefox (v.35: working) and Chrome (v.40: broken):
http://codepen.io/superstructure-net/pen/EaWvZv/

Any ideas? Thx!

@hatsumatsu
Copy link
Author

Really no one's having trouble with responsive images + picturefill + imagesLoaded?
Looking into imagesLoaded's code I wonder if it's ready for responsive images at all.

Since in
https://github.com/desandro/imagesloaded/blob/master/imagesloaded.js#L293
the script checks for the image's src it will only detect the loading progress of the fallback image (if specified), right? That would explain why it is failing when no src / fallback image is specified. On browsers without native srcset support (Firefox v35) picturefill manually sets the src before imagesLoaded kicks in, that's why it's working in these browsers.

In the case described (srcset defined but no src) we could wait for the image's currentSrc attribute to be set (by picturefill) if srcset is defined and then continue as usual.

@spoeken
Copy link

spoeken commented Jan 26, 2015

+1

@pawelgur
Copy link

pawelgur commented Feb 1, 2015

+1

@theamnesic
Copy link

+1 !
#166 talk about currentSrc too.

@hatsumatsu
Copy link
Author

There is a related topic in the picturefill repo: scottjehl/picturefill#401
In this thread @aFarkas posted a replacement for ìmagesLoaded` that looks promising:

https://gist.github.com/aFarkas/15de1cea3acd6602a71e

@hatsumatsu
Copy link
Author

Might be a little off-topic, but I ended up approaching the issue from a different perspective and writing jquery.imagePlaceholder. In short, instead of waiting for the images to be loaded I add empty <canvas> elements with the same width and height attributes as the original images to the container elements. By doing so scripts like masonry can get the container's dimensions immediately for calculating the layout... Check out the demo, feedback appreciated.

@aFarkas
Copy link

aFarkas commented Mar 27, 2015

While this issue should be solved. I think in most cases the problems that are solved by imagesloaded should be solved in a different way.

In general imagesLoaded is mostly used to read and write layout after all images inside a specific widget (slider, masonry or what ever) are loaded.

But this has some problems:

  1. There is currently a draft introducing a new attribute called lazyload for different elements including images (and lazyloading via JS is a pretty common technique)
  2. responsive images in form of art directed images using the picture element, do never have a final complete state
  3. Waiting until everything is loaded until doing layout is pretty bad for perceived performance, especially if we have a slow connection

So how should we deal with this in a future proof way?

  1. Developers using a slider/masonry
    Devs who are using such a script, should make sure, that the dimension of images are already defined via content attributes or CSS. In case of responsive images devs can use the CSS intrinsic ratio pattern to provide the dimensions. This does not only solve the problem. It also minimizes reflows.
  2. Developers developing a slider/masonry
    Often developers who create such a script, want to be able to give something robust and convenient, that also works, if the markup/CSS isn't done perfect. Therefore it is no option to rely, that the image dimensions are calculable via CSS. But instead of waiting for all images to be loaded to react on, there is a simple pattern to progressively update the layout after an image has loaded.

An example for masonry used as a plugin would look something like this:

$('.masonry-container').each(function(){
    var $masonry = $(this);
    var update = function(){
        $masonry.masonry();
    };
    update();
    this.addEventListener('load', update, true);
});

Most such scripts already bind global events to window.resize to progressively update layout on resize, but I really don't understand, why they don't already bind events to get informed if a children image has changed (and instead wait for all images to be loaded).

@hatsumatsu
Copy link
Author

@aFarkas: I agree on that, especially that it's bad UX to wait (without visual feedback) for all images in a given layout to be loaded until doing the layout math. But regarding your post I was wondering:

make sure, that the dimension of images are already defined via content attributes or CSS

How to programmatically achieve this in case of art directed responsive images using <picture> where the aspect ratio can be different on every <source>? Afaik there are no dimension attributes on <source> elements neither is there a fallback source with an intrinsic aspect ratio as there is in <img srcset="" src="" sizes="">. I can only think of putting the <source>'s dimensions in data-attributes, using JS to figure out what source is chosen by the browser, then calculating the image's actual dimensions in the layout and finally applying these via inline styles.

this.addEventListener('load', update, true);

I must admit that I've never seen this pattern using the load event on generic DOM elements to check for child images to be loaded? And wasn' t imagesLoaded initial goal to overcome the browser implementation problems of the load event applied to images?

Despite most demos imagesLoaded can of course be used differently than waiting for all images in a given layout to be loaded. F.e. wait for each image separately to be loaded and pass these elements one by one to layout algorithms like masonry.

btw, your gist is doing a pretty good job for this use case :)

@aFarkas
Copy link

aFarkas commented Mar 28, 2015

How to programmatically achieve this in case of art directed responsive images using <picture> where the aspect ratio can be different on every <source>?

By extending the normal intrinsic ratio pattern with media queries:

<style>
.ratio-container {
    position: relative;
}
.ratio-container:after {
    content: '';
    display: block;
    height: 0;
    width: 100%;
    content: "";
}
.ratio-container > * {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: block;
}

.ratio-container:after {
    /* 600 / 14 */
    padding-bottom: 42.857%;
}

@media (max-width: 1050px) {
    .ratio-container:after {
        /* 600 / 9 */
        padding-bottom: 66.666%;
    }
}

@media (max-width: 500px) {
    .ratio-container:after {
        /* 40000 / 525 */
        padding-bottom: 76.19%;
    }
}
</style>

<picture class="intrinsic-ratio">
    <!--[if IE 9]><video style="display: none;"><![endif]-->
    <source
            srcset="http://placehold.it/525x400"
            media="(max-width: 500px)" />
    <source
            srcset="http://placehold.it/900x600/e8117f/fff"
            media="(max-width: 1050px)" />
    <source
        srcset="http://placehold.it/1400x600/117fe8/fff" />
    <!--[if IE 9]></video><![endif]-->
    <img
        src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
        alt="image with artdirection" />
</picture>

Afaik there are no dimension attributes on elements neither is there a fallback source with an intrinsic aspect ratio as there is in <img srcset="" src="" sizes="">.

Sad story, this still needs to be standardized, but is put on hold until a CSS specification for aspect ratio is ready.

I can only think of putting the <source>'s dimensions in data-attributes, using JS to figure out what source is chosen by the browser, then calculating the image's actual dimensions in the layout and finally applying these via inline styles.

Yep, exactly. here is the script.

I must admit that I've never seen this pattern using the load event on generic DOM elements to check for child images to be loaded?

I would assume, because the combination of event capturing and event delegation isn't well known (and isn't even possible with jQuery/IE8-).

And wasn' t imagesLoaded initial goal to overcome the browser implementation problems of the load event applied to images?

The load event in browsers does work just fine for our use case. The main problem is to detect wheter an image was already loaded, in cache or failed before the script is run (This problem is worked around by imagesLoaded. imagesLoaded is more about complete than load). The "load-update pattern" above doesn't need to know, whether an image was already loaded. It calculates layout immediately (without knowledge) and simply updates layout as soon as the load event is fired.

Also note, that most bugs are already fixed and the most annoying one was just fixed.

In the very near future you will be able to do this:

var img = document.querySelector('img');
var onComplete = function(){
    //do it
};
if(img.complete){
    onComplete();
}
img.addEventListener('load', onComplete);
img.addEventListener('error', onComplete);

@hatsumatsu
Copy link
Author

Thanks for clarifying, great work! Looking forward to the day these things are not only spec'd but have broader browser support, too...

PS: I think there is a typo in your last code snippet, shouldn't onLoadeded(); be onComplete()?

@aFarkas
Copy link

aFarkas commented Mar 30, 2015

@hatsumatsu
Thx, fixed.

@tiagogon
Copy link

+1.. imagesloaded also doesn't work here with "srcset"

@tiagogon
Copy link

oh, just used a ratio-container, and fixed my issues with masonry ;) thank you @aFarkas ! your posts here helped me a lot! <3

@desandro desandro changed the title images without "src" but "srcset" + picturefill + native support = reported broken Add srcset support Feb 18, 2016
@toptalo
Copy link

toptalo commented Mar 2, 2016

+1

2 similar comments
@sphism
Copy link

sphism commented Jul 6, 2016

+1

@Maybach91
Copy link

+1

@terryupton
Copy link

+1

@stabilimenta
Copy link

stabilimenta commented Mar 30, 2017

Has there been any progress to getting imagesLoaded to work with srcset? This thread is over two years old. I'm having a lot of problems getting my Masonry+ImagesLoaded+InfiniteScroll to play nicely on Safari with retina displays (both iOS and Mac). I'm getting a lot of overlapping.
https://evolutionarygraphics.com/asheville-web-design/

Does anyone have an interim solution to this? An alternative script besides imagesLoaded?
It works so nicely on most browsers, but retina support is important for me, so this is a big problem, to an otherwise awesome set of scripts.

Sample of my masonry item's html:

<div class="item book-item even item-8">
    <div class="primary-cat">Book</div>
    <a class="box-link" href="https://evolutionarygraphics.com/asheville-web-design/asheville-book-design/" title="View Details About This Book Design Project">
        <div class="photo">
            <img 
                width="356" 
                height="292" 
                src="https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-356x292.jpg" 
                class="attachment-medium_nocrop size-medium_nocrop" 
                alt="Remembering Asheville Cover" 
                sizes="
                    (min-width: 1130px) 299px, 
                    (min-width: 769px) calc(.88 * ((.31 * (100vw - 30px)) - 2px)), 
                    (min-width: 461px) calc(.88 * ((100vw - 30px) - 2px)), 93vw
                " 
                srcset="
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-356x292.jpg 356w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-150x122.jpg 150w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-260x213.jpg 260w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-390x319.jpg 390w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-850x696.jpg 850w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-175x143.jpg 175w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-550x450.jpg 550w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-1100x901.jpg 1100w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-843x691.jpg 843w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-684x561.jpg 684w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-227x186.jpg 227w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover-624x511.jpg 624w, 
                    https://evolutionarygraphics.com/wp/wp-content/uploads/2016/12/Remembering-Asheville-cover.jpg 1120w
            " />
        </div>
    
        <div class="text">
            <div class="item-title">Remembering Asheville book</div>
            <div class="description">I designed and produced this history of Asheville for an author who was self-publishing. It&hellip;</div>
            <div class="link read-more">View Project</div><!-- ./read-more -->
        </div><!-- ./text -->
    </a><!-- /.box-link -->
</div><!-- /.item -->

the javascript I'm using

jQuery(document).ready(function($) {

    // Main content container
    var $container = $('.masonry-container');

    // Masonry + ImagesLoaded
    $container.imagesLoaded(function(){
        $container.masonry({
            // selector for entry content
            itemSelector: '.item',
            columnWidth: '.grid-sizer',
            percentPosition: true,
            gutter: '.gutter-sizer'
        });
    });

    // Infinite Scroll
    $container.infinitescroll({

        // selector for the paged navigation (it will be hidden)
        navSelector  : ".post-nav",
        // selector for the NEXT link (to page 2)
        nextSelector : ".next a",
        // selector for all items you'll retrieve
        itemSelector : ".item",

        // finished message
        loading: {
            msgText : 'Loading more items&hellip;',
            img : '/wp/wp-content/themes/evolutionarygraphics/images/wheel.svg',
            finishedMsg: "*********",
            }
        },

        // Trigger Masonry as a callback
        function( newElements ) {
            // hide new items while they are loading
            var $newElems = $( newElements ).css({ opacity: 0 });
            // ensure that images load before adding to masonry layout
            $newElems.imagesLoaded(function(){
                // show elems now they're ready
                $newElems.animate({ opacity: 1 });
                $container.masonry( 'appended', $newElems, true );
            })
        }
    );

}); //end ready functions

@desandro
Copy link
Owner

desandro commented Mar 31, 2017

Has there been any progress to getting imagesLoaded to work with srcset?

Sorry, no news on my front. I'm happy to look over Pull Requests if submitted.

Add your 👍 reaction to the original comment on top to add your support. Do not add +1 comments — They will be deleted.

@DaAwesomeP
Copy link

Is it a known bug that there is a side-affect where the browser will end up loading two images?

I am seeing an issue where the browser loads the srcset image and then a little later imagesLoaded loads the fallback image. In many cases the fallback image is much larger since it is not size-selected by the browser.

@mattsummers
Copy link

I'm seeing that same behavior

desandro added a commit that referenced this issue Feb 18, 2022
- use currentSrc
- emit progress with <picture>
- ✅ add <picture> test
- ✅ test srcset
- add test css
- remove bower_components
@desandro
Copy link
Owner

imagesLoaded v5 has been released with full support for srcset and <picture>. I'm closing this issue as fixed. If you do encounter an issue with srcset and imagesLoaded v5, please submit a new issue.

Thank you for your patience all these years! ❤️

Repository owner locked as resolved and limited conversation to collaborators Feb 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests