I've got a grid of models, each consisting of a link, image and name. On mouseenter, I'm looking to replace the src-attribute of the hovered image every second by a list of AJAX loaded images in a loop. On mouseleave, the original image should revert and the loop should stop.
<div class="c-grid c-grid--models">
<a href="#link-to-model" class="c-model" data-id="1">
<figure class="c-model__wrap">
<img src="http://placeholder.image" data-original="http://lazyloaded.image" class="c-model__image" />
<figcaption class="c-model__name">Model 1</figcaption>
</figure>
</a>
<a href="#link-to-model" class="c-model" data-id="2">
<figure class="c-model__wrap">
<img src="http://placeholder.image" data-original="http://lazyloaded.image" class="c-model__image" />
<figcaption class="c-model__name">Model 2</figcaption>
</figure>
</a>
</div>
The images are lazyloaded when they appear in the viewport. They are stored in the data-original tag. Below is the script that I got working.
var _brakes = 1;
var _timer1 = null;
var _timer2 = null;
var _data = null;
$('.c-grid--models .c-model').hover(
function() {
_brakes = 0;
_id = $(this).attr('data-id');
$image = $('img',$(this));
var $this = $(this);
_timer1 = setTimeout(function (){
$this.request('onHover', {
data : {
id : _id
},
success: function(data) {
_data = $.parseJSON(data.result);
$.each(_data, function(i, new_image) {
_timer2 = setTimeout(function (){
if(_brakes == 0) {
$image.attr('src', new_image);
} else {
clearTimeout(_timer1);
clearTimeout(_timer2);
_data = null;
_timer1 = null;
_timer2 = null;
}
}, i * 1000);
});
}
});
}, 1000);
}, function() {
_brakes = 1;
$image = $('img',$(this));
$image.attr('src',$image.attr('data-original'));
clearTimeout(_timer1);
clearTimeout(_timer2);
_data = null;
_timer1 = null;
_timer2 = null;
}
);
Problems:
Note: $(this).request('onHover'...
is part of the October CMS Ajax framework. It executes a PHP function that returns a JSON array of all the images of the model that currently is hovered.
Can anyone help with this issue? It might be that the logic I used is not right. Any input would be greatly appreciated. Thanks!
Related:
A good friend helped me out and solved the problem for me.
We took a different approach, storing the total amount of images and the current image in data-attributes on each html element.
Instead of returning the whole array of images, we now rotate over the images one per one.
New HTML:
<div class="c-grid c-grid--models">
<a href="#link-to-model" class="c-model" data-id="1" data-image-counter="5" data-current-image="0">
<figure class="c-model__wrap">
<img src="http://placeholder.image" data-original="http://lazyloaded.image" class="c-model__image" />
<figcaption class="c-model__name">Model 1</figcaption>
</figure>
</a>
<a href="#link-to-model" class="c-model" data-id="2" data-image-counter="5" data-current-image="0">
<figure class="c-model__wrap">
<img src="http://placeholder.image" data-original="http://lazyloaded.image" class="c-model__image" />
<figcaption class="c-model__name">Model 2</figcaption>
</figure>
</a>
</div>
New JavaScript:
$(function() {
var hoveredItem = null;
// Don't fire on touch devices
if($('.c-grid--models').length && !Modernizr.touchevents) {
$('.c-grid--models .c-model').hover(
function() { // mouseenter
$(this).data("mouse-hover", true);
if(!$(this).data("is-busy") ) {
$(this).data("is-busy", true);
GetNextImage($(this));
}
// get next image with delay
}, function() { // mouseleave
// set original image and stop loop
$(this).data("mouse-hover", false);
var orignalimage = $("img",$(this)).data("original");
$("img", $(this)).attr("src", orignalimage);
}
);
}
function GetNextImage(imageContainer) {
hoveredItem = imageContainer;
if (parseInt(imageContainer.data("image-counter")) <= 1) return;
setTimeout( function() {
if (imageContainer.data("mouse-hover") == true)
{
imageContainer.data("current-image", parseInt(imageContainer.data("current-image")) + 1);
$(this).request("onHover", {
data: {
id: imageContainer.data("id"),
image_id: imageContainer.data("current-image")
}, success: function (data) {
// Reset
var total = parseInt(imageContainer.data("image-counter"))-1;
var current = parseInt(imageContainer.data("current-image"));
if (current >= total) imageContainer.data("current-image", 0);
// async callback finished. no longer busy
if (imageContainer.data("mouse-hover") == true) {
var _data = $.parseJSON(data.result);
$('img',imageContainer).attr('src', _data);
GetNextImage(imageContainer); //Loop
} else {
imageContainer.data("is-busy", false);
}
}
});
} else {
imageContainer.data("is-busy", false);
}
}, 1000);
}
$(window).scroll(function() {
if (hoveredItem != null) {
hoveredItem.data("mouse-hover", false);
}
});
});
Important to note is that on scrolling does not always fire onmouseleave. So upon scrolling we cancel the image rotation until the user mouseenters once more.
I hope this can be useful. If somethings are not clear, just comment below and I'll update the answer.