hotwired / turbo

The speed of a single-page web application without having to write any JavaScript
https://turbo.hotwired.dev
MIT License
6.73k stars 429 forks source link

Safari: `<picture>` in asynchronously loaded pages get fallback image #331

Closed saneef closed 2 years ago

saneef commented 3 years ago

Turbo version: 7.0.0-rc.1.

This is a weird problem only faced in Safari (tested in 14.1.2).

I moved to Turbo from Swup. In my website, I use <picture> tag to load responsive images (showing sample code below).

In Safari, when the page is navigated to, through Turbo the fallback image (file specified in the <img>) is loaded.

In other browsers, the correct image matching the width and pixel density is loaded.

Example:

<picture
  ><source
    type="image/webp"
    srcset="
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-100w.webp   100w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-300w.webp   300w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-500w.webp   500w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-700w.webp   700w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-900w.webp   900w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-1100w.webp 1100w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-1300w.webp 1300w
    "
    sizes="100vw" />
  <source
    type="image/jpeg"
    srcset="
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-100w.jpeg   100w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-300w.jpeg   300w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-500w.jpeg   500w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-700w.jpeg   700w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-900w.jpeg   900w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-1100w.jpeg 1100w,
      /images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-1300w.jpeg 1300w
    "
    sizes="100vw" />
  <img
    src="/images/anh-tuan-to-F2FIM5HJogk-unsplash-110cbaf3-100w.jpeg"
    width="1300"
    height="866"
    alt="Photo by Anh Tuan To on Unsplash"
    sizes="100vw"
    loading="lazy"
    decoding="async"
/></picture>

To demonstrate the problem, you can visit https://sleepy-hawking-71d88c.netlify.app in Safari and click through the links. When navigated through links, you'll see a blurred image (with filename ending with -100w.jpeg). If you refresh the page, the correct image is loaded.

You can also find the code the sample page in this repo.

This problem is not there in the latest versions of Turbolinks, Barba.js, or Swup.

tleish commented 3 years ago

This appears to be an issue with safari, ajax and srcset... not turbo.

That said, you can look at some workarounds such as suggested here https://stackoverflow.com/questions/45487105/ajax-loaded-images-in-safari-not-respecting-srcset

saneef commented 3 years ago

If the <picture> tag is within a <turbo-frame> the correct image loads. But, the fallback image is also fetched.

blimpage commented 3 years ago

I've been running into this same issue with Turbo in both macOS Safari and iOS Safari. It happens with both <picture> and <img> elements.

I noticed that Safari doesn't load the fallback image, it loads the first image URL from the srcset attribute. So you can at least control which image Safari will use by changing the order of the URLs listed in your srcset attributes.

This old issue from the Turbolinks Classic repo sounds very similar: https://github.com/turbolinks/turbolinks-classic/issues/413 Is this possibly a re-occurrence of the same issue?

boboldehampsink commented 3 years ago

Same here. For now I am using this (ugly) snippet to work around the Safari problem:

// Image fix for Safari
document.addEventListener('turbo:render', () => {
  if (navigator.userAgent.match(/Version\/[\d.]+.*Safari/)) {
    document.querySelectorAll('picture').forEach((img) => {
      img.outerHTML = img.outerHTML; // eslint-disable-line no-param-reassign, no-self-assign
    });
  }
});
hudon commented 2 years ago

<audio controls=true> tags in Safari seem to suffer from the same bug: navigating through a Turbo link does not render the <audio> controls at all, and you need to use the above workaround https://github.com/hotwired/turbo/issues/331#issuecomment-956019113 (replace picture with audio)

ocarreterom commented 2 years ago

I have the same problem with <video> element. The controls are not displayed and the video can't be played.

<video controls>
   <source src=""/>
</video>
saneef commented 2 years ago

Does anyone see this problem went away in the recent version of Safari? <picture> works for me on Safari 15.5 and Safari TP Release 149 (16.0).

petrsigut commented 2 years ago

@saneef we are using with srcset and in Safari 15.16.1 we still see the issue (and in the latest iOS 15 Safari).

We are running on turbo 7.2 beta:

https://user-images.githubusercontent.com/87228/188577893-7bb871e9-e27c-4ccd-97b8-ff9481854af0.mp4

rik commented 2 years ago

I believe this is related to Turbo's use of new DOMParser().parseFromString(). Other frameworks probably don't use this and don't run into this issue.

I've opened https://bugs.webkit.org/show_bug.cgi?id=244815 on Safari's side.

petrsigut commented 2 years ago

@rik thank you!

I just tried Safari on iOS 16, the same behavior.

rik commented 2 years ago

A fix landed 4 days ago. So I'd expect to see it soon in a Safari Technology Preview (probably 154 or 155) and later in a release Safari (16.1 or 16.2).

tbuehl commented 2 years ago

Thanks @rik!

rik commented 2 years ago

STP 155 is out so you can try and see if it fixes your issues: https://webkit.org/blog/13338/release-notes-for-safari-technology-preview-155/