Giter Site home page Giter Site logo

Comments (15)

dinbror avatar dinbror commented on August 22, 2024

Very interesting, thanks for sharing.

Will need to do some testing

from blazy.

tomByrer avatar tomByrer commented on August 22, 2024

Images seem to stall out on loading on my iPad5 (Air) iOS7.0. When I update I'll edit this post.

from blazy.

dinbror avatar dinbror commented on August 22, 2024

Any news tom? I tested on ipad5 with iOS7.1. No issues

from blazy.

joshribakoff avatar joshribakoff commented on August 22, 2024

I'm also working on this issue. First of all, I'm on an iPad, and I do replicate the problem:
1 - Touch & hold at bottom of viewport
2 - Drag up to scroll down, but do not release

Actual results: I see placeholder images/broken images, and no loading is triggered until I release
Expected results: trigger loading as soon as I can "see" the image [regardless of my finger being remaining pressed on the screen or not].

Now, I changed the code to bind touchmove, which yields no observable changes to the behavior at all. Although by debugging I can see that by binding touchmove, your function validate() gets called continuously as I drag, via the throttle() method. Furthermore it triggers loadImage() on every image immediately as it enters my viewport, without my releasing... but what doesn't happen is the browser does not show those images until I release. It does however preload them.

It looks like this is because when an image is called with loadImage() it creates an Image object in javascript for purposes of preloading, and attaches an onload callback to that image object, which updates the actual DOM image upon success. It is perhaps the execution of this onload callback that seems to be queued until I "touch release". The article does mention timeouts & intervals are queued, so this seems to be the same problem. I also found another article about JS image loading, where one commenter states:

iOS currently pauses all JavaScript during scroll to divert CPU power to rendering the page.

I'm not sure what this means, but its not literally true. I can write out a timestamp to a div "ontouchmove" and view the number continuously changing in a rapid fashion while I'm moving my finger, before releasing, so this proves the whole JS thread isn't blocked.

I then replaced your img.src = src line with ele.src = src which will set the src on the image tag in the DOM immediately, circumventing your preload indirection. I also append some debug info to a div upon doing this:

ele.src = src;
$('#ios').append('<br />' + src);

What I observe is that while my finger is held down on the screen scrolling, I see the URLs for the images logged to that div continuously, indicating loadImage() is being called on each image as it enters the viewport - however it seems the images still don't showup until I release my finger.

One difference worth noting is that when I set the src on the element & circumvent the preload logic, there is more latency before the images are loaded. To me what this suggests is that JS does execute while scrolling, and you can load images in the background while scrolling with:

var img = new Image();
img.src = 'foo.jpg';

and that foo.jpg will be prefetched while scrolling. However it seems iOS is blocking the DOM updates, or the repainting of the viewport. I tried changing the code from:

ele.src = src;
                $('#ios').append('<br />' + src);

to this

ele.src = src;
                $('#ios').append('<br />' + ele.src);

What this is supposed to test out is did the HTML attribute change. If the HTML attribute change is what was queued, I'd expect to get NULL or whatever my placeholder src was. In fact I get the new URL, which seems to indicate that HTML attribute for the img's src is updated instantly while scrolling, as well as the browser fetches the image resource (as suggested by the fact the images load 'instantly' after releasing my finger). So from all of this, I can only assume that iOS is queue the redraw within the browser (albeit still executing javascript).

It's interesting the text in the div is updated though & not queued. Do they queue only certain DOM updates, and is there a way to abuse that to force our images to load? I don't know and I've run out of things to try at this point. Although I have now learned their reason to make the updates queue while scrolling. Its apparently to preserve battery/CPU.

I guess some developers have worked around this by creating libraries that disable scrolling in the browser, and render a "fake" scrollback which is entirely JS based. Not something I'm fond of though.

from blazy.

tomByrer avatar tomByrer commented on August 22, 2024

Great sluthing @joshribakoff

from blazy.

joshribakoff avatar joshribakoff commented on August 22, 2024

Actually I think I cracked the cookie guys! (or whatever the saying is).

So, like I said above I found its strange I can append HTML to a div, but not update an HTML attribute of an existing element. I tried changing just now:

$('#ios').append('<br />' + ele.src);

becomes this:

$('#ios').append('<br />loaded: ' + '<img src="'+src+'" />');

And while holding my finger on the screen & moving, I see the images load into my debug div right away without releasing my finger! So the trick is to insert new DOM elements instead of manipulating existing ones [in conjunction with binding to ontouchmove] for iOS.

So all you need to do is insert a new img element below the original img, and remove the original img tag. These types of updates are repainted right away, whereas changing the src attribute on an image tag is queued.

PS... I am on iOS 7.1

from blazy.

tomByrer avatar tomByrer commented on August 22, 2024

the trick is to insert new DOM elements instead of manipulating existing ones [in conjunction with binding to ontouchmove] for iOS

Great find. Could be a performance nightmare; all the reflows, so likely iOS-only workaround. I tried to find on jsPerf a test, but no luck.

from blazy.

joshribakoff avatar joshribakoff commented on August 22, 2024

There's actually no reflow, since blazy recommends a div wrapper with a padding-bottom to retain the space in the flow of the page.

Here's another observation:

$('#ios').append('preload: ' + '<img src="' + src + '" /><hr />');

This works, the text & image is appended immediately while scrolling (never releasing my finger) I see the images append to the div.

This does not work, seems replacing a div's contents is queued.

$('#ios').html('preload: ' + '<img src="' + src + '" /><hr />');

This also does not work, seems that the presence of CSS is making the repaint queue up:

$('#ios').append('preload: ' + '<img src="' + src + '" style="max-width:50px" /><hr />');

Also even when I've hit the sweet spot on things working, things intermittently don't work, sometimes the updates don't batch out until I release my finger.... but by tweaking the code to get in the sweet spot things seem to work 90% of the time.

So my sweet spot so far is to append a new image, with no styling, to the wrapper div. Your CSS should be written in such a way that appended images obscure any other images in that container. Something like this works for me:

(LESS)

.lazy-img-container {
  max-width:100%;
  position:relative;
  img {
    position:absolute;
    width:100%;
    height:100%;
  }
}

I should also note that there seems to be a bug where loadImage(ele) is called twice for a given image. Doesn't seem to affect the viability of this solution, but should be fixed separately.

from blazy.

dinbror avatar dinbror commented on August 22, 2024

Very interesting and nice debugging. Will need to do some testing myself, just really busy at the moment. I'm not thrilled about the workaround :) I foresee some inexpediencies.

Have any of you tested on an android tablet?

Thanks for all the testing, I really appreciate it

from blazy.

benjibee avatar benjibee commented on August 22, 2024

I'm running into the same issue now while trying to implement this. In doing some basic testing, every user I've observed rapidly scrolls around my page and rarely fully released their finger from the screen or let the momentum of the scrolling come to a stop. So images barely ever have a chance to load…

Any progress on this front? : )

from blazy.

dinbror avatar dinbror commented on August 22, 2024

No Im afraid not. It's a "feature" in iOS. iOS devices freeze DOM manipulation during scroll, queuing them to apply when the scroll finishes.

Solutions at the moment is either go with @joshribakoff approach where you append new image markup or override the native scroll.

from blazy.

joshribakoff avatar joshribakoff commented on August 22, 2024

As of iOS8, the scroll event fires continuosly & all DOM updates go through right away.

from blazy.

dinbror avatar dinbror commented on August 22, 2024

Hey Josh.

Yes I read that and I'm pretty sure I tested it when iOS 8 was released but no change. However now it works wonderful in 8.1.x.
Anyone still running 8.0.0 and able to test?

from blazy.

jDeppen avatar jDeppen commented on August 22, 2024

Not fixed for Cordova web views :(

One caveat for Cordova developers

Although Apple implemented this change in iOS Safari, as well as its new WKWebView control, it did not change the scroll behavior in its old UIWebView control. And because of a major bug in the replacement WKWebView control, the Cordova team cannot upgrade to WKWebView yet.

This means that at the moment Cordova apps running on iOS 8 continue to pause JavaScript execution, and will continue to until Cordova can upgrade. And this doesn’t just affect Cordova apps. Any iOS app that uses web views — including Facebook, Twitter, and Chrome for iOS — will get the old behavior until they upgrade their apps to WKWebView. So yes, that means you could get different behavior opening the same URL from different iOS apps depending on which API they use internally.

Update (September 25th): Commenter Ben Kennedy found that, for whatever crazy reason, the old scroll behavior also applies to home screen web apps. So to summarize, apps that run in Safari or in a WKWebView get the new scroll behavior, apps that run in a UIWebView or in a home screen web app do not.

from blazy.

dinbror avatar dinbror commented on August 22, 2024

Hey @jDeppen.

Yes I noticed it in chrome for IOS. Thanks for the explanation.

from blazy.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.