Reputation: 7416
I have a very long page that dynamically loads images as users scroll through.
However, if a user quickly scrolls away from a certain part of the page, I don't want the images to continue loading in that now out-of-view part of the page.
There are lots of other requests happening on the page simultaneously apart from image loading, so a blunt window.stop() firing on the scroll event is not acceptable.
I have tried removing & clearing the img src attributes for images that are no longer in view, however, since the request was already started, the image continues to load.
Remember that the image src was filled in as the user briefly scrolled past that part of the page. Once past though, I couldn't get that image from stop loading without using window.stop(). Clearing src didn't work. (Chrome & FF)
Similar posts I found that get close, but don't seem to solve this problem:
Upvotes: 48
Views: 28674
Reputation: 1618
The solution could be a webworker. a webworker can be terminated and with him the connection. But there is a small problem that the webworker uses the limited connections of the browser so the application will be blocked.
Right now I'm working on a solution with serviceWorkers - they don't have a connection limit (I hope so)
Upvotes: 0
Reputation: 33827
You can load your images using ajax calls, and in case that the uses scrolls-out, you can abort the calls.
In jQuery pseudo-code it would be something like that (forgive me mistakes in syntax, it is just an example):
1) tag images that you want to load
$(".image").each( function(){
if ( is_in_visible_area(this) ) { // check [1] for example
$(this).addClass("load_me");
} else {
$(this).addClass("dont_load");
}
});
2) load images
ajax_requests = {};
$(".image.load_me").each( function(){
// load image
var a = $.ajax({
url: 'give_me_photos.php',
data: {img: photo_id},
success: function(html){
photo_by_id(photo_id), img.append(html);
}
});
ajax_requests[photo_id] = a;
});
3) cancel loading those out of the screen
for( id in ajax_requests ) {
if ( ! is_in_visible_area(id) ) {
ajax_requests[id].abort();
}
}
Of course, add also some checking if the image is already loaded (e.g. class "loaded")
[1]. Check if element is visible after scrolling
[2]. Abort Ajax requests using jQuery
Upvotes: 2
Reputation: 1476
Maybe you could serve the image through a php script which would check a field in the the db (or better yet a memcached) that would indicate stop loading. the script would portion up the image into chunks and pause in between each chunk and check if the stop flag for the particular request is. If it is set you send the header with A 204 no content which as soon as the browser gets it will stop receiving.
This may be a bit over kill though.
Upvotes: 0
Reputation: 5664
Use a stack to manage ajax requests (means you will have serial loading instead of parallel but it is worth it)
On scroll stop, wait for 300ms and then push all images inside view-area into stack
Every time a user scrolls check if a stack is running. (fyi - you can stop all requests to a particular url instead of killing all ajax calls. also you can use regex so it should not stop any other requests on the page)
If an existing stack is running - pop all the images that are in it except for the top most one.
On all ajax calls - bind beforeSend() event to remove that particular image from the stack
It is late right now, but we have done something very similar at work - if you need the detailed code let me know.
Cheers!
Upvotes: 1
Reputation: 6955
BTW, another idea that might work:
1) create a new iframe
2) inside of the iframe have the script that starts loading the image, and once it's loaded, call the .parent
's method
3) when in need, stop the iframe content loading using .stop
on the iframe object
Upvotes: 1
Reputation: 6955
Well, my idea:
1) initiate an AJAX request for the image, if it succeeds, the image goes to the browser cache, and once you set the 'src' attribute, the image is shown from the cache
2) you can abort the XHR
I wrote a tiny server with express emulating the huge image download (it actually just waits 20 seconds, then returns an image). Then I have this in my HTML:
<!DOCTYPE html>
<html>
<head>
<style>
img {
width: 469px;
height: 428px;
background-color: #CCC;
border: 1px solid #999;
}
</style>
</head>
<body>
<img data-src="./img" src="" />
<br />
<a id="cancel" href="javascript:void(0)">CANCEL</a>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>
$(function () {
var xhr, img = $('img'), src = img.data('src');
xhr = $.ajax(src, {
success: function (data) { img.attr('src', src) }
});
$('#cancel').click(function (){
xhr.abort();
})
});
</script>
</body>
</html>
Upvotes: 12
Reputation: 53536
What you are trying to do is the wrong approach, as mentioned by nrabinowitz. You can't just "cancel" the loading process of an image (setting the src
attribute to an empty string is not a good idea). In fact, even if you could, doing so would only make things worst, as your server would continually send data that would get cancelled, increasing it's load factor and slow it down. Also, consider this:
Often, computer problems are simply design problems.
** EDIT **
Here's an idea :
your page should display DIV
containers with the width and height of the expected image size (use CSS to style). Inside of each DIV
, add an link. For example :
<div class="img-wrapper thumbnail">
<a href="http://www.domain.com/path/to/image">Loading...</a>
</div>
Add this Javascript (untested, the idea is self describing)
$(function() {
var imgStack;
var loadTimeout;
$(window).scroll(function() {
imgStack = null;
if (loadTimeout) clearTimeout(loadTimeout);
loadTimeout = setTimeout(function() {
// get all links visible in the view port
// should be an array or jQuery object
imgStack = ...
loadNextImage();
}, 200); // 200 ms delay
});
function loadNextImage() {
if (imgStack && imgStack.length) {
var nextLink = $(imgStack.pop()); // get next image element
$('<img />').attr('src', nextLink.attr('href'))
.appendTo(nextLink.parent())
.load(function() {
loadNextImage();
});
// remove link from container (so we don't precess it twice)
nextLink.remove();
}
};
});
Upvotes: 17