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

Chrome doesn't load rel="preload" stylesheets on resize / viewport change when using a media query #254

Closed
optimalisatie opened this Issue Dec 5, 2017 · 5 comments

Comments

Projects
None yet
3 participants
@optimalisatie

optimalisatie commented Dec 5, 2017

Chrome skips loading rel="preload" as="style" based on the media query (media="") and it doesn't load the stylesheet when the viewport is changed. This causes issues on mobile devices when CSS is loaded specifically based on the viewport.

I found a report on StackOverflow in which it is suggested that it is a browser bug. I am using Chrome 62.

https://stackoverflow.com/questions/42441960/preloaded-media-query-css-doesnt-applies-on-resize-in-chrome

@optimalisatie

This comment has been minimized.

Show comment
Hide comment
@optimalisatie

optimalisatie Dec 5, 2017

The following may work:

// add viewport watch to load stylesheet on viewport change
// Chrome doesn't load rel="preload" when the viewport changes

// matchMedia support
var matchMedia = window.matchMedia || window.msMatchMedia;

// rel="preload" with media="" that does not match viewport
if (media && ['all','print'].indexOf(media) === -1 && matchMedia && !matchMedia(media).matches) {

    var mediaWatcher = function() {
        // @todo add throttle

        // viewport matches media query
        if (matchMedia(media).matches) {
            sheet.rel = 'stylesheet';
        }

        // also unwatch if already loaded
        if (sheet.rel === 'stylesheet') {
            // window.removeEventListener;
        }
    }

    // @todo cross browser
    window.addEventListener("orientationchange", mediaWatcher, false); 
    window.addEventListener("resize", mediaWatcher, false);   
}

optimalisatie commented Dec 5, 2017

The following may work:

// add viewport watch to load stylesheet on viewport change
// Chrome doesn't load rel="preload" when the viewport changes

// matchMedia support
var matchMedia = window.matchMedia || window.msMatchMedia;

// rel="preload" with media="" that does not match viewport
if (media && ['all','print'].indexOf(media) === -1 && matchMedia && !matchMedia(media).matches) {

    var mediaWatcher = function() {
        // @todo add throttle

        // viewport matches media query
        if (matchMedia(media).matches) {
            sheet.rel = 'stylesheet';
        }

        // also unwatch if already loaded
        if (sheet.rel === 'stylesheet') {
            // window.removeEventListener;
        }
    }

    // @todo cross browser
    window.addEventListener("orientationchange", mediaWatcher, false); 
    window.addEventListener("resize", mediaWatcher, false);   
}
@scottjehl

This comment has been minimized.

Show comment
Hide comment
@scottjehl

scottjehl Dec 5, 2017

Member

This one's interesting. It appears to be a Chrome bug with stylesheet links that begin as preload, and since it's not in the tracker, I'll file it

As for how to work around this in your own code, I'd suggest you either:

a) Use media=all (or omit media entirely) for your rel=preload stylesheet(s) and instead move the @media qualifiers inside the stylesheets rather than on the media attribute. Each link is going to download a stylesheet regardless of whether the media attribute matches or not, so the difference here is minor: it's just another means of having styles apply or not when they match. You might go further and combine the styles into one sheet as well, as I'm not sure there's much to gain in breaking up the styles into many requests.
b) Since your CSS is cleanly separated out into breakpoints already, you might just use rel=stylesheet from the start and avoid preload. Like preload, every rel=stylesheet link will download a file, but the links with media types that do not match will at least download in an async, non-blocking manner, like preload would. The drawback here is potentially having several blocking CSS requests.

I think I'd go with A if it were my project.

Since this is a chrome bug, and loadCSS's preload polyfill actually does nothing in Chrome because it supports the feature natively, I'll close this out. I'll link the chromium bug when it's up.

Member

scottjehl commented Dec 5, 2017

This one's interesting. It appears to be a Chrome bug with stylesheet links that begin as preload, and since it's not in the tracker, I'll file it

As for how to work around this in your own code, I'd suggest you either:

a) Use media=all (or omit media entirely) for your rel=preload stylesheet(s) and instead move the @media qualifiers inside the stylesheets rather than on the media attribute. Each link is going to download a stylesheet regardless of whether the media attribute matches or not, so the difference here is minor: it's just another means of having styles apply or not when they match. You might go further and combine the styles into one sheet as well, as I'm not sure there's much to gain in breaking up the styles into many requests.
b) Since your CSS is cleanly separated out into breakpoints already, you might just use rel=stylesheet from the start and avoid preload. Like preload, every rel=stylesheet link will download a file, but the links with media types that do not match will at least download in an async, non-blocking manner, like preload would. The drawback here is potentially having several blocking CSS requests.

I think I'd go with A if it were my project.

Since this is a chrome bug, and loadCSS's preload polyfill actually does nothing in Chrome because it supports the feature natively, I'll close this out. I'll link the chromium bug when it's up.

@scottjehl scottjehl closed this Dec 5, 2017

scottjehl added a commit that referenced this issue Dec 5, 2017

@optimalisatie

This comment has been minimized.

Show comment
Hide comment
@optimalisatie

optimalisatie Dec 5, 2017

It appears that Chrome doesn't download rel="preload" resources that do not match a media query, potentially saving traffic. If the preload polyfill would provide a fix then it would provide a significant performance advantage in Chrome and maybe other browsers (60% of all browsers).

Mozilla describes it as following indicating that it's the intended functionality:

One nice feature of <link> elements is their ability to accept media attributes. These can accept media types or full-blown media queries, allowing you to do responsive preloading!

https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content

Responsive preloading is an optimization technique with quite some potential for responsive designs that are optimized for every possible device and usage scenario.

On mobile the first page load speed is sometimes what matters. An option to save 100kb CSS using a responsive preload is something that could be of significance. It would be nice if loadCSS would be a solution that would make it possible.

optimalisatie commented Dec 5, 2017

It appears that Chrome doesn't download rel="preload" resources that do not match a media query, potentially saving traffic. If the preload polyfill would provide a fix then it would provide a significant performance advantage in Chrome and maybe other browsers (60% of all browsers).

Mozilla describes it as following indicating that it's the intended functionality:

One nice feature of <link> elements is their ability to accept media attributes. These can accept media types or full-blown media queries, allowing you to do responsive preloading!

https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content

Responsive preloading is an optimization technique with quite some potential for responsive designs that are optimized for every possible device and usage scenario.

On mobile the first page load speed is sometimes what matters. An option to save 100kb CSS using a responsive preload is something that could be of significance. It would be nice if loadCSS would be a solution that would make it possible.

@radum

This comment has been minimized.

Show comment
Hide comment
@radum

radum Feb 13, 2018

@scottjehl Did you managed to file a bug for this yet?

@optimalisatie It can't really be intended functionality, because if you are on a tabled that on portrait loads a CSS file based on the media query and on landscape loads another one, it will fail.

I also think loadCSS should actually cater for this bug.

radum commented Feb 13, 2018

@scottjehl Did you managed to file a bug for this yet?

@optimalisatie It can't really be intended functionality, because if you are on a tabled that on portrait loads a CSS file based on the media query and on landscape loads another one, it will fail.

I also think loadCSS should actually cater for this bug.

@optimalisatie

This comment has been minimized.

Show comment
Hide comment
@optimalisatie

optimalisatie Feb 22, 2018

Based on this bug report/discovery, we created a solution for responsive CSS loading, rendering and unrendering. It is integrated in our new advanced CSS optimization plugin for WordPress.

https://github.com/o10n-x/wordpress-css-optimization

The plugin enables to load CSS responsive or using a timing method (inview, requestAnimationFrame with frame target etc). Both loading and rendering can be timed independently so that it would be possible to start downloading (a group of) stylesheets in the background on domready or CPU idle time and render them when an element scrolls into view or based on a mobile device orientation change. It also enables to simply save 500kb of CSS data on mobile devices.

Timed render also supports unrendering stylesheets, enabling to use the feature to change the design dynamically based on a timing method, element visibility or Media Query.

image

The plugin also provides an option to load CSS from localStorage which has proven to be very fast. The debug modus of the plugin provides a Performance API result for both loading and rendering to be able to find the best configuration.

optimalisatie commented Feb 22, 2018

Based on this bug report/discovery, we created a solution for responsive CSS loading, rendering and unrendering. It is integrated in our new advanced CSS optimization plugin for WordPress.

https://github.com/o10n-x/wordpress-css-optimization

The plugin enables to load CSS responsive or using a timing method (inview, requestAnimationFrame with frame target etc). Both loading and rendering can be timed independently so that it would be possible to start downloading (a group of) stylesheets in the background on domready or CPU idle time and render them when an element scrolls into view or based on a mobile device orientation change. It also enables to simply save 500kb of CSS data on mobile devices.

Timed render also supports unrendering stylesheets, enabling to use the feature to change the design dynamically based on a timing method, element visibility or Media Query.

image

The plugin also provides an option to load CSS from localStorage which has proven to be very fast. The debug modus of the plugin provides a Performance API result for both loading and rendering to be able to find the best configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment