jQuery prev/next button in polaroid gallery plugin - javascript

I am using a polaroid image gallery plugin. The way it works, the polaroid images are scattered in a div and it has some circle nav buttons at the bottom that can be clicked to make the selected image active and moves it to the middle.
Instead of having circle nav buttons for selecting each image, I wanted to use prev/next buttons (this way it won't show 20+ circle nav icons when the gallery has more than 20 photos).
I was able to add some prev/next buttons and get them working, but there is one slight issue I cannot figure out. When the gallery loads, if you select the prev/next buttons, the gallery works as intended, it will switch the photos as necessary when the buttons are clicked. However, if the user clicks anywhere in the gallery (not on the buttons) it will shuffle all of the photos (it should not shuffle), then once a user does this, the prev/next buttons no longer work until the page is refreshed.
I am still learning jQuery/js and I cannot figure out what is causing this. I need to restrict the shuffling and selecting of the photos only when the prev/next buttons are selected because if a user accidentally clicks anywhere in the gallery, the buttons break.
I created a jsfiddle here.
The html is pretty straight forward, here is an example with the prev/next buttons I added:
<section id="photostack-1" class="photostack photostack-start">
<div>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/5.jpg" alt="img05"/>
<figcaption>
<h2 class="photostack-title">Speed Racer</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/2.jpg" alt="img02"/>
<figcaption>
<h2 class="photostack-title">Happy Days</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/3.jpg" alt="img03"/>
<figcaption>
<h2 class="photostack-title">Beautywood</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/1.jpg" alt="img01"/>
<figcaption>
<h2 class="photostack-title">Serenity Beach</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/4.jpg" alt="img04"/>
<figcaption>
<h2 class="photostack-title">Heaven of time</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/6.jpg" alt="img06"/>
<figcaption>
<h2 class="photostack-title">Forever this</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/7.jpg" alt="img07"/>
<figcaption>
<h2 class="photostack-title">Lovely Green</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/8.jpg" alt="img08"/>
<figcaption>
<h2 class="photostack-title">Wonderful</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/9.jpg" alt="img09"/>
<figcaption>
<h2 class="photostack-title">Love Addict</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/10.jpg" alt="img10"/>
<figcaption>
<h2 class="photostack-title">Friendship</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/11.jpg" alt="img11"/>
<figcaption>
<h2 class="photostack-title">White Nights</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/12.jpg" alt="img12"/>
<figcaption>
<h2 class="photostack-title">Serendipity</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/13.jpg" alt="img13"/>
<figcaption>
<h2 class="photostack-title">Pure Soul</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/14.jpg" alt="img14"/>
<figcaption>
<h2 class="photostack-title">Winds of Peace</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/15.jpg" alt="img15"/>
<figcaption>
<h2 class="photostack-title">Shades of blue</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/16.jpg" alt="img16"/>
<figcaption>
<h2 class="photostack-title">Lightness</h2>
</figcaption>
</figure>
</div>
<!-- Next and Previous controls -->
<div id="imageControls">
<button id="leftArrowGallery" class="btnGallery"><i class="fa fa-chevron-left"></i></button>
<button id="rightArrowGallery" class="btnGallery"><i class="fa fa-chevron-right"></i></button>
</div>
</section>
The custom jQuery used to add the prev/next buttons and remove the circle nav icons is:
var ps = new Photostack( document.getElementById( 'photostack-1' ), {});
$("#leftArrowGallery").prependTo($(".photostack > nav"));
$("#rightArrowGallery").appendTo($(".photostack > nav"));
$("#leftArrowGallery").on("click", function () {
ps.navigate('prev');
});
$("#rightArrowGallery").on("click", function () {
ps.navigate('next');
});
$(".photostack > nav > span").each(function(){ $(this).remove(); });
You can view all of the js for the plugin in the jsfiddle.
Screenshot example of original gallery with circle nav icons:
Screenshot example of gallery with custom prev/next buttons added:
Again, the buttons are working correctly, I just need to prevent when someone clicks anywhere in the gallery, it shuffles the images then the prev/next buttons break and no longer work until the page is refreshed.
Thank you for your help!

I read your code and I found out that the biggest problem when you put photostack-start class is that every time you click on photostack stage you schuffle your photos twice: once in _photoShow function and once in open function (open function is triggered every time). To resolve your problem, a solution could be to stop one schuffled action. To do this I created a variable and I put it in open function:
if( this.open ) {
return false;
}
this.open = true;
Now, _open function is triggered only once (when you click on button "view gallery") and after that only _photoShow works so your code works finally as you want.
BE CAREFUL: In this example I removed some pics 'cause I have a character limit here (30000), but the code will work anyway also with more pics.
A fiddle with more pics: https://jsfiddle.net/m46cxkhg/217/ (tested with Chrome and Firefox)
;( function( window ) {
'use strict';
Modernizr.addTest('csstransformspreserve3d', function () {
var prop = Modernizr.prefixed('transformStyle');
var val = 'preserve-3d';
var computedStyle;
if(!prop) return false;
prop = prop.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
Modernizr.testStyles('#modernizr{' + prop + ':' + val + ';}', function (el, rule) {
computedStyle = window.getComputedStyle ? getComputedStyle(el, null).getPropertyValue(prop) : '';
});
return (computedStyle === val);
});
var support = {
transitions : Modernizr.csstransitions,
preserve3d : Modernizr.csstransformspreserve3d
},
transEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'msTransition': 'MSTransitionEnd',
'transition': 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ];
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function shuffleMArray( marray ) {
var arr = [], marrlen = marray.length, inArrLen = marray[0].length;
for(var i = 0; i < marrlen; i++) {
arr = arr.concat( marray[i] );
}
arr = shuffleArr( arr );
var newmarr = [], pos = 0;
for( var j = 0; j < marrlen; j++ ) {
var tmparr = [];
for( var k = 0; k < inArrLen; k++ ) {
tmparr.push( arr[ pos ] );
pos++;
}
newmarr.push( tmparr );
}
return newmarr;
}
function shuffleArr( array ) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
function Photostack( el, options ) {
this.el = el;
this.inner = this.el.querySelector( 'div' );
this.allItems = [].slice.call( this.inner.children );
this.allItemsCount = this.allItems.length;
if( !this.allItemsCount ) return;
this.items = [].slice.call( this.inner.querySelectorAll( 'figure:not([data-dummy])' ) );
this.itemsCount = this.items.length;
this.options = extend( {}, this.options );
extend( this.options, options );
this.current = this.options.start;
this._init();
var ps = this;
return {
showPhoto: function(idx) {
ps._showPhoto.call(ps, idx);
},
open: function() {
ps._open.call(ps, true);
},
navigate: function(dir) {
ps._navigate.call(ps, dir);
},
}
}
Photostack.prototype.options = {
start: 0,
showNavigation: true,
afterInit: null,
afterShowPhoto: null,
afterNavigate: null
};
Photostack.prototype._init = function() {
this.currentItem = this.items[ this.current ];
if(this.options.showNavigation) {
this._addNavigation();
}
this._getSizes();
this._initEvents();
if(this.options.afterInit) {
this.options.afterInit(this);
}
}
Photostack.prototype._addNavigation = function() {
// add nav dots
this.nav = document.createElement( 'nav' )
var inner = '';
for( var i = 0; i < this.itemsCount; ++i ) {
inner += '<span></span>';
}
this.nav.innerHTML = inner;
this.el.appendChild( this.nav );
this.navDots = [].slice.call( this.nav.children );
}
Photostack.prototype._open = function( beforeStep ) {
/* I added this */
if( this.open ) {
return false;
}
this.open = true;
/* End of change */
var self = this,
el = this.el;
var setTransition = function() {
if( support.transitions ) {
classie.addClass( el, 'photostack-transition' );
}
}
if( beforeStep ) {
el.removeEventListener( 'click', open );
classie.removeClass( el, 'photostack-start' );
setTransition();
}
else {
self.openDefault = true;
setTimeout( setTransition, 25 );
}
self.started = true;
self._showPhoto( self.current );
};
Photostack.prototype._initEvents = function() {
if(this.options.clickToFlip == 'true')
{
this.items.forEach(function(img, idx){
img.addEventListener('click', function(event){
event.preventDefault();
if( idx === self.current ) {
self._rotateItem();
}
})
});
}
var self = this,
beforeStep = classie.hasClass( this.el, 'photostack-start' );
if( beforeStep ) {
this._shuffle();
this.el.addEventListener( 'click', function() {
self._open(beforeStep);
});
}
else {
this._open(beforeStep);
}
if(this.options.showNavigation) {
this.navDots.forEach( function( dot, idx ) {
dot.addEventListener( 'click', function() {
if( idx === self.current ) {
self._rotateItem();
}
else {
// if the photo is flipped then rotate it back before shuffling again
var callback = function() { self._showPhoto( idx ); }
if( self.flipped ) {
self._rotateItem( callback );
}
else {
callback();
}
}
} );
} );
}
window.addEventListener( 'resize', function() { self._resizeHandler(); } );
}
Photostack.prototype._resizeHandler = function() {
var self = this;
function delayed() {
self._resize();
self._resizeTimeout = null;
}
if ( this._resizeTimeout ) {
clearTimeout( this._resizeTimeout );
}
this._resizeTimeout = setTimeout( delayed, 100 );
}
Photostack.prototype._resize = function() {
var self = this, callback = function() { self._shuffle( true ); }
this._getSizes();
if( this.started && this.flipped ) {
this._rotateItem( callback );
}
else {
callback();
}
}
Photostack.prototype._showPhoto = function( pos ) {
if( this.isShuffling ) {
return false;
}
this.isShuffling = true;
// if there is something behind..
if( classie.hasClass( this.currentItem, 'photostack-flip' ) ) {
this._removeItemPerspective();
if(this.options.showNavigation) {
classie.removeClass( this.navDots[ this.current ], 'flippable' );
}
}
if(this.options.showNavigation) {
classie.removeClass( this.navDots[ this.current ], 'current' );
}
classie.removeClass( this.currentItem, 'photostack-current' );
// change current
this.current = pos;
this.currentItem = this.items[ this.current ];
if(this.options.showNavigation) {
classie.addClass( this.navDots[ this.current ], 'current' );
}
// if there is something behind..
if( this.options.showNavigation && this.currentItem.querySelector( '.photostack-back' ) ) {
// nav dot gets class flippable
classie.addClass( this.navDots[ pos ], 'flippable' );
}
// shuffle a bit
this._shuffle();
if(this.options.afterShowPhoto) {
this.options.afterShowPhoto(this);
}
}
// display items (randomly)
Photostack.prototype._shuffle = function( resize ) {
var iter = resize ? 1 : this.currentItem.getAttribute( 'data-shuffle-iteration' ) || 1;
if( iter <= 0 || !this.started || this.openDefault ) { iter = 1; }
// first item is open by default
if( this.openDefault ) {
// change transform-origin
classie.addClass( this.currentItem, 'photostack-flip' );
this.openDefault = false;
this.isShuffling = false;
}
var overlapFactor = .5,
// lines & columns
lines = Math.ceil(this.sizes.inner.width / (this.sizes.item.width * overlapFactor) ),
columns = Math.ceil(this.sizes.inner.height / (this.sizes.item.height * overlapFactor) ),
// since we are rounding up the previous calcs we need to know how much more we are adding to the calcs for both x and y axis
addX = lines * this.sizes.item.width * overlapFactor + this.sizes.item.width/2 - this.sizes.inner.width,
addY = columns * this.sizes.item.height * overlapFactor + this.sizes.item.height/2 - this.sizes.inner.height,
// we will want to center the grid
extraX = addX / 2,
extraY = addY / 2,
// max and min rotation angles
maxrot = 35, minrot = -35,
self = this,
// translate/rotate items
moveItems = function() {
--iter;
// create a "grid" of possible positions
var grid = [];
// populate the positions grid
for( var i = 0; i < columns; ++i ) {
var col = grid[ i ] = [];
for( var j = 0; j < lines; ++j ) {
var xVal = j * (self.sizes.item.width * overlapFactor) - extraX,
yVal = i * (self.sizes.item.height * overlapFactor) - extraY,
olx = 0, oly = 0;
if( self.started && iter === 0 ) {
var ol = self._isOverlapping( { x : xVal, y : yVal } );
if( ol.overlapping ) {
olx = ol.noOverlap.x;
oly = ol.noOverlap.y;
var r = Math.floor( Math.random() * 3 );
switch(r) {
case 0 : olx = 0; break;
case 1 : oly = 0; break;
}
}
}
col[ j ] = { x : xVal + olx, y : yVal + oly };
}
}
// shuffle
grid = shuffleMArray(grid);
var l = 0, c = 0, cntItemsAnim = 0;
self.allItems.forEach( function( item, i ) {
// pick a random item from the grid
if( l === lines - 1 ) {
c = c === columns - 1 ? 0 : c + 1;
l = 1;
}
else {
++l
}
var randXPos = Math.floor( Math.random() * lines ),
randYPos = Math.floor( Math.random() * columns ),
gridVal = grid[c][l-1],
translation = { x : gridVal.x, y : gridVal.y },
onEndTransitionFn = function() {
++cntItemsAnim;
if( support.transitions ) {
this.removeEventListener( transEndEventName, onEndTransitionFn );
}
if( cntItemsAnim === self.allItemsCount ) {
if( iter > 0 ) {
moveItems.call();
}
else {
// change transform-origin
classie.addClass( self.currentItem, 'photostack-flip' );
// all done..
self.isShuffling = false;
if( typeof self.options.callback === 'function' ) {
self.options.callback( self.currentItem );
}
}
}
};
if(self.items.indexOf(item) === self.current && self.started && iter === 0) {
self.currentItem.style.WebkitTransform = 'translate(' + self.centerItem.x + 'px,' + self.centerItem.y + 'px) rotate(0deg)';
self.currentItem.style.msTransform = 'translate(' + self.centerItem.x + 'px,' + self.centerItem.y + 'px) rotate(0deg)';
self.currentItem.style.transform = 'translate(' + self.centerItem.x + 'px,' + self.centerItem.y + 'px) rotate(0deg)';
// if there is something behind..
if( self.currentItem.querySelector( '.photostack-back' ) ) {
self._addItemPerspective();
}
classie.addClass( self.currentItem, 'photostack-current' );
}
else {
item.style.WebkitTransform = 'translate(' + translation.x + 'px,' + translation.y + 'px) rotate(' + Math.floor( Math.random() * (maxrot - minrot + 1) + minrot ) + 'deg)';
item.style.msTransform = 'translate(' + translation.x + 'px,' + translation.y + 'px) rotate(' + Math.floor( Math.random() * (maxrot - minrot + 1) + minrot ) + 'deg)';
item.style.transform = 'translate(' + translation.x + 'px,' + translation.y + 'px) rotate(' + Math.floor( Math.random() * (maxrot - minrot + 1) + minrot ) + 'deg)';
}
if( self.started ) {
if( support.transitions ) {
item.addEventListener( transEndEventName, onEndTransitionFn );
}
else {
onEndTransitionFn();
}
}
} );
};
moveItems.call();
}
Photostack.prototype._navigate = function(dir) {
var current = this.current,
itemsCount = this.itemsCount,
lastItem = itemsCount - 1,
idx = 0;
if(dir == 'next') {
idx = current < lastItem ? current + 1 : 0
} else if(dir == 'prev') {
idx = current > 0 ? current - 1 : lastItem;
}
this._showPhoto(idx);
if(this.options.afterNavigate) {
this.options.afterNavigate(this);
}
}
Photostack.prototype._getSizes = function() {
this.sizes = {
inner : { width : this.inner.offsetWidth, height : this.inner.offsetHeight },
item : { width : this.currentItem.offsetWidth, height : this.currentItem.offsetHeight }
};
// translation values to center an item
this.centerItem = { x : this.sizes.inner.width / 2 - this.sizes.item.width / 2, y : this.sizes.inner.height / 2 - this.sizes.item.height / 2 };
}
Photostack.prototype._isOverlapping = function( itemVal ) {
var dxArea = this.sizes.item.width + this.sizes.item.width / 3, // adding some extra avoids any rotated item to touch the central area
dyArea = this.sizes.item.height + this.sizes.item.height / 3,
areaVal = { x : this.sizes.inner.width / 2 - dxArea / 2, y : this.sizes.inner.height / 2 - dyArea / 2 },
dxItem = this.sizes.item.width,
dyItem = this.sizes.item.height;
if( !(( itemVal.x + dxItem ) < areaVal.x ||
itemVal.x > ( areaVal.x + dxArea ) ||
( itemVal.y + dyItem ) < areaVal.y ||
itemVal.y > ( areaVal.y + dyArea )) ) {
// how much to move so it does not overlap?
// move left / or move right
var left = Math.random() < 0.5,
randExtraX = Math.floor( Math.random() * (dxItem/4 + 1) ),
randExtraY = Math.floor( Math.random() * (dyItem/4 + 1) ),
noOverlapX = left ? (itemVal.x - areaVal.x + dxItem) * -1 - randExtraX : (areaVal.x + dxArea) - (itemVal.x + dxItem) + dxItem + randExtraX,
noOverlapY = left ? (itemVal.y - areaVal.y + dyItem) * -1 - randExtraY : (areaVal.y + dyArea) - (itemVal.y + dyItem) + dyItem + randExtraY;
return {
overlapping : true,
noOverlap : { x : noOverlapX, y : noOverlapY }
}
}
return {
overlapping : false
}
}
Photostack.prototype._addItemPerspective = function() {
classie.addClass( this.el, 'photostack-perspective' );
}
Photostack.prototype._removeItemPerspective = function() {
classie.removeClass( this.el, 'photostack-perspective' );
classie.removeClass( this.currentItem, 'photostack-flip' );
}
Photostack.prototype._rotateItem = function( callback ) {
if( classie.hasClass( this.el, 'photostack-perspective' ) && !this.isRotating && !this.isShuffling ) {
this.isRotating = true;
var self = this, onEndTransitionFn = function() {
if( support.transitions && support.preserve3d ) {
this.removeEventListener( transEndEventName, onEndTransitionFn );
}
self.isRotating = false;
if( typeof callback === 'function' ) {
callback();
}
};
if( this.flipped ) {
if(this.options.showNavigation) {
classie.removeClass( this.navDots[ this.current ], 'flip' );
}
if( support.preserve3d ) {
this.currentItem.style.WebkitTransform = 'translate(' + this.centerItem.x + 'px,' + this.centerItem.y + 'px) rotateY(0deg)';
this.currentItem.style.transform = 'translate(' + this.centerItem.x + 'px,' + this.centerItem.y + 'px) rotateY(0deg)';
}
else {
classie.removeClass( this.currentItem, 'photostack-showback' );
}
}
else {
if(this.options.showNavigation) {
classie.addClass( this.navDots[ this.current ], 'flip' );
}
if( support.preserve3d ) {
this.currentItem.style.WebkitTransform = 'translate(' + this.centerItem.x + 'px,' + this.centerItem.y + 'px) translate(' + this.sizes.item.width + 'px) rotateY(-179.9deg)';
this.currentItem.style.transform = 'translate(' + this.centerItem.x + 'px,' + this.centerItem.y + 'px) translate(' + this.sizes.item.width + 'px) rotateY(-179.9deg)';
}
else {
classie.addClass( this.currentItem, 'photostack-showback' );
}
}
this.flipped = !this.flipped;
if( support.transitions && support.preserve3d ) {
this.currentItem.addEventListener( transEndEventName, onEndTransitionFn );
}
else {
onEndTransitionFn();
}
}
}
// add to global namespace
window.Photostack = Photostack;
})( window );
var ps = new Photostack( document.getElementById( 'photostack-1' ), {});
$("#leftArrowGallery").prependTo($(".photostack > nav"));
$("#rightArrowGallery").appendTo($(".photostack > nav"));
$("#leftArrowGallery").on("click", function () {
ps.navigate('prev');
});
$("#rightArrowGallery").on("click", function () {
ps.navigate('next');
});
$(".photostack > nav > span").each(function(){ $(this).remove(); });
.photostack {
background: #ddd;
position: relative;
text-align: center;
overflow: hidden;
}
.js .photostack {
height: 580px;
}
.photostack-start {
cursor: pointer;
}
.photostack > div {
width: 100%;
height: 100%;
margin: 0 auto;
}
.photostack figure {
width: 320px;
height: 360px;
position: relative;
display: inline-block;
background: #fff;
padding: 40px;
text-align: center;
margin: 5px;
}
.js .photostack figure {
position: absolute;
display: block;
margin: 0;
}
.photostack figcaption h2 {
margin: 20px 0 0 0;
color: #a7a0a2;
font-size: 16px;
}
.photostack-img {
outline: none;
width: 240px;
height: 240px;
background: #f9f9f9;
}
.photostack-back {
display: none;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: #fff;
font-family: "Give You Glory", cursive;
color: #a7a0a2;
padding: 50px 40px;
text-align: left;
font-size: 22px;
line-height: 1.25;
z-index: 1;
}
.photostack-back p {
margin: 0;
}
.photostack-back p span {
text-decoration: line-through;
}
.photostack nav {
position: absolute;
width: 100%;
bottom: 30px;
z-index: 90;
text-align: center;
left: 0;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
}
.photostack-start nav {
opacity: 0;
}
.photostack nav span {
position: relative;
display: inline-block;
margin: 0 5px;
width: 30px;
height: 30px;
cursor: pointer;
background: #aaa;
border-radius: 50%;
text-align: center;
-webkit-transition: -webkit-transform 0.6s ease-in-out, background 0.3s;
transition: transform 0.6s ease-in-out, background 0.3s;
-webkit-transform: scale(0.48);
transform: scale(0.48);
}
.photostack nav span:last-child {
margin-right: 0;
}
.photostack nav span::after {
content: "\e600";
font-family: 'icons';
font-size: 80%;
speak: none;
display: inline-block;
vertical-align: top;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 30px;
color: #fff;
opacity: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.photostack nav span.current {
background: #888;
-webkit-transform: scale(1);
transform: scale(1);
}
.photostack nav span.current.flip {
-webkit-transform: scale(1) rotateY(-180deg) translateZ(-1px);
transform: scale(1) rotateY(-180deg) translateZ(-1px);
background: #555;
}
.photostack nav span.flippable::after {
opacity: 1;
-webkit-transition-delay: 0.4s;
transition-delay: 0.4s;
}
.js .photostack::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
top: 0;
left: 0;
z-index: 100;
-webkit-transition: opacity 0.3s, visibility 0s 0.3s;
transition: opacity 0.3s, visibility 0s 0.3s;
}
.js .photostack-start::before {
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
}
.js .photostack::after {
content: 'View Gallery';
font-weight: 400;
position: absolute;
border: 3px solid #fff;
text-align: center;
white-space: nowrap;
left: 50%;
top: 50%;
-webkit-transform: translateY(-50%) translateX(-50%);
transform: translateY(-50%) translateX(-50%);
padding: 10px 20px;
color: #fff;
text-transform: uppercase;
letter-spacing: 1px;
cursor: pointer;
z-index: 101;
}
.js .photostack::before,
.js .photostack::after {
opacity: 0;
visibility: hidden;
}
.js .photostack-start::before,
.js .photostack-start:hover::after,
.touch .photostack-start::after {
opacity: 1;
visibility: visible;
}
.photostack figure::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
visibility: visible;
opacity: 1;
background: rgba(0,0,0,0.05);
-webkit-transition: opacity 0.6s;
transition: opacity 0.6s;
}
figure.photostack-current::after {
-webkit-transition: opacity 0.6s, visibility 0s 0.6s;
transition: opacity 0.6s, visibility 0s 0.6s;
opacity: 0;
visibility: hidden;
}
.photostack-transition figure {
-webkit-transition: -webkit-transform 0.6s ease-in-out;
transition: transform 0.6s ease-in-out;
}
.photostack-perspective {
-webkit-perspective: 1800px;
perspective: 1800px;
}
.photostack-perspective > div,
.photostack-perspective figure {
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
}
.photostack-perspective figure,
.photostack-perspective figure div {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.photostack-perspective figure.photostack-flip {
-webkit-transform-origin: 0% 50%;
transform-origin: 0% 50%;
}
.csstransformspreserve3d figure.photostack-flip .photostack-back {
-webkit-transform: rotateY(180deg);
transform: rotateY(180deg);
display: block;
}
.no-csstransformspreserve3d figure.photostack-showback .photostack-back {
display: block;
}
.no-js .photostack figure {
box-shadow: -2px 2px 0 rgba(0,0,0,0.05);
}
.no-js .photostack figure::after {
display: none;
}
.no-js .photostack figure:nth-child(3n) {
-webkit-transform: translateX(-10%) rotate(5deg);
transform: translateX(-10%) rotate(5deg);
}
.no-js .photostack figure:nth-child(3n-2) {
-webkit-transform: translateY(10%) rotate(-3deg);
transform: translateY(10%) rotate(-3deg);
}
#photostack-1 nav span.current {
background: #888;
-webkit-transform: scale(0.61);
transform: scale(0.61);
}
#leftArrowGallery {
margin-right: 10px;
}
#rightArrowGallery {
margin-left: 10px;
}
#rightArrowGallery i {
padding-left: 5px;
}
#leftArrowGallery i {
padding-right: 5px;
}
.btnGallery {
background-color: #da2c33;
border: medium none;
color: #FFFFFF;
cursor: pointer;
display: inline-block;
font: 12px;
padding: 3px 6px;
text-decoration: none;
white-space: nowrap;
border-radius: 5px;
}
.btnGallery:hover {
background-color: #b20a11;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.7.1/modernizr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classie/1.0.1/classie.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" media="all" type="text/css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="photostack-1" class="photostack photostack-start">
<div>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/5.jpg" alt="img05"/>
<figcaption>
<h2 class="photostack-title">Speed Racer</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/2.jpg" alt="img02"/>
<figcaption>
<h2 class="photostack-title">Happy Days</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/3.jpg" alt="img03"/>
<figcaption>
<h2 class="photostack-title">Beautywood</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/1.jpg" alt="img01"/>
<figcaption>
<h2 class="photostack-title">Serenity Beach</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/4.jpg" alt="img04"/>
<figcaption>
<h2 class="photostack-title">Heaven of time</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/6.jpg" alt="img06"/>
<figcaption>
<h2 class="photostack-title">Forever this</h2>
</figcaption>
</figure>
<figure>
<img src="https://tympanus.net/Development/ScatteredPolaroidsGallery/img/7.jpg" alt="img07"/>
<figcaption>
<h2 class="photostack-title">Lovely Green</h2>
</figcaption>
</figure>
</div>
<div id="imageControls">
<button id="leftArrowGallery" class="btnGallery"><i class="fa fa-chevron-left"></i></button>
<button id="rightArrowGallery" class="btnGallery"><i class="fa fa-chevron-right"></i></button>
</div>
</section>

Related

Javascript CSS transition end firing at start of transition

I'm trying to build a simple slideshow where an image slides out for two seconds whilst another image appears beneath it.
(function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
})(init);
function init() {
var pictureCollection = document.getElementsByClassName('slideshowimg');
var pictureTracker = document.getElementsByClassName('trackPoint');
var count = 0;
//load initial pic
pictureCollection[count].style.animation = 'fadein10 0.1s forwards';
pictureTracker[count].style.backgroundColor = 'black';
//Fade
var fadetime = setInterval(function() {
fading()
}, 5000);
function fading() {
pictureCollection[count].style.animation = 'fadeout10 2s forwards';
pictureTracker[count].style.backgroundColor = 'gray';
if (count == pictureCollection.length - 1) {
count = -1;
}
count++;
pictureCollection[count].style.animation = 'fadein10 2s forwards';
pictureTracker[count].style.backgroundColor = 'black';
}
function fixer(direction) {
//remove event listener to ensure it doesn't reset EVERY transition
event.target.removeEventListener('transitionend', fixer);
pictureCollection[count].style.animation = 'fadeout10 0.1s forwards';
pictureCollection[count].style.transform = 'translateX(0px)';
//reset counter
if (count === 3 && direction === "right") {
count = 0;
return;
}
if (count === 0 && direction === "left") {
count = 3;
return;
}
if (direction === "right") {
count++;
}
if (direction === "left") {
count--;
}
}
//add event listener to handle user clicks to each lside
for (i = 0; i < pictureCollection.length; i++) {
pictureCollection[i].addEventListener('click', function() {
//cancel autofade
clearInterval(fadetime);
//check where on pic user clicked
if (event.clientX > event.target.width / 2) {
event.target.style.animation = 'nofade10 0.1s forwards';
pictureTracker[count].style.backgroundColor = 'gray';
if (count === 3) {
pictureCollection[0].style.animation = 'fadein5 0.1s forwards';
pictureTracker[0].style.backgroundColor = 'black';
} else {
//bring next pic forwards and unhide
pictureCollection[count + 1].style.animation = 'fadein5 0.1s forwards';
pictureTracker[count + 1].style.backgroundColor = 'black';
}
//slide out right
event.target.style.transform = 'translateX(250px)';
//ensure that when the picture slides out it repositions behind
pictureCollection[count].addEventListener('transitionend', fixer('right'));
}
//or going left . . .
else {
pictureCollection[count].style.animation = 'nofade10 0.1s forwards';
pictureTracker[count].style.backgroundColor = 'gray';
if (count === 0) {
pictureCollection[3].style.animation = 'fadein5 0.1s forwards';
pictureTracker[3].style.backgroundColor = 'black';
} else {
//bring next pic forwards and unhide
pictureCollection[count - 1].style.animation = 'fadein5 0.1s forwards';
pictureTracker[count - 1].style.backgroundColor = 'black';
}
//slide out left
event.target.style.transform = 'translateX(-' + pictureCollection[count].width + 'px)';
//ensure that when the picture slides out it repositions behind
pictureCollection[count].addEventListener('transitionend', fixer("left"), false);
}
});
}
}
.slideshowimg {
height: 100%;
width: 100%;
position: inherit;
z-index: 0;
opacity: 0;
transition: all 2s ease-in-out;
}
#sliderCase {
overflow: hidden;
position: absolute;
z-index: 5;
background-color: black;
height: 150px;
width: 225px;
}
.trackPoint {
height: 10px;
width: 10px;
margin: 0 2px;
background-color: gray;
display: inline-block;
border-radius: 2em;
text-align: center;
position: relative;
top: 160px;
left: 75px;
}
#keyframes fadein10 {
from {
opacity: 0;
}
to {
opacity: 1;
z-index: 10;
}
}
#keyframes fadeout10 {
from {
opacity: 1;
}
to {
opacity: 0;
z-index: 0;
}
}
#keyframes fadein5 {
from {
opacity: 0;
}
to {
opacity: 1;
z-index: 5;
}
}
#keyframes nofade10 {
from {
opacity: 1;
}
to {
opacity: 1;
z-index: 10;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf8'>
<script src='mainedit.js'></script>
<link rel='stylesheet' href='stylesedit.css' type='text/css'></link>
<title>Slideshow test</title>
</head>
<body>
<h1> Slideshow test: </h1>
<div id='sliderCase'>
<div style='background-color:red' class='slideshowimg'>pic 1</div>
<div style='background-color:blue' class='slideshowimg'>pic 2</div>
<div style='background-color:green' class='slideshowimg'>pic 3</div>
<div style='background-color:orange' class='slideshowimg'>pic 4</div>
</div>
<span class='trackPoint'></span>
<span class='trackPoint'></span>
<span class='trackPoint'></span>
<span class='trackPoint'></span>
</body>
</html>
It seems as if the transition end event is firing as the transition starts. Additionally the movement of slides seems to be inverted in the snippet, yet works locally.
I have used coloured divs in place of images for the sake of this example.
This isn't correct:
pictureCollection[count].addEventListener('transitionend', fixer("left"), false);
Here, you are calling and adding the return value of fixer("left") to the event listener, which isn't a valid value anyway.

Trying to add an additional title animation to my jquery slideshow

So I downloaded a slideshow for my home page. It comes with 2 animated text titles (H2 and H3) that slide in from the left of the screen to center. I am now trying to add an additional line of text (H4) that should slide in from the right... with a few seconds delay.
I already edited the webpage and .css file accordingly, but the javascript I am not too sure I know exactly what to do, and I don't wanna mess it up all together.
I will paste here part of the js file where the title animation is being handled (if necessary I also stick the whole file at the bottom of this post).
// titles animation
$nextSlide.find('div.ei-title > h2')
.css( 'margin-right', 50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 },
this.options.titlespeed, this.options.titleeasing )
.end()
.find('div.ei-title > h3')
.css( 'margin-right', -50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 },
this.options.titlespeed, this.options.titleeasing )
And here's the css file (already edited to include the added text as 'H4'):
.ei-slider{
position: relative;
width: 100%;
max-width: 1999px;
height: 500px;
margin: 0 auto;
}
.ei-slider-loading{
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
z-index:999;
background: rgba(0,0,0,0.9);
color: #fff;
text-align: center;
line-height: 400px;
}
.ei-slider-large{
height: 100%;
width: 100%;
position:relative;
overflow: hidden;
}
.ei-slider-large li{
position: absolute;
top: 20px;
left: 0px;
overflow: hidden;
height: 100%;
width: 100%;
}
.ei-slider-large li img{
width: 100%;
}
.ei-title h4{
position: absolute;
left: 50%;
margin-right: 13%;
top: 30%;
}
.ei-title h2, h3{
position: absolute;
right: 50%;
margin-right: 13%;
top: 30%;
}
.ei-title h2, .ei-title h3{
text-align: right;
}
.ei-title h4 {
text-align: left;
}
.ei-title h2{
font-size: 40px;
line-height: 50px;
font-family: 'Playfair Display', serif;
font-style: italic;
color: #b5b5b5;
}
.ei-title h3{
font-size: 20px;
line-height: 20px;
font-family: 'Open Sans Condensed', sans-serif;
text-transform: uppercase;
color: #000;
}
.ei-title h4{
font-size: 10px;
line-height: 20px;
font-family: 'sans-serif'; italic;
text-transform: uppercase;
color: #f0ffff;
}
/* the thumbs... */
.ei-slider-thumbs{
height: 1px;
margin: 0 auto;
position: relative;
}
.ei-slider-thumbs li{
position: relative;
float: left;
height: 10%;
}
.ei-slider-thumbs li.ei-slider-element{
top: 0px;
left: 0px;
position: absolute;
height: 10%;
z-index: 10;
text-indent: -9000px;
background: #000;
background: rgba(0,0,0,0.9);
}
.ei-slider-thumbs li a{
display: block;
text-indent: -9000px;
background: #666 ;
width: 100%;
height: 100%;
cursor: pointer;
-webkit-box-shadow:
0px 1px 1px 0px rgba(0,0,0,0.3),
0px 1px 0px 1px rgba(255,255,255,0.5);
-moz-box-shadow:
0px 1px 1px 0px rgba(0,0,0,0.3),
0px 1px 0px 1px rgba(255,255,255,0.5);
box-shadow:
0px 1px 1px 0px rgba(0,0,0,0.3),
0px 1px 0px 1px rgba(255,255,255,0.5);
-webkit-transition: background 0.2s ease;
-moz-transition: background 0.2s ease;
-o-transition: background 0.2s ease;
-ms-transition: background 0.2s ease;
transition: background 0.2s ease;
}
.ei-slider-thumbs li a:hover{
background-color: #f0f0f0;
}
.ei-slider-thumbs li img{
position: absolute;
bottom: 10px;
opacity: 0;
z-index: 999;
max-width: 100%;
-webkit-transition: all 0.4s ease;
-moz-transition: all 0.4s ease;
-o-transition: all 0.4s ease;
-ms-transition: all 0.4s ease;
transition: all 0.4s ease;
-webkit-box-reflect:
below 0px -webkit-gradient(
linear,
left top,
left bottom,
from(transparent),
color-stop(50%, transparent),
to(rgba(255,255,255,0.3))
);
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
}
.ei-slider-thumbs li:hover img{
opacity: -1;
bottom: -1px;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
}
#media screen and (max-width: 830px) {
.ei-title{
position: absolute;
right: 0px;
margin-right: 0px;
width: 100%;
text-align: center;
top: auto;
bottom: 10px;
background: #fff;
background: rgba(255,255,255,0.9);
padding: 5px 0;
}
.ei-title h2, .ei-title h3, .ei-title h4 {
text-align: center;
}
.ei-title h2{
font-size: 30px;
line-height: 24px;
}
.ei-title h3{
font-size: 20px;
line-height: 20px;
}
.ei-title h4{
font-size: 10px;
line-height: 20px;
}
}
And the html/js code on the webpage:
<div class="wrapper">
<div id="ei-slider" class="ei-slider">
<ul class="ei-slider-large">
<li>
<img src="uploads/2016/01/slider-3.jpg" alt="image01">
<div class="ei-title">
<h2>High end Management Tools</h2>
<h3>Available at your fingertips!</h3>
<h4>With the available technologies, we provide the methods and tools that will help society to excel to new heights</h4>
</div>
</li>
<li>
<img src="uploads/2016/01/about-1.jpg" alt="image02" />
<div class="ei-title">
<h2>Enterprises, Administration Managers</h2>
<h3>Improve your Performance!</h3>
<h4>Regardless of your position, we have the solutions for all levels of professionals, and students alike.... </h4>
</div>
</li>
<li>
<img src="uploads/2016/01/home-parallax.jpg" alt="image03" />
<div class="ei-title">
<h2>Stunning Solutions</h2>
<h3>For Remarkable results!</h3>
<h4>We are bringing today's technology to assist the traditional family with promising results.</h4>
</div>
</li>
</ul><!-- ei-slider-large -->
</div><!-- ei-slider -->
</div><!-- wrapper -->
</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.eislideshow.js"></script>
<script type="text/javascript" src="js/jquery.easing.1.3.js"></script>
<script type="text/javascript">
$(function() {
$('#ei-slider').eislideshow({
animation : 'center',
autoplay : true,
slideshow_interval : 6000,
titlesFactor : 0
});
});
</script>
Oh, and finally, the entire javascript file:
(function( window, $, undefined ) {
/*
* smartresize: debounced resize event for jQuery
*
* latest version and complete README available on Github:
* https://github.com/louisremi/jquery.smartresize.js
*
* Copyright 2011 #louis_remi
* Licensed under the MIT license.
*/
var $event = $.event, resizeTimeout;
$event.special.smartresize = {
setup: function() {
$(this).bind( "resize", $event.special.smartresize.handler );
},
teardown: function() {
$(this).unbind( "resize", $event.special.smartresize.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments;
// set correct event type
event.type = "smartresize";
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
resizeTimeout = setTimeout(function() {
jQuery.event.handle.apply( context, args );
}, execAsap === "execAsap"? 0 : 100 );
}
};
$.fn.smartresize = function( fn ) {
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
};
$.Slideshow = function( options, element ) {
this.$el = $( element );
/***** images ****/
// list of image items
this.$list = this.$el.find('ul.ei-slider-large');
// image items
this.$imgItems = this.$list.children('li');
// total number of items
this.itemsCount = this.$imgItems.length;
// images
this.$images = this.$imgItems.find('img:first');
/***** thumbs ****/
// thumbs wrapper
this.$sliderthumbs = this.$el.find('ul.ei-slider-thumbs').hide();
// slider elements
this.$sliderElems = this.$sliderthumbs.children('li');
// sliding div
this.$sliderElem = this.$sliderthumbs.children('li.ei-slider-element');
// thumbs
this.$thumbs = this.$sliderElems.not('.ei-slider-element');
// initialize slideshow
this._init( options );
};
$.Slideshow.defaults = {
// animation types:
// "sides" : new slides will slide in from left / right
// "center": new slides will appear in the center
animation : 'sides', // sides || center
// if true the slider will automatically slide, and it will only stop if the user clicks on a thumb
autoplay : false,
// interval for the slideshow
slideshow_interval : 3000,
// speed for the sliding animation
speed : 1000,
// easing for the sliding animation
easing : '',
// percentage of speed for the titles animation. Speed will be speed * titlesFactor
titlesFactor : 0.60,
// titles animation speed
titlespeed : 900,
// titles animation easing
titleeasing : '',
// maximum width for the thumbs in pixels
thumbMaxWidth : 150
};
$.Slideshow.prototype = {
_init : function( options ) {
this.options = $.extend( true, {}, $.Slideshow.defaults, options );
// set the opacity of the title elements and the image items
this.$imgItems.css( 'opacity', 0 );
this.$imgItems.find('div.ei-title > *').css( 'opacity', 0 );
// index of current visible slider
this.current = 0;
var _self = this;
// preload images
// add loading status
this.$loading = $('<div class="ei-slider-loading">Loading</div>').prependTo( _self.$el );
$.when( this._preloadImages() ).done( function() {
// hide loading status
_self.$loading.hide();
// calculate size and position for each image
_self._setImagesSize();
// configure thumbs container
_self._initThumbs();
// show first
_self.$imgItems.eq( _self.current ).css({
'opacity' : 1,
'z-index' : 10
}).show().find('div.ei-title > *').css( 'opacity', 1 );
// if autoplay is true
if( _self.options.autoplay ) {
_self._startSlideshow();
}
// initialize the events
_self._initEvents();
});
},
_preloadImages : function() {
// preloads all the large images
var _self = this,
loaded = 0;
return $.Deferred(
function(dfd) {
_self.$images.each( function( i ) {
$('<img/>').load( function() {
if( ++loaded === _self.itemsCount ) {
dfd.resolve();
}
}).attr( 'src', $(this).attr('src') );
});
}
).promise();
},
_setImagesSize : function() {
// save ei-slider's width
this.elWidth = this.$el.width();
var _self = this;
this.$images.each( function( i ) {
var $img = $(this);
imgDim = _self._getImageDim( $img.attr('src') );
$img.css({
width : imgDim.width,
height : imgDim.height,
marginLeft : imgDim.left,
marginTop : imgDim.top
});
});
},
_getImageDim : function( src ) {
var $img = new Image();
$img.src = src;
var c_w = this.elWidth,
c_h = this.$el.height(),
r_w = c_h / c_w,
i_w = $img.width,
i_h = $img.height,
r_i = i_h / i_w,
new_w, new_h, new_left, new_top;
if( r_w > r_i ) {
new_h = c_h;
new_w = c_h / r_i;
}
else {
new_h = c_w * r_i;
new_w = c_w;
}
return {
width : new_w,
height : new_h,
left : ( c_w - new_w ) / 2,
top : ( c_h - new_h ) / 2
};
},
_initThumbs : function() {
// set the max-width of the slider elements to the one set in the plugin's options
// also, the width of each slider element will be 100% / total number of elements
this.$sliderElems.css({
'max-width' : this.options.thumbMaxWidth + 'px',
'width' : 100 / this.itemsCount + '%'
});
// set the max-width of the slider and show it
this.$sliderthumbs.css( 'max-width', this.options.thumbMaxWidth * this.itemsCount + 'px' ).show();
},
_startSlideshow : function() {
var _self = this;
this.slideshow = setTimeout( function() {
var pos;
( _self.current === _self.itemsCount - 1 ) ? pos = 0 : pos = _self.current + 1;
_self._slideTo( pos );
if( _self.options.autoplay ) {
_self._startSlideshow();
}
}, this.options.slideshow_interval);
},
// shows the clicked thumb's slide
_slideTo : function( pos ) {
// return if clicking the same element or if currently animating
if( pos === this.current || this.isAnimating )
return false;
this.isAnimating = true;
var $currentSlide = this.$imgItems.eq( this.current ),
$nextSlide = this.$imgItems.eq( pos ),
_self = this,
preCSS = {zIndex : 10},
animCSS = {opacity : 1};
// new slide will slide in from left or right side
if( this.options.animation === 'sides' ) {
preCSS.left = ( pos > this.current ) ? -1 * this.elWidth : this.elWidth;
animCSS.left = 0;
}
// titles animation
$nextSlide.find('div.ei-title > h2')
.css( 'margin-right', 50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
.end()
.find('div.ei-title > h3')
.css( 'margin-right', -50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
$.when(
// fade out current titles
$currentSlide.css( 'z-index' , 1 ).find('div.ei-title > *').stop().fadeOut( this.options.speed / 2, function() {
// reset style
$(this).show().css( 'opacity', 0 );
}),
// animate next slide in
$nextSlide.css( preCSS ).stop().animate( animCSS, this.options.speed, this.options.easing ),
// "sliding div" moves to new position
this.$sliderElem.stop().animate({
left : this.$thumbs.eq( pos ).position().left
}, this.options.speed )
).done( function() {
// reset values
$currentSlide.css( 'opacity' , 0 ).find('div.ei-title > *').css( 'opacity', 0 );
_self.current = pos;
_self.isAnimating = false;
});
},
_initEvents : function() {
var _self = this;
// window resize
$(window).on( 'smartresize.eislideshow', function( event ) {
// resize the images
_self._setImagesSize();
// reset position of thumbs sliding div
_self.$sliderElem.css( 'left', _self.$thumbs.eq( _self.current ).position().left );
});
// click the thumbs
this.$thumbs.on( 'click.eislideshow', function( event ) {
if( _self.options.autoplay ) {
clearTimeout( _self.slideshow );
_self.options.autoplay = false;
}
var $thumb = $(this),
idx = $thumb.index() - 1; // exclude sliding div
_self._slideTo( idx );
return false;
});
}
};
var logError = function( message ) {
if ( this.console ) {
console.error( message );
}
};
$.fn.eislideshow = function( options ) {
if ( typeof options === 'string' ) {
var args = Array.prototype.slice.call( arguments, 1 );
this.each(function() {
var instance = $.data( this, 'eislideshow' );
if ( !instance ) {
logError( "cannot call methods on eislideshow prior to initialization; " +
"attempted to call method '" + options + "'" );
return;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for eislideshow instance" );
return;
}
instance[ options ].apply( instance, args );
});
}
else {
this.each(function() {
var instance = $.data( this, 'eislideshow' );
if ( !instance ) {
$.data( this, 'eislideshow', new $.Slideshow( options, this ) );
}
});
}
return this;
};
})( window, jQuery );
My slider in action
The jQuery code of your plugin, the first snipped you attached, is looking for each element (h2 and h3) and animating them one after the other.
Just add the same code for the h4 afterwards, and apply any changes you want to the pixel values.
// titles animation
$nextSlide.find('div.ei-title > h2')
.css( 'margin-right', 50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 },
this.options.titlespeed, this.options.titleeasing )
.end()
.find('div.ei-title > h3')
.css( 'margin-right', -50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 },
this.options.titlespeed, this.options.titleeasing )
.end()
.find('div.ei-title > h4')
.css( 'margin-right', -150 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 },
this.options.titlespeed, this.options.titleeasing )
As you can see, I told it to set margin-right to -150px, to apply the same difference between h3 and h4 as there is between h2 and h3, but you can choose the value that suits your needs best.

How to download sketch with quote included

I will try this one more time.
I am having trouble getting the quote to download as part of the sketch canvas. I am using sketch.js.
Here is the jsfiddle, you can draw like MSpaint but I when pressing the download button only what is drawn is copied. I would like the sketch and the words downloaded.
http://jsfiddle.net/e1ovm9mn/
I should also add that I am quite new to all.
Here is the code
<body>
<nav>
<div id="SketchTools">
<!-- Basic tools -->
<img src="img/black_icon.png" alt="Black"/>
<img src="img/red_icon.png" alt="Red"/>
<img src="img/green_icon.png" alt="Green"/>
<img src="img/blue_icon.png" alt="Blue"/>
<img src="img/yellow_icon.png" alt="Yellow"/>
<img src="img/cyan_icon.png" alt="Cyan"/>
<!-- Advanced colors -->
<img src="img/alizarin_icon.png" alt="Alizarin"/>
<img src="img/pomegrante_icon.png" alt="Pomegrante"/>
<img src="img/emerald_icon.png" alt="Emerald"/>
<img src="img/torquoise_icon.png" alt="Torquoise"/>
<img src="img/peterriver_icon.png" alt="Peter River"/>
<img src="img/amethyst_icon.png" alt="Amethyst"/>
<img src="img/sunflower_icon.png" alt="Sun Flower"/>
<img src="img/orange_icon.png" alt="Orange"/>
<img src="img/clouds_icon.png" alt="Clouds"/>
<img src="img/silver_icon.png" alt="Silver"/>
<img src="img/asbestos_icon.png" alt="Asbestos"/>
<img src="img/wetasphalt_icon.png" alt="Wet Asphalt"/>
</br> <img src="img/eraser_icon.png" alt="Eraser"/>
<!-- Size options -->
<img src="img/pencil_icon.png" alt="Pencil"/>
<img src="img/pen_icon.png" alt="Pen"/>
<img src="img/stick_icon.png" alt="Stick"/>
<img src="img/smallbrush_icon.png" alt="Small brush"/>
<img src="img/mediumbrush_icon.png" alt="Medium brush"/>
<img src="img/bigbrush_icon.png" alt="Big brush"/>
<img src="img/bucket_icon.png" alt="Huge bucket"/>
Download
<br/>
</div>
<div class="links">
<ul>
<li><img src="ficon.png" alt="Facebook"></li>
<li><img src="igramicon.png" alt="Instagram"></li>
<li><img src="picon.png" alt="Pinterest"></li>
<li><img src="mcicon.png" alt="Mixcloud"></li>
<li><img src="twicon.png" alt="Twitter"></li>
</ul>
</div>
<div class="message">
<div id="quote"></div>
<script>
(function() {
var quotes = [
{ text: "Snuggletooth likes pancakes"},
{ text: "Would you like Snuggletooth to tuck you in?"},
{ text: " Snuggletooth loves you"},
{ text: "Snuggletooth is here for you"},
{ text: "Did you know that Snuggletooth </br>can be in 2 places at once?"},
{ text: "Heyyyy!<br> I was just thinking about you </br>Love Snuggletooth" },
{ text: "Wanna Sandwich??</br>xSnuggletooth"},
{ text: "Want some breakfast???</br> ;) Snuggletooth"},
{ text: "Snuggletooth-a-riffic!!!"},
{ text: "Snuggletooth makes great popcorn!"},
{ text: "Come over to Snuggletooth's! He makes a great guacamole!"},
{ text: "Snuggletooth likes his bubblebaths to smell like bubblegum"},
{ text: "Snuggletooth wants to know what are you up to later?"},
{ text: "Snuggletooth-a-licious!!!"},
];
var quote = quotes[Math.floor(Math.random() * quotes.length)];
document.getElementById("quote").innerHTML =
'<p>' + quote.text + '</p>' +
'' + '';
})();
</script>
</div>
</nav>
<canvas id="SketchPad" width="1125" height="600">
</canvas>
</div>
<script type="text/javascript">
$(function() {
$('#SketchPad').sketch();
});
</script>
</body>
And here is the sketch.js
var __slice = Array.prototype.slice;
(function($) {
var Sketch;
$.fn.sketch = function() {
var args, key, sketch;
key = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (this.length > 1) {
$.error('Sketch.js can only be called on one element at a time.');
}
sketch = this.data('sketch');
if (typeof key === 'string' && sketch) {
if (sketch[key]) {
if (typeof sketch[key] === 'function') {
return sketch[key].apply(sketch, args);
} else if (args.length === 0) {
return sketch[key];
} else if (args.length === 1) {
return sketch[key] = args[0];
}
} else {
return $.error('Sketch.js did not recognize the given command.');
}
} else if (sketch) {
return sketch;
} else {
this.data('sketch', new Sketch(this.get(0), key));
return this;
}
};
Sketch = (function() {
function Sketch(el, opts) {
this.el = el;
this.canvas = $(el);
this.context = el.getContext('2d');
this.options = $.extend({
toolLinks: true,
defaultTool: 'marker',
defaultColor: 'black',
defaultSize: 5
}, opts);
this.painting = false;
this.color = this.options.defaultColor;
this.size = this.options.defaultSize;
this.tool = this.options.defaultTool;
this.actions = [];
this.action = [];
this.canvas.bind('click mousedown mouseup mousemove mouseleave mouseout touchstart touchmove touchend touchcancel', this.onEvent);
if (this.options.toolLinks) {
$('body').delegate("a[href=\"#" + (this.canvas.attr('id')) + "\"]", 'click', function(e) {
var $canvas, $this, key, sketch, _i, _len, _ref;
$this = $(this);
$canvas = $($this.attr('href'));
sketch = $canvas.data('sketch');
_ref = ['color', 'size', 'tool'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
if ($this.attr("data-" + key)) {
sketch.set(key, $(this).attr("data-" + key));
}
}
if ($(this).attr('data-download')) {
sketch.download($(this).attr('data-download'));
}
return false;
});
}
}
Sketch.prototype.download = function(format) {
var mime;
format || (format = "png");
if (format === "jpg") {
format = "jpeg";
}
mime = "image/" + format;
return window.open(this.el.toDataURL(mime));
};
Sketch.prototype.set = function(key, value) {
this[key] = value;
return this.canvas.trigger("sketch.change" + key, value);
};
Sketch.prototype.startPainting = function() {
this.painting = true;
return this.action = {
tool: this.tool,
color: this.color,
size: parseFloat(this.size),
events: []
};
};
Sketch.prototype.stopPainting = function() {
if (this.action) {
this.actions.push(this.action);
}
this.painting = false;
this.action = null;
return this.redraw();
};
Sketch.prototype.onEvent = function(e) {
if (e.originalEvent && e.originalEvent.targetTouches) {
e.pageX = e.originalEvent.targetTouches[0].pageX;
e.pageY = e.originalEvent.targetTouches[0].pageY;
}
$.sketch.tools[$(this).data('sketch').tool].onEvent.call($(this).data('sketch'), e);
e.preventDefault();
return false;
};
Sketch.prototype.redraw = function() {
var sketch;
this.el.width = this.canvas.width();
this.context = this.el.getContext('2d');
sketch = this;
$.each(this.actions, function() {
if (this.tool) {
return $.sketch.tools[this.tool].draw.call(sketch, this);
}
});
if (this.painting && this.action) {
return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
}
};
return Sketch;
})();
$.sketch = {
tools: {}
};
$.sketch.tools.marker = {
onEvent: function(e) {
switch (e.type) {
case 'mousedown':
case 'touchstart':
this.startPainting();
break;
case 'mouseup':
case 'mouseout':
case 'mouseleave':
case 'touchend':
case 'touchcancel':
this.stopPainting();
}
if (this.painting) {
this.action.events.push({
x: e.pageX - this.canvas.offset().left,
y: e.pageY - this.canvas.offset().top,
event: e.type
});
return this.redraw();
}
},
draw: function(action) {
var event, previous, _i, _len, _ref;
this.context.lineJoin = "round";
this.context.lineCap = "round";
this.context.beginPath();
this.context.moveTo(action.events[0].x, action.events[0].y);
_ref = action.events;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
event = _ref[_i];
this.context.lineTo(event.x, event.y);
previous = event;
}
this.context.strokeStyle = action.color;
this.context.lineWidth = action.size;
return this.context.stroke();
}
};
return $.sketch.tools.eraser = {
onEvent: function(e) {
return $.sketch.tools.marker.onEvent.call(this, e);
},
draw: function(action) {
var oldcomposite;
oldcomposite = this.context.globalCompositeOperation;
this.context.globalCompositeOperation = "copy";
action.color = "rgba(0,0,0,0)";
$.sketch.tools.marker.draw.call(this, action);
return this.context.globalCompositeOperation = oldcomposite;
}
};
})(jQuery);
And just in case here is the css
#import url(http://fonts.googleapis.com/css?family=Codystar);
#import url(http://fonts.googleapis.com/css?family=Lobster);
img {
width: 25px;
height: 25px;
}
li{
}
ul li {
list-style-type: none;
display: inline;
}
.message {
margin-left: 20%;
margin-top: 20%;
z-index: 1;
position: absolute;
font-family: Lobster, Codystar, arial;
font-size: 3.5em;
color:white;
text-align: center;
text-shadow: 2px 2px #ff0000;
}
.links {
float: right;
padding-right: 1em;
}
#SketchPad {
border-color: black;
border-width: 3px;
border-style: solid;
position: fixed;
}
#DownloadPng {
padding: 4px 2px;
font-size: 1em;
line-height: 100%;
text-shadow: 0 1px rgba(0, 0, 0, 0.5);
color: #fff;
display:inline-block;
/*vertical-align: middle;
text-align: center;*/
cursor: pointer;
font-weight: bold;
transition: background 0.1s ease-in-out;
-webkit-transition: background 0.1s ease-in-out;
-moz-transition: background 0.1s ease-in-out;
-ms-transition: background 0.1s ease-in-out;
-o-transition: background 0.1s ease-in-out;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
color: #fff;
font-family: Arial, sans-serif;
background-color: #2ecc71;
box-shadow: 0px 7px 0px 0px #27ae60;
text-decoration: none;
margin-top: 10px;
margin-bottom: 15px;
}
#DownloadPng:hover {
background-color: #27ae60;
border-radius: 7px;
}
#DownloadPng:active {
box-shadow: 0px 1px 0px 0px #27ae60;
border-radius: 7px;
}
#SketchTools {
width: 65%;
height: 5%;
position: fixed;
float: left;
z-index: 1;
padding: .2em;
}

How to implement a jQuery plugin in my server?

I'm new to jQuery and web development. I have succesfully implemented other jQuery plugins like data tables or a simple sliders.
I'm having some problems when trying to make this run:
http://jsfiddle.net/KurtWM/pQnPg/
I know that is a must to initialize my code so I did the following:
<script type="text/javascript" language="javascript" class="init">
$(document).ready(function() {
$('#survey1').numericScale();
} );
</script>
I copied the js part exactly as it is from the provided link and uploaded to my server with the name:
jquery.numericScale.js
I have included jQuery and this plugin in the following way:
<script type="text/javascript" language="javascript" src="media/js/jquery.dataTables.js"></script>
<script type="text/javascript" language="javascript" src="media/js/jquery.numericScale.js"></script>
Regarding the HTML part I just copied it into my HTML body.
I really don't have a clue of what could I be doing wrong.
Try removing this from the end of your jquery.numericScale.js file:
var disciplines = $('#survey1').numericScale({
'responseRange' : 5,
'lowOpinionAnswer' : 'Least like me',
'highOpinionAnswer' : 'Most like me'
});
console.dir(disciplines);
Add this to your html page after your loaded plugins:
<script>
$('#survey1').numericScale({
'responseRange' : 5,
'lowOpinionAnswer' : 'Least like me',
'highOpinionAnswer' : 'Most like me'
});
</script>
This should work for you
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.survey-wrapper
{
position: relative;
display: table;
width: 100%; /*height: 500px;*/
max-width: 640px;
border-collapse: separate !important;
border-spacing: 1px !important;
}
ol.survey
{
list-style: decimal; /*margin-top: 160px;*/
list-style-position: inside;
}
ol.survey > li:last-child
{
border-bottom: 1px solid #CDCDCD;
}
ol.survey li
{
padding-left: -20px;
border-top: 1px solid #CDCDCD;
border-right: 1px solid #CDCDCD;
border-left: 1px solid #CDCDCD;
}
ol.survey li.alt, ol.survey li:nth-child(even)
{
background-color: #E8E8E4;
}
.scores > div
{
background: #E8E8E4;
}
.scores div.alt, .scores > div:nth-child(even)
{
background-color: #E8E8E4;
}
ol.survey li .opinion-question
{
margin-bottom: 0.5em;
font-weight: bold;
}
ol.survey li
{
padding-top: 6px;
padding-bottom: 1px;
padding-left: 12px;
}
ol.survey li .opinion-responses
{
display: table;
width: 100%;
margin-bottom: 1.0em;
}
ol.survey li .opinion-responses .bipolar-adjective
{
display: table-cell;
width: 25%;
text-align: center;
vertical-align: middle;
font-style: italic;
}
ol.survey li .opinion-responses .response-choice
{
display: table-cell;
width: 10px;
text-align: center;
vertical-align: middle;
}
ol.survey li .opinion-responses .response-choice input[type=radio], ol.survey li .opinion-responses .response-choice input.radio
{
}
.scores
{
width: 100%;
height: 400px;
position: relative;
}
.scores .discipline
{
display: block;
position: absolute;
bottom: 0;
text-align: center;
background: #E8E8E4 url(../images/gifts_orange.png) no-repeat 0 0;
border: 1px solid #FFFFFF;
}
.scores .discipline .discipline-name
{
text-align: center;
position: relative;
bottom: 24px;
z-index: 200;
font-family: "Futura Lt BT" , helvetica, sans-serif;
}
.scores .discipline .discipline-total
{
text-align: center;
display: block;
font-weight: bold;
font-size: 150%;
font-family: "Futura Md BT" , helvetica, sans-serif;
margin-top: 0px;
}
.scores .selected
{
background: #1047a9 url(../images/gifts_blue.png) no-repeat 0 0 !important;
}
.scores .selected .discipline-total
{
color: #FFFFFF !important;
}
.box
{
position: relative;
width: 60%;
background: #ddd;
-moz-border-radius: 4px;
border-radius: 4px;
padding: 2em 1.5em;
color: rgba(0,0,0, .8);
text-shadow: 0 1px 0 #fff;
line-height: 1.5;
margin: 60px auto;
}
.box:before, .box:after
{
z-index: -1;
position: absolute;
content: "";
bottom: 15px;
left: 10px;
width: 50%;
top: 80%;
max-width:300px;
background: rgba(0, 0, 0, 0.7);
-webkit-box-shadow: 0 15px 10px rgba(0,0,0, 0.7);
-moz-box-shadow: 0 15px 10px rgba(0, 0, 0, 0.7);
box-shadow: 0 15px 10px rgba(0, 0, 0, 0.7);
-webkit-transform: rotate(-3deg);
-moz-transform: rotate(-3deg);
-o-transform: rotate(-3deg);
-ms-transform: rotate(-3deg);
transform: rotate(-3deg);
}
.box:after
{
-webkit-transform: rotate(3deg);
-moz-transform: rotate(3deg);
-o-transform: rotate(3deg);
-ms-transform: rotate(3deg);
transform: rotate(3deg);
right: 10px;
left: auto;
}
.rotate
{
/* Safari */
-webkit-transform: rotate(-90deg); /* Firefox */
-moz-transform: rotate(-90deg); /* IE -ms-transform: rotate(-90deg); */ /* Opera */
-o-transform: rotate(-90deg);
}
</style>
</head>
<body>
<form>
<ol id="survey1" class="survey">
<li class="question" title="Prophecy">When a situation needs to be corrected I feel a burden to speak up about it in order to correct it.</li>
<li class="question" title="Shepherd">I feel a special concern for less mature Christians and feel compelled to care for them spiritually.</li>
<li class="question" title="Teaching">I find it easy and enjoyable to spend time in intensive Bible study.</li>
<li class="question" title="Encouraging">I am able to help others identify problems and offer solutions.</li>
<li class="question" title="Giving">I don't understand why others don't give as much and as freely as I do.</li>
<li class="question" title="Mercy">I am comfortable visiting people who are sick and disabled.</li>
<li class="question" title="Evangelism">I have greater desire than most to witness to non-Christians.</li>
<li class="question" title="Administration">If there is no leadership in a group I will step up and take charge.</li>
<li class="question" title="Serving">I enjoy being called upon to do special jobs around the church.</li>
<li class="question" title="Prophecy">When issues are being dealt with in a group, I speak up rather than just listening.</li>
<li class="question" title="Shepherd">I find myself especially concerned that newer Christians will be influenced by false teachers and be harmed in their spiritual growth as a result. </li>
<li class="question" title="Teaching">Others sometimes accuse me of being too technical or detail-oriented. </li>
<li class="question" title="Encouraging">I would rather talk personally with someone rather than refer them elsewhere. </li>
<li class="question" title="Giving">I find myself looking for opportunities to give my money without being asked to give. </li>
<li class="question" title="Mercy">I have a tendency to think about things for a while before making a decision. </li>
<li class="question" title="Evangelism">Witnessing to non-Christians comes easily to me. </li>
<li class="question" title="Administration">I enjoy handling the details of organizing ideas, people, resources, and time in order to have more effective ministry. </li>
<li class="question" title="Serving">I feel that I am not specifically skilled, but I enjoy doing what needs to be done around the church. </li>
</ol>
</form>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
(function( $ ) {
$.fn.extend({
numericScale: function(options) {
var defaults = {
responseRange: 5,
topNumber: 3,
lowOpinionAnswer: 'Lowest',
highOpinionAnswer: 'Highest'
};
var scores = []; // Array to hold scores
var key; // HTML5 localStorage key
var disciplines = []; // Array to hold disciplines
var aHighVals = []; // Array to hold n highest scores
var options = $.extend(defaults, options);
// Act on every target list of the plugin.
return this.each(function() {
var $list = $(this);
key = $list.attr('id') + "_key";
// Replace list items with survey controls.
$($list).children().each(function(index) {
createQuestion($list, $(this), index);
}).filter(':odd').addClass('alt');
// Create HTML for survey & scores containers and button
$list.wrap('<div id="wrap-' + $list.attr('id') +
'" class="survey-wrapper"></div>');
$list.after('<div id="scores-' + $list.attr('id') +
'" class="scores"></div>');
$list.after('<input type="button" id="submitBtn"' +
' class="button btnStyle" value="Show My Gifts"' +
' disabled="disabled" />');
// Hide scores initially
$('#scores-' + $list.attr('id')).hide();
loadScores();
setSubmitBtnState();
console.dir(scores);
// ====================
// Handler:
// ====================
$('input[type="radio"]').change(function(e) {
// Get the discipline of the question
var discipline = $(e.target).closest('li[class~="question"]').attr('data-discipline');
var qNum = $(e.target).attr('name').substr(1) - 1;
// Replace the question's object property 'value' in the Scores array with the new selection
scores[qNum].value = $(e.target).val();
storeScores();
setSubmitBtnState();
});
// ====================
// Function:
// ====================
function storeScores() {
var jsonScores = JSON.stringify(scores);
localStorage.setItem(key, jsonScores);
}
// ====================
// Function:
// ====================
function setSubmitBtnState() {
if (getFormFinished()) {
$('#submitBtn').removeAttr('disabled');
}
else {
$('#submitBtn').attr('disabled', 'disabled');
}
}
// ====================
// Function:
// ====================
function getFormFinished() {
//var boolFinished = true;
for (var i = 0; i < scores.length; i++) {
if (scores[i].value == 0) {
//boolFinished = false;
return false;
break;
}
}
//return boolFinished;
return true;
}
// ====================
// Function:
// ====================
function createQuestion(oList, oItem, index) {
// Add the 'title' of the list item
var title = oItem.attr('title');
var qName = "q" + (index + 1);
// Create score items in scores Array.
createScore(oItem, title, qName);
var question = "<div class='opinion-question'>" +
oItem.text() + "</div>" +
"<div class='opinion-responses'>" +
"<span class='bipolar-adjective'>" +
defaults.lowOpinionAnswer + "</span>";
// Create a radio button group for each question.
for (var i = 1; i <= defaults.responseRange; ++i) {
question += "<span class='response-choice'>" +
"<input type='radio' name='" + qName +
"' value='" + i + "' class='radio'";
// Create a LocalStorage item for each question.
//check if discipline's localstorage is set.
if (localStorage.getItem(oList.attr('id') + "_" + qName)) {
if (localStorage.getItem(oList.attr('id') + "_" + qName) == i) {
question += " checked='checked'";
}
}
// Add required attribute to first radio button in group to allow validation with the jquery.validate.js plugin.
if (i == 1) {
question += " validate='required:true'";
}
question += " />" + i + "</span>";
}
question += "<span class='bipolar-adjective'>" + defaults.highOpinionAnswer + "</span>" + "</div>";
oItem.empty().prepend(question).attr('data-discipline', oItem.attr('title')).removeAttr('title');
}
// ====================
// Function:
// ====================
function createScore(oItem, d, qName) {
var score = {};
score.question = qName;
score.value = oItem.val();
score.discipline = d;
scores.push(score);
}
// ====================
// Function:
// ====================
function addScoreToPage(score) {
if (replace(scores, score.question, score.value)) {
var scoreUl = document.getElementById('scores-' + $list.attr('id') + '-ul');
var li = document.createElement('li');
li.innerHTML = score.question + '; ' + score.value + '; ' + score.discipline;
if (scoreUl.childElementCount > 0) {
scoreUl.insertBefore(li, scoreUl.firstChild);
} else {
scoreUl.appendChild(li);
}
}
}
// ====================
// Function:
// ====================
function replace(arrayName, replaceTo, replaceWith) {
for (var i = 0; i < arrayName.length; i++) {
if (arrayName[i].question == replaceTo) {
arrayName.splice(i, 1, replaceWith);
return true;
} else {
return false;
}
}
}
// ====================
// Function:
// ====================
function getHighestScores(oItems, num) {
for (var key in oItems) {
var obj = oItems[key];
for (var prop in obj) {
}
}
}
// ====================
// Function:
// ====================
function surveySetup() {
var submitButton = document.getElementById("submitBtn");
submitButton.onclick = submitSurvey;
if (!window.localStorage) {
alert("The Web Storage API is not supported in your browser. You may still submit the form, but your answers will not be saved to your browser.")
} else {
loadScores();
}
}
// ====================
// Handler:
// ====================
$("#submitBtn").click(function() {
if (!window.localStorage) {
alert("The Web Storage API is not supported in your browser. You may still submit the form, but your answers will not be saved to your browser.")
} else {
submitSurvey();
$('html, body').animate({
scrollTop: $("html, body").offset().top
}, 1000);
}
});
// ====================
// Function:
// ====================
function submitSurvey() {
// Create visual elements for scores.
var surveyId = 'div#scores-' + $list.attr('id');
var dNumber = 0;
var dWidth;
var maxHeight = 350;
var tallBarHeight = 0;
$(surveyId).empty();
for (var i = 0; i < scores.length; i++) {
if ($('div#' + scores[i].discipline).length == 0) {
var dScore = tallyDiscipline(scores[i].discipline);
dNumber++;
var discipline = {};
discipline.name = scores[i].discipline;
discipline.value = dScore;
disciplines.push(discipline);
$(surveyId).append("<div id='" + scores[i].discipline + "' class='discipline'><div class='discipline-name'>" + scores[i].discipline + "</div><div class='discipline-total'>" + dScore + "</div>" + "</div>");
if (dScore > tallBarHeight) {
tallBarHeight = dScore;
}
};
$(surveyId).show('fast');
};
//console.dir(disciplines);
//return(disciplines);
dWidth = 100 / dNumber
for (var ii = 0; ii < dNumber; ii++) {
$('.scores .discipline').eq(ii).css({
'left': Math.floor(dWidth) * ii + '%'
});
$('.scores .discipline').eq(ii).css({
'width': (Math.floor(dWidth) - 1) + '%'
});
barHeight = Math.floor((disciplines[ii].value / tallBarHeight) * maxHeight)
$('.scores .discipline').eq(ii).animate({
height: barHeight
}, 2000);
$('.scores .discipline'); //.addClass('box');
};
getHighestValues();
$list.hide();
$('#submitBtn').hide();
$('[id*="btnSaveGifts"]').show();
};
// ====================
// Function:
// ====================
function getHighestValues() {
for (var i = 0; i < disciplines.length; i++) {
aHighVals[i] = [disciplines[i].value, disciplines[i].name];
}
aHighVals.sort(mySorting);
aHighVals.splice(defaults.topNumber, aHighVals.length - defaults.topNumber);
for (var ii = 0; ii < aHighVals.length; ii++) {
$('#' + aHighVals[ii][1]).addClass('selected');
$('input[id*="hdnSelectedVals"]').val($('input[id*="hdnSelectedVals"]').val() + aHighVals[ii][1]);
if (aHighVals.length - 1 > ii) {
$('input[id*="hdnSelectedVals"]').val($('input[id*="hdnSelectedVals"]').val() + ", ");
}
}
}
// ====================
// Function:
// ====================
function mySorting(a, b) {
a = a[0];
b = b[0];
return b == a ? 0 : (b < a ? -1 : 1)
}
// ====================
// Function:
// ====================
function tallyDiscipline(discipline) {
var total = 0;
for (var i = 0; i < scores.length; i++) {
if (scores[i].discipline == discipline) {
total += parseInt(scores[i].value);
}
}
return total;
}
// ====================
// Function:
// ====================
function loadScores() {
var jsonScores = localStorage.getItem(key);
if (jsonScores != null) {
scores = JSON.parse(jsonScores);
for (var i = 0; i < scores.length; i++) {
addScoresToPage(scores[i]);
}
}
}
// ====================
// Function:
// ====================
function addScoresToPage(score) {
$('input:radio[name=' + score.question + '][value=' + score.value + ']').attr('checked', 'checked');
}
});
}
});
})( jQuery );
var disciplines = $('#survey1').numericScale({
'responseRange' : 5,
'lowOpinionAnswer' : 'Least like me',
'highOpinionAnswer' : 'Most like me'
});
console.dir(disciplines);
</script>
</body>
</html>
I've put everything is one file

Thumbnail grid with expanding preview navigation

I have implemented the grid into a site and it's all working great! I have added a previous and next button using the JavaScript file to append them but can't seem to get them to cycle previous and next.
I notice Google's image search you can use the arrow keys to cycle through them but would like to use these buttons to do it. I know you can target the name attribute and cycle through the images but in this particular grid there is only the data-largesrc which I can't seem to target (I can target the img src of the thumbs. The images are in <li> so maybe that could be targeted? Any ideas would be appreciated. The JavaScript file is a bit big...
/*
* debouncedresize: special jQuery event that happens once after a window resize
*
* latest version and complete README available on Github:
* https://github.com/louisremi/jquery-smartresize/blob/master/jquery.debouncedresize.js
*
* Copyright 2011 #louis_remi
* Licensed under the MIT license.
*/
var $jevent = $j.event,
$jspecial,
resizeTimeout;
$jspecial = $jevent.special.debouncedresize = {
setup: function() {
$j( this ).on( "resize", $jspecial.handler );
},
teardown: function() {
$j( this ).off( "resize", $jspecial.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments,
dispatch = function() {
// set correct event type
event.type = "debouncedresize";
$jevent.dispatch.apply( context, args );
};
if ( resizeTimeout ) {
clearTimeout( resizeTimeout );
}
execAsap ?
dispatch() :
resizeTimeout = setTimeout( dispatch, $jspecial.threshold );
},
threshold: 250
};
// ======================= imagesLoaded Plugin ===============================
// https://github.com/desandro/imagesloaded
// $j('#my-container').imagesLoaded(myFunction)
// execute a callback when all images have loaded.
// needed because .load() doesn't work on cached images
// callback function gets image collection as argument
// this is the container
// original: MIT license. Paul Irish. 2010.
// contributors: Oren Solomianik, David DeSandro, Yiannis Chatzikonstantinou
// blank image data-uri bypasses webkit log warning (thx doug jones)
var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
$j.fn.imagesLoaded = function( callback ) {
var $jthis = this,
deferred = $j.isFunction($j.Deferred) ? $j.Deferred() : 0,
hasNotify = $j.isFunction(deferred.notify),
$jimages = $jthis.find('img').add( $jthis.filter('img') ),
loaded = [],
proper = [],
broken = [];
// Register deferred callbacks
if ($j.isPlainObject(callback)) {
$j.each(callback, function (key, value) {
if (key === 'callback') {
callback = value;
} else if (deferred) {
deferred[key](value);
}
});
}
function doneLoading() {
var $jproper = $j(proper),
$jbroken = $j(broken);
if ( deferred ) {
if ( broken.length ) {
deferred.reject( $jimages, $jproper, $jbroken );
} else {
deferred.resolve( $jimages );
}
}
if ( $j.isFunction( callback ) ) {
callback.call( $jthis, $jimages, $jproper, $jbroken );
}
}
function imgLoaded( img, isBroken ) {
// don't proceed if BLANK image, or image is already loaded
if ( img.src === BLANK || $j.inArray( img, loaded ) !== -1 ) {
return;
}
// store element in loaded images array
loaded.push( img );
// keep track of broken and properly loaded images
if ( isBroken ) {
broken.push( img );
} else {
proper.push( img );
}
// cache image and its state for future calls
$j.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } );
// trigger deferred progress method if present
if ( hasNotify ) {
deferred.notifyWith( $j(img), [ isBroken, $jimages, $j(proper), $j(broken) ] );
}
// call doneLoading and clean listeners if all images are loaded
if ( $jimages.length === loaded.length ){
setTimeout( doneLoading );
$jimages.unbind( '.imagesLoaded' );
}
}
// if no images, trigger immediately
if ( !$jimages.length ) {
doneLoading();
} else {
$jimages.bind( 'load.imagesLoaded error.imagesLoaded', function( event ){
// trigger imgLoaded
imgLoaded( event.target, event.type === 'error' );
}).each( function( i, el ) {
var src = el.src;
// find out if this image has been already checked for status
// if it was, and src has not changed, call imgLoaded on it
var cached = $j.data( el, 'imagesLoaded' );
if ( cached && cached.src === src ) {
imgLoaded( el, cached.isBroken );
return;
}
// if complete is true and browser supports natural sizes, try
// to check for image status manually
if ( el.complete && el.naturalWidth !== undefined ) {
imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 );
return;
}
// cached images don't fire load sometimes, so we reset src, but only when
// dealing with IE, or image is complete (loaded) and failed manual check
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
if ( el.readyState || el.complete ) {
el.src = BLANK;
el.src = src;
}
});
}
return deferred ? deferred.promise( $jthis ) : $jthis;
};
var Grid = (function() {
// list of items
var $jgrid = $j( '#og-grid' ),
// the items
$jitems = $jgrid.children( 'li' ),
// current expanded item's index
current = -1,
// position (top) of the expanded item
// used to know if the preview will expand in a different row
previewPos = -1,
// extra amount of pixels to scroll the window
scrollExtra = 0,
// extra margin when expanded (between preview overlay and the next items)
marginExpanded = 10,
$jwindow = $j( window ), winsize,
$jbody = $j( 'html, body' ),
// transitionend events
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
// support for csstransitions
support = Modernizr.csstransitions,
// default settings
settings = {
minHeight : 500,
speed : 350,
easing : 'ease'
};
function init( config ) {
// the settings..
settings = $j.extend( true, {}, settings, config );
// preload all images
$jgrid.imagesLoaded( function() {
// save item´s size and offset
saveItemInfo( true );
// get window´s size
getWinSize();
// initialize some events
initEvents();
} );
}
// saves the item´s offset top and height (if saveheight is true)
function saveItemInfo( saveheight ) {
$jitems.each( function() {
var $jitem = $j( this );
$jitem.data( 'offsetTop', $jitem.offset().top );
if( saveheight ) {
$jitem.data( 'height', $jitem.height() );
}
} );
}
function initEvents() {
// when clicking an item, show the preview with the item´s info and large image.
// close the item if already expanded.
// also close if clicking on the item´s cross
$jitems.on( 'click', 'span.og-close', function() {
hidePreview();
return false;
} ).children( 'a' ).on( 'click', function(e) {
var $jitem = $j( this ).parent();
// check if item already opened
current === $jitem.index() ? hidePreview() : showPreview( $jitem );
return false;
} );
// on window resize get the window´s size again
// reset some values..
$jwindow.on( 'debouncedresize', function() {
scrollExtra = 0;
previewPos = -1;
// save item´s offset
saveItemInfo();
getWinSize();
var preview = $j.data( this, 'preview' );
if( typeof preview != 'undefined' ) {
hidePreview();
}
} );
}
function getWinSize() {
winsize = { width : $jwindow.width(), height : $jwindow.height() };
}
function showPreview( $jitem ) {
var preview = $j.data( this, 'preview' ),
// item´s offset top
position = $jitem.data( 'offsetTop' );
scrollExtra = 0;
// if a preview exists and previewPos is different (different row) from item´s top then close it
if( typeof preview != 'undefined' ) {
// not in the same row
if( previewPos !== position ) {
// if position > previewPos then we need to take te current preview´s height in consideration when scrolling the window
if( position > previewPos ) {
scrollExtra = preview.height;
}
hidePreview();
}
// same row
else {
preview.update( $jitem );
return false;
}
}
// update previewPos
previewPos = position;
// initialize new preview for the clicked item
preview = $j.data( this, 'preview', new Preview( $jitem ) );
// expand preview overlay
preview.open();
}
function hidePreview() {
current = -1;
var preview = $j.data( this, 'preview' );
preview.close();
$j.removeData( this, 'preview' );
}
// the preview obj / overlay
function Preview( $jitem ) {
this.$jitem = $jitem;
this.expandedIdx = this.$jitem.index();
this.create();
this.update();
}
Preview.prototype = {
create : function() {
// create Preview structure:
this.$jtitle = $j( '<h3></h3>' );
this.$jdescription = $j( '<p></p>' );
this.$jhref = $j( 'Go To Contact Page...' );
this.$jdetails = $j( '<div class="og-details"></div>' ).append( this.$jtitle, this.$jdescription, this.$jhref );
this.$jloading = $j( '<div class="og-loading"></div>' );
this.$jfullimage = $j( '<div class="og-fullimg"></div>' ).append( this.$jloading );
this.$jclosePreview = $j( '<span class="og-close"></span>' );
this.$jprevious = $j( '<span class="og-previous"></span>' );
this.$jnext = $j( '<span class="og-next"></span>' );
this.$jpreviewInner = $j( '<div class="og-expander-inner"></div>' ).append( this.$jclosePreview, this.$jprevious, this.$jnext, this.$jfullimage, this.$jdetails );
this.$jpreviewEl = $j( '<div class="og-expander"></div>' ).append( this.$jpreviewInner );
// append preview element to the item
this.$jitem.append( this.getEl() );
// set the transitions for the preview and the item
if( support ) {
this.setTransition();
}
},
update : function( $jitem ) {
if( $jitem ) {
this.$jitem = $jitem;
}
// if already expanded remove class "og-expanded" from current item and add it to new item
if( current !== -1 ) {
var $jcurrentItem = $jitems.eq( current );
$jcurrentItem.removeClass( 'og-expanded' );
this.$jitem.addClass( 'og-expanded' );
// position the preview correctly
this.positionPreview();
}
// update current value
current = this.$jitem.index();
// update preview´s content
var $jitemEl = this.$jitem.children( 'a' ),
eldata = {
href : $jitemEl.attr( 'href' ),
largesrc : $jitemEl.data( 'largesrc' ),
title : $jitemEl.data( 'title' ),
description : $jitemEl.data( 'description' )
};
this.$jtitle.html( eldata.title );
this.$jdescription.html( eldata.description );
this.$jhref.attr( 'href', eldata.href );
var self = this;
// remove the current image in the preview
if( typeof self.$jlargeImg != 'undefined' ) {
self.$jlargeImg.remove();
}
// preload large image and add it to the preview
// for smaller screens we don´t display the large image (the media query will hide the fullimage wrapper)
if( self.$jfullimage.is( ':visible' ) ) {
this.$jloading.show();
$j( '<img/>' ).load( function() {
var $jimg = $j( this );
if( $jimg.attr( 'src' ) === self.$jitem.children('a').data( 'largesrc' ) ) {
self.$jloading.hide();
self.$jfullimage.find( 'img' ).remove();
self.$jlargeImg = $jimg.fadeIn( 350 );
self.$jfullimage.append( self.$jlargeImg );
}
} ).attr( 'src', eldata.largesrc );
}
},
open : function() {
setTimeout( $j.proxy( function() {
// set the height for the preview and the item
this.setHeights();
// scroll to position the preview in the right place
this.positionPreview();
}, this ), 25 );
},
close : function() {
var self = this,
onEndFn = function() {
if( support ) {
$j( this ).off( transEndEventName );
}
self.$jitem.removeClass( 'og-expanded' );
self.$jpreviewEl.remove();
};
setTimeout( $j.proxy( function() {
if( typeof this.$jlargeImg !== 'undefined' ) {
this.$jlargeImg.fadeOut( 'fast' );
}
this.$jpreviewEl.css( 'height', 0 );
// the current expanded item (might be different from this.$jitem)
var $jexpandedItem = $jitems.eq( this.expandedIdx );
$jexpandedItem.css( 'height', $jexpandedItem.data( 'height' ) ).on( transEndEventName, onEndFn );
if( !support ) {
onEndFn.call();
}
}, this ), 25 );
return false;
},
calcHeight : function() {
var heightPreview = winsize.height - this.$jitem.data( 'height' ) - marginExpanded,
itemHeight = winsize.height;
if( heightPreview < settings.minHeight ) {
heightPreview = settings.minHeight;
itemHeight = settings.minHeight + this.$jitem.data( 'height' ) + marginExpanded;
}
this.height = heightPreview;
this.itemHeight = itemHeight;
},
setHeights : function() {
var self = this,
onEndFn = function() {
if( support ) {
self.$jitem.off( transEndEventName );
}
self.$jitem.addClass( 'og-expanded' );
};
this.calcHeight();
this.$jpreviewEl.css( 'height', this.height );
this.$jitem.css( 'height', this.itemHeight ).on( transEndEventName, onEndFn );
if( !support ) {
onEndFn.call();
}
},
positionPreview : function() {
// scroll page
// case 1 : preview height + item height fits in window´s height
// case 2 : preview height + item height does not fit in window´s height and preview height is smaller than window´s height
// case 3 : preview height + item height does not fit in window´s height and preview height is bigger than window´s height
var position = this.$jitem.data( 'offsetTop' ),
previewOffsetT = this.$jpreviewEl.offset().top - scrollExtra,
scrollVal = this.height + this.$jitem.data( 'height' ) + marginExpanded <= winsize.height ? position : this.height < winsize.height ? previewOffsetT - ( winsize.height - this.height ) : previewOffsetT;
$jbody.animate( { scrollTop : scrollVal }, settings.speed );
},
setTransition : function() {
this.$jpreviewEl.css( 'transition', 'height ' + settings.speed + 'ms ' + settings.easing );
this.$jitem.css( 'transition', 'height ' + settings.speed + 'ms ' + settings.easing );
},
getEl : function() {
return this.$jpreviewEl;
}
}
return { init : init };
})();
Here is the HTML...
<ul id="og-grid" class="og-grid">
<!-- The Entrance -->
<li>
<a href="http://www.thebarnandpinncottage.co.uk/contact" data-largesrc="/images/cottage-images/entrance/image-entrance.jpg" data-title="The Entrance" data-description="The Barn and Pinn Cottage">
<img src="/images/cottage-images/entrance/image-entrance-thumb.jpg" alt="img01"/>
</a>
</li>
<li>
<a href="http://www.thebarnandpinncottage.co.uk/contact" data-largesrc="/images/cottage-images/entrance/image-entrance-2.jpg" data-title="The Entrance" data-description="The Barn and Pinn Cottage">
<img src="/images/cottage-images/entrance/image-entrance-2-thumb.jpg" alt="img01"/>
</a>
</li>
<li>
<a href="http://www.thebarnandpinncottage.co.uk/contact" data-largesrc="/images/cottage-images/entrance/image-entrance-3.jpg" data-title="The Entrance" data-description="The Barn and Pinn Cottage">
<img src="/images/cottage-images/entrance/image-entrance-3-thumb.jpg" alt="img01"/>
</a>
</li>
<li>
<a href="http://www.thebarnandpinncottage.co.uk/contact" data-largesrc="/images/cottage-images/entrance/image-entrance-4.jpg" data-title="The Entrance" data-description="The Barn and Pinn Cottage">
<img src="/images/cottage-images/entrance/image-entrance-4-thumb.jpg" alt="img01"/>
</a>
</li>
<li>
<a href="http://www.thebarnandpinncottage.co.uk/contact" data-largesrc="/images/cottage-images/entrance/image-entrance-5.jpg" data-title="The Entrance" data-description="The Barn and Pinn Cottage">
<img src="/images/cottage-images/entrance/image-entrance-5-thumb.jpg" alt="img01" name="VCRImage"/>
</a>
</li>
Here is the CSS...
.og-grid {
list-style: none;
padding: 20px 0;
margin: 0 auto;
text-align: center;
width: 100%;
}
.og-grid li {
display: inline-block;
margin: 10px 5px 0 5px;
vertical-align: top;
height: 250px;
}
.og-grid li > a,
.og-grid li > a img {
border: none;
outline: none;
display: block;
position: relative;
}
.og-grid li.og-expanded > a::after {
top: auto;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-bottom-color: #B3BCC6;
border-width: 15px;
left: 50%;
margin: -20px 0 0 -15px;
}
.og-expander {
position: absolute;
background: #B3BCC6;
top: auto;
left: 0;
width: 88.888888%;
margin-top: 10px;
text-align: left;
height: 0;
overflow: hidden;
}
.og-expander-inner {
padding: 80px 30px;
height: 100%;
}
.og-close {
position: absolute;
width: 40px;
height: 40px;
top: 20px;
right: 20px;
cursor: pointer;
}
.og-close::before,
.og-close::after {
content: '';
position: absolute;
width: 100%;
top: 50%;
height: 1px;
background: white;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
transform: rotate(45deg);
}
.og-close::after {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.og-close:hover::before,
.og-close:hover::after {
background: #333;
}
.og-previous {
position: absolute;
width: 40px;
height: 40px;
top: 20px;
right: 200px;
cursor: pointer;
}
.og-previous::before {
content: '<<';
position: absolute;
width: 100%;
top: 50%;
color: white;
}
.og-next {
position: absolute;
width: 40px;
height: 40px;
top: 20px;
right: 100px;
cursor: pointer;
}
.og-next::before {
content: '>>';
position: absolute;
width: 100%;
top: 50%;
color: white;
}
.og-details {
padding: 0 40px 0 20px;
}
.og-fullimg {
text-align: center;
}
.og-fullimg img {
display: inline-block;
max-height: 100%;
max-width: 100%;
}
.og-details h3 {
font-weight: 300;
font-size: 1.6em;
padding: 0;
margin: 0 0 1em 0;
color: #4B77B1;
}
.og-details p {
font-weight: 400;
font-size: 16px;
line-height: 22px;
color: white;
}
.og-details a {
font-weight: 700;
font-size: 16px;
color: #4B77B1;
text-transform: uppercase;
letter-spacing: 2px;
padding: 10px 20px;
border: 3px solid #4B77B1;
display: inline-block;
margin: 30px 0 0;
outline: none;
}
.og-details a::before {
content: '\2192';
display: inline-block;
margin-right: 10px;
}
.og-details a:hover {
border-color: white;
color: white;
}
.og-loading {
width: 20px;
height: 20px;
border-radius: 50%;
background: #ddd;
box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ccc;
position: absolute;
top: 50%;
left: 50%;
margin: -25px 0 0 -25px;
-webkit-animation: loader 0.5s infinite ease-in-out both;
-moz-animation: loader 0.5s infinite ease-in-out both;
animation: loader 0.5s infinite ease-in-out both;
}
#-webkit-keyframes loader {
0% { background: #ddd; }
33% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ddd; }
66% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ddd, -15px 30px 1px #ccc; }
}
#-moz-keyframes loader {
0% { background: #ddd; }
33% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ddd; }
66% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ddd, -15px 30px 1px #ccc; }
}
#keyframes loader {
0% { background: #ddd; }
33% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ddd; }
66% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ddd, -15px 30px 1px #ccc; }
}

Categories