I have 4 buttons, button 1 should trigger an animation, button 2 should reverse that animation, this works, with a small css catch, though.(I want the divs to come back into position by clicking button 2, not appearing first, then being animated, I would be happy about a solution for this as well).
Now button 3 causes the divs to fly out, button 4 brings them back into place, but this process only works once.
I have the following functions triggered on click
function animationTwo() {
var div1 = document.querySelector('.inner1');
var div2 = document.querySelector('.inner2');
var div3 = document.querySelector('.inner3');
var div4 = document.querySelector('.inner4');
removeAllClasses('div1', 'div2', 'div3', 'div4');
setTimeout(function() {
addClass(div1, 'outTopLeft');
addClass(div2, 'outTopRight');
addClass(div3, 'outTopRight');
addClass(div4, 'outTopRight');
}, 100);
}
function reverseAnimationTwo() {
var div1 = document.querySelector('.inner1');
var div2 = document.querySelector('.inner2');
var div3 = document.querySelector('.inner3');
var div4 = document.querySelector('.inner4');
removeAllClasses('div1', 'div2', 'div3', 'div4');
setTimeout(function() {
addClass(div1, 'inAgain');
addClass(div2, 'inAgain');
addClass(div3, 'inAgain');
addClass(div4, 'inAgain');
}, 100);
}
Sometimes, I get a reference error, "element" is not defined, but sometimes, there is no error thrown. It always works after page refresh.
Here are the add and remove class functions which are called in the above functions:
function addClass(element, classToAdd) {
var currentClassValue = element.className;
if (currentClassValue.indexOf(classToAdd) == -1) {
if ((currentClassValue == null) || (currentClassValue === "")) {
element.className = classToAdd;
} else {
element.className += " " + classToAdd;
}
}
}
function removeAllClasses(el, el, el, el) {
var currentClassValue = el.className;
el.className = "";
};
Another thing is, I would like to have the animations to be staggered for each div, like the one triggered when clicking the first button, but I have no success with neither loop nor callbacks, I have tried. And the scrollbars when clicking button 3 and 4, how can I avoid these?
I know this is a mix of JS and Css questions, and I hope this does not upset anyone.
Link to pen:
http://codepen.io/damianocel/pen/QdKyzm
After Modifying Your removeAllClasses(el,el,el,el) function, it is working
var aniOne = document.getElementById('move');
var aniOneReversed = document.getElementById('moveBack');
var aniTwo = document.getElementById('move2');
var aniTwoReversed = document.getElementById('moveBack2');
aniOne.addEventListener('click', function () {
animationOne();
});
aniOneReversed.addEventListener('click', function () {
reverseAnimationOne();
});
aniTwo.addEventListener('click', function () {
animationTwo();
});
aniTwoReversed.addEventListener('click', function () {
reverseAnimationTwo();
});
function animationOne() {
var allDivs = document.querySelectorAll('div');
[].forEach.call(allDivs, function (allDivs, i) {
allDivs.classList.remove('animationOneReversed');
setTimeout(function () {
allDivs.classList.add('animationOne');
}, 100 * i);
});
}
function reverseAnimationOne() {
var allDivs = document.querySelectorAll('div');
[].forEach.call(allDivs, function (allDivs, i) {
allDivs.classList.remove('animationOne');
setTimeout(function () {
allDivs.classList.add('animationOneReversed');
}, 100 * i);
});
}
function animationTwo() {
var div1 = document.querySelector('.inner1');
var div2 = document.querySelector('.inner2');
var div3 = document.querySelector('.inner3');
var div4 = document.querySelector('.inner4');
removeAllClasses(div1);
removeAllClasses(div2);
removeAllClasses(div3);
removeAllClasses(div4);
setTimeout(function () {
addClass(div1, 'outTopLeft');
addClass(div2, 'outTopRight');
addClass(div3, 'outTopRight');
addClass(div4, 'outTopRight');
}, 100);
}
function reverseAnimationOne() {
var allDivs = document.querySelectorAll('div');
[].forEach.call(allDivs, function (allDivs, i) {
allDivs.classList.remove('animationOne');
setTimeout(function () {
allDivs.classList.add('animationOneReversed');
}, 100 * i);
});
}
function reverseAnimationTwo() {
var div1 = document.querySelector('.inner1');
var div2 = document.querySelector('.inner2');
var div3 = document.querySelector('.inner3');
var div4 = document.querySelector('.inner4');
setTimeout(function () {
addClass(div1, 'inAgain');
addClass(div2, 'inAgain');
addClass(div3, 'inAgain');
addClass(div4, 'inAgain');
}, 100);
}
function addClass(element, classToAdd) {
var currentClassValue = element.className;
if (currentClassValue.indexOf(classToAdd) == -1) {
if (currentClassValue == null || currentClassValue === '') {
element.className = classToAdd;
} else {
element.className += ' ' + classToAdd;
}
}
}
function removeAllClasses(el) {
var currentClassValue = el.className;
currentClassValue=currentClassValue.split(" ");
if(currentClassValue[1]!=""){
el.className = currentClassValue[0];
}
}
.wrapper {
perspective: 800px;
position:relative;
margin:0 auto;
width:1000px;
height:500px;
border:black 10px solid;
padding:30px;
}
.wrapperInner {
position:relative;
width:100%;
height:500px;
transform-style: preserve-3d;
transition: transform 1s;
margin: 0 auto;
padding: 30px;
}
div {
height:40%;
width:40%;
background-image: url(http://scontent.cdninstagram.com/t51.2885-15/e35/12728470_425209597603876_513397964_n.jpg?ig_cache_key=MTE5MDA5Nzg0MjkxOTc4NTAzNg%3D%3D.2);
background-size: 100% 100%;
border:2px pink solid;
margin:40px;
}
.inner1 {
position:absolute;
top:0px;
left:0;
}
.inner2 {
position:absolute;
top:0px;
right:0;
}
.inner3 {
position:absolute;
bottom:0px;
left:0;
}
.inner4 {
position:absolute;
bottom:0px;
right:0;
}
#keyframes turner{
0% { transform:rotateY(0deg); }
15% { transform:rotate(360deg); }
40% { transform:skew(23deg,22deg); }
50% { transform:translateX(50%);}
60% { transform:skew(-23deg,-22deg);}
70% { transform:translateX(-250%);}
80% { opacity:.9;}
100% { transform:scale(0);}
}
#keyframes turnerBack{
0% { opacity:0; }
15% { transform:scale(.3); }
40% { transform:skew(-23deg,-22deg); }
50% { transform:translateX(-50%);}
60% { transform:skew(23deg,22deg);}
70% { transform:translateX(150%);}
80% { opacity:1;}
100% { transform:scale(1);}
}
.animationOne {
animation: turner 3s 1;
backface-visibility: visible;
-webkit-animation-fill-mode: forwards; /* Safari 4.0 - 8.0 */
animation-fill-mode: forwards;
}
.animationOneReversed {
animation: turnerBack 3s 1;
backface-visibility: visible;
-webkit-animation-fill-mode: forwards; /* Safari 4.0 - 8.0 */
animation-fill-mode: forwards;
}
.outTopLeft {
transition:2s;
transform:translate(-100%,-100%);
}
.outBottomLeft {
transition:2s;
transform:translate(-100%,100%);
}
.outTopRight {
transition:2s;
transform:translate(200%,-100%);
}
.outBottomRight {
transition:2s;
transform:translate(200%,100%);
}
.inAgain {
transition:1s;
transform:translate(0);
}
<button id="move">Click for animation</button>
<button id="moveBack">Click to reverse animation</button>
<button id="move2">Click for animation 2</button>
<button id="moveBack2">Click to reverse animation 2</button>
<section class="wrappper">
<section class="wrappperInner">
<div class="inner1"></div>
<div class="inner2"></div>
<div class="inner3"></div>
<div class="inner4"></div>
</section>
</section>
Related
I'm having trouble with the following situation.
I have a button which acts like a normal toggle. When I click on the "Animate" button, I want the <p>This is new Div</p> to fade in when I again click on the Animate button, this <p> should fade out.
How can I achieve this?
const main = document.getElementById('main');
const btn = document.getElementById('btn');
let show = false;
btn.addEventListener('click', () => {
if(show) {
const newDiv = document.getElementById("new-div");
newDiv.remove();
show = false;
} else {
const newDiv = document.createElement('div');
newDiv.id = "new-div";
newDiv.innerHTML = "<p>This is new Div</p>";
main.appendChild(newDiv);
show = true;
}
})
#new-div {
transition: all 2s ease-in-out;
}
<div id="main">
<button id="btn">Animate</button>
</div>
I'm actually building a gallary layout app, which requires to fade in when clicked on a image + show in full screen, then fade out to its original position when clicked. Since there will be many images, I want to use JS to dynamically work on this.
And the biggest hurdle so far is to implement fade-out, because the element is being deleted.
Based on your information I've made a refined version, pls see fiddle and code below: https://jsfiddle.net/Kenvdb/8nsbp16o/
JavaScript:
const main = document.getElementById('main');
const btn = document.getElementById('btn');
let toggledDiv = null;
btn.addEventListener('click', () => {
if (!toggledDiv) {
show();
} else {
hide();
}
})
const show = () => {
toggledDiv = document.createElement('div');
toggledDiv.id = "content";
toggledDiv.style.opacity = "1";
toggledDiv.innerHTML = "<p>This is new Div</p>";
main.appendChild(toggledDiv);
}
const hide = () => {
toggledDiv.style.animation = "fade-out 0.5s ease-in";
toggledDiv.style.opacity = "0";
toggledDiv.addEventListener('animationend', remove);
toggledDiv.addEventListener('webkitAnimationEnd', remove);
}
const remove = () => {
toggledDiv.remove();
toggledDiv = null;
};
CSS:
#content {
opacity: 0;
animation: fade-in 0.5s ease-in;
}
#keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
#keyframes fade-out {
0% { opacity: 1; }
100% { opacity: 0; }
}
HTML:
<div id="main">
<button id="btn">Animate</button>
</div>
You'll need to set the opacity to 0 first. Then you can apply a keyframe animation.
Otherwise, the element has nothing to transition from.
See below.
#new-div {
opacity: 1;
animation: fadeIn 2s ease-in-out;
}
#keyframes fadeIn {
from {
opacity: 0;
}
}
There's several ways of doing this. You can set the opacity of the newly added element using the style attribute:
const main = document.getElementById('main');
const btn = document.getElementById('btn');
let show = false;
let fading = false;
btn.addEventListener('click', () => {
if (fading) return;
if (show) {
const newDiv = document.getElementById("new-div");
newDiv.style = "opacity: 0"; // start the fade
fading = true;
window.setTimeout(function() {
fading = false; // disable showing/hiding while fading
newDiv.remove(); // remove after fade completed
show = false;
}, 2000);
} else {
show = true;
const newDiv = document.createElement('div');
newDiv.id = "new-div";
newDiv.innerHTML = "<p>This is new Div</p>";
main.appendChild(newDiv);
window.setTimeout(function() {
newDiv.style = "opacity: 1"; // Start fading after a minimal time
});
}
})
#new-div {
transition: all 2s ease-in-out;
opacity: 0;
}
<div id="main">
<button id="btn">Animate</button>
</div>
Or you can use jQuery, which significantly reduce the code:
$("#btn").on('click', () => {
var newDiv = $("#new-div");
if (newDiv.length) {
newDiv.stop().fadeOut(2000, function() {
newDiv.remove();
});
} else {
$(`<div id='new-div'>
<p>This is new Div</p>
</div`).appendTo("#main").hide().fadeIn(2000);
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="main">
<button id="btn">Animate</button>
</div>
You can do it simply using both of fadeIn() and fadeOut() methods in jQuery.
Here is an example:
let alreadyClicked = false;
$("#btn").click(function() {
if(alreadyClicked == false) {
$("p").remove(); //Remove the paragraph if already created.
$("#main").append("<p style='display: none;'>Hello, world!</p>"); //Create a paragraph.
$("p").fadeIn(); //Show it by fading it in.
alreadyClicked = true;
} else {
$("p").fadeOut(); //Fade it out
alreadyClicked = false;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="main">
<button id="btn">Animate</button>
</div>
i am trying to loop through a javascript object to change background-image to other images every 3 seconds.
i know i will be using setInterval, a for-loop to go through the object and jQuery to access the css and change the background. but i am not sure about how to use all that together
HTML
<div class="example"></div>
CSS
.example {
height: 600px;
background-image: url(path/pic01.jpeg); }
JS
var header = {
pic01 : 'url(path/pic02.jpeg)',
pic02 : 'url(path/pic03.jpeg)',
pic03 : 'url(path/pic04.jpeg)',
pic04 : 'url(path/pic05.jpeg)'}
i tried:
var counter = 0;
var i = setInterval(function(){
counter++;
if(counter === 5) {
clearInterval(i);
}
}, 3000);
and something like this. but i can't seem to get it done
for (var key in header) {
if (header.hasOwnProperty(key)) {
console.log(header[key]);
})
}
Why not use CSS?
html, body {
position: relative;
padding: 0;
margin: 0;
}
html, body, .example {
width: 100%;
height: 100%;
}
.example {
background-color: #333;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
animation-name: gallery;
animation-duration: 12s;
animation-timing-function: steps(1, start);
animation-iteration-count: infinite;
}
#keyframes gallery {
0%, 100% {
background-image: url(https://placebear.com/145/201)
}
25% {
background-image: url(https://placebear.com/395/205)
}
50% {
background-image: url(https://placebear.com/150/200)
}
75% {
background-image: url(https://placebear.com/145/300)
}
}
<div class="example"></div>
First, use an array for the header images, that makes it a little easier. Below are two functions that will either loop infinite, starting from 0 index once the array's length is reached, the other one will loop the array once until all elements have been shown once. (You'll need to adjust it to your needs instead of writing the text to the div...)
var header = [
'url(path/pic02.jpeg)',
'url(path/pic03.jpeg)',
'url(path/pic04.jpeg)',
'url(path/pic05.jpeg)'
];
var i = 0;
function loopInfinite(arr) {
document.getElementById('foo').innerHTML = arr[i];
i++;
if (i === arr.length) i = 0;
setTimeout(() => {
loopInfinite(arr);
}, 500);
}
function loopOnce(arr) {
document.getElementById('foo').innerHTML = arr[i];
i++;
if (i < arr.length) {
setTimeout(() => {
loopOnce(arr);
}, 500);
}
}
loopInfinite(header);
<div id="foo"></div>
In this codeblock:
var counter = 0;
var i = setInterval(function(){
counter++;
if(counter === 5) {
clearInterval(i);
}
}, 3000);
You have not included your background-updating code. Something like this might be what you are looking for:
var imageNumber = 0;
var counter = 0;
var i = setInterval(function(){
counter++;
if(counter === 5) {
// your background-updating code
$('.example').css('background-image',headers['pic0' + imageNumber]);
imageNumber++;
if (imageNumber > headers.length) {
imageNumber = 0;
}
clearInterval(i);
}
}, 3000);
The following script is set to fade out, and then fade in the next banner set. I would like to find out how to modify this to be a crossfade so the next banner set fades in on top of the existing one, and then it disappears. It would just look a lot cleaner.
I've seen a bunch of scripts for crossfade; however, because this script fades "children" elements, not sure how to modify them to make this work.
If there is a better way to do this, please let me know,
$(function () {
/* SET PARAMETERS */
var change_img_time = 9000;
var transition_speed = 1000;
var simple_slideshow = $("#graphic_1"),
listItems = simple_slideshow.children('.banner'),
listLen = listItems.length,
i = 0,
changeList = function () {
listItems.eq(i).fadeOut(transition_speed, function () {
i += 1;
if (i === listLen) {
i = 0;
}
listItems.eq(i).fadeIn(transition_speed);
});
};
listItems.not(':first').hide();
setInterval(changeList, change_img_time);
});
html/php (Just so y'all can see how the data looks)
$rotban1 = $db1->query("SELECT background_image, background_image_alt, foreground_image, foreground_image_alt, text FROM banner") or die ('Unable to execute query. '. mysqli_error($db1con));
$slidecount == 0;
if ($rotban1->num_rows > 0) {
while ($slide = $rotban1->fetch_assoc()) {
echo '<div class="banner">';
echo '<img class="background_image" alt="'. $slide['background_image_alt'] .'" src="'. $slide['background_image'] .'">';
echo '<img class="foreground_image" alt="'. $slide['foreground_image_alt'] .'" src="'. $slide['foreground_image'] .'">';
if (!empty( $slide['text'])) { echo '<h1>'. $text .'</h1>'; }
echo '</div>';
}
}
Instead of calling one effect when the first one finishes, use the .animate() method with the queue option set to false and run them both at the same time.
changeList = function () {
listItems.eq(i).animate(
{ opacity:0 },
{
queue: false,
duration: 3000
});
i += 1;
if (i === listLen) {
i = 0;
}
listItems.eq(i).animate(
{ opacity:1 },
{
queue: false,
duration: 3000
});
};
Here's a working example:
$("#red").animate({
opacity:0
}, {
queue: false,
duration: 3000
});
$("#blue").animate({
opacity:1
}, {
queue: false,
duration: 3000
});
div { width:50px; height:50px; position:absolute; top:0; }
#red { background:red; }
#blue { background:blue; opacity:0;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="red">
</div>
<div id="blue">
</div>
You call fadeIn after fadeOut finishes.
Try calling them both at the same time, to do that don't use callback function:
changeList = function () {
listItems.eq(i).fadeOut(transition_speed);
i += 1;
if (i === listLen) {
i = 0;
}
listItems.eq(i).fadeIn(transition_speed);
};
This way, fadeOut and fadeIn animations start at the same time (ignore miliseconds)
You can use css transition and opacity, position:absolute, setTimeout() and .eq() to "crossfade" elements opacity rendering
function crossFade(n) {
setTimeout(function() {
$("div img").eq(n).css("opacity", 0);
$("div img").eq(n).prev("img").css("opacity", 1);
setTimeout(function() {
if (--n > 0) {
crossFade(n)
} else {
setTimeout(function() {
$("div img").css("opacity", 0).eq(len)
.css("opacity", 1);
setTimeout(function() {
crossFade(len)
}, 1500)
}, 1500)
}
}, 1500)
}, 1500)
}
const len = $("div img").length - 1;
crossFade(len);
body {
width: 100vw;
overflow: hidden;
}
div {
position: relative;
width: 200px;
height: 200px;
left: calc(50vw - 100px);
}
div img {
transition: opacity 3s ease-in-out;
position: absolute;
}
div img:not(:nth-child(3)) {
opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div>
<img src="https://lorempixel.com/200/200/cats" alt="">
<img src="https://lorempixel.com/200/200/nature" alt="">
<img src="https://lorempixel.com/200/200/sports" alt="">
</div>
I need to convert (when the user does something):
adcb
to (the next letter of every letter):
bedc
Currently I am just going with javascript and overwrite the character.
Remember the Pkmn Game Corner?
Wouldn't it be cool if we could simulate that movement of the character (in the pic, change from 7 to Pikachu)?
PS - I have no idea on how to approach this...
This can be achieved with CSS animations to move the letter around, updating the letter when it is not visible.
jsfiddle
HTML:
<div id="reels">
<div>A</div>
<div>L</div>
<div>E</div>
<div>X</div>
</div>
<button id="slide">Slide</button>
CSS:
#reels > div {
width: 20px;
height:30px;
overflow:hidden;
padding:3px;
display:inline-block;
}
#reels > div.reel-change {
animation: slideReel 1s 1;
}
#keyframes slideReel {
0% {
transform: translateY(0);
}
49% {
transform: translateY(-30px);
}
50% {
transform: translateY(30px);
}
100 % {
transform: translateY(0px);
}
}
#reels {
margin-bottom:10px;
}
JavaScript:
$(function() {
$('#slide').click(function() {
var delay = 0;
$('#reels').children().each(function() {
var reel = this;
setTimeout(function() {
$(reel).toggleClass("reel-change");
}, delay);
setTimeout(function() {
changeLetter(reel);
}, delay + 500);
setTimeout(function() {
$(reel).toggleClass("reel-change");
}, delay + 1000);
delay += 500;
});
});
});
function changeLetter(el) {
el.innerHTML = incrementChar(el.innerHTML);
}
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
function incrementChar(c) {
var index = alphabet.indexOf(c);
return alphabet[index + 1] || alphabet[0];
}
Edit: Seeing as it's a good reason for doing it based on your above comments, I made it a bit prettier!!
https://jsfiddle.net/bigalreturns/ctuujz3j/2/
For the record, this is what you can do...
var strMsg = 'abcdef';
var tmpStr = '';
function nextLetter(letter) {
var charCode = letter.charCodeAt(0);
return String.fromCharCode((charCode - 96) % 26 + 97)
}
for(var i=0; i < strMsg.length; i++) {
tmpStr += nextLetter(strMsg[i]);
}
console.log(tmpStr); // bcdefg
Okay I have the following code:-
[SEE JSFIDDLE]
HTML:
<div id="header">
<span class="mobile-menu"></span>
</div>
CSS:
#header {
width: 100%;
background: #000000;
height: 100px;
}
.mobile-menu {
position: absolute;
right: 25px;
top: 20px;
background: url(http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-01.png);
background-repeat: no-repeat;
background-size: 26px !important;
height: 26px;
width: 26px;
display: inline-block;
margin: 7px 0;
-webkit-transition-duration: 0.8s;
-moz-transition-duration: 0.8s;
-o-transition-duration: 0.8s;
transition-duration: 0.8s;
}
.mobile-menu-hover {
background: url(http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/mobile-menu-hover.png);
}
jQuery:
var imagesArray = ["http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-01.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-02.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-03.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-04.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-05.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-06.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-07.png"];
function preloadImg(pictureUrls, callback) {
var i, j, loaded = 0;
var imagesArray = [];
for (i = 0, j = pictureUrls.length; i < j; i++) {
imagesArray.push(new Image());
}
for (i = 0, j = pictureUrls.length; i < j; i++) {
(function (img, src) {
img.onload = function () {
if (++loaded == pictureUrls.length && callback) {
callback(imagesArray);
}
};
img.src = src;
}(imagesArray[i], pictureUrls[i]));
}
};
function changeImage(background, imagesArray, index, reverse) {
background.css("background-image", "url('" + imagesArray[index].src + "')").fadeIn(10, function() {
if (reverse) {
index--;
if (index == -1) {
return; // stop the interval
}
} else {
index++;
if (index == imagesArray.length) {
return; // stop the interval
}
}
//Fade in the top element
background.fadeOut(10, function () {
//Set the background of the top element to the new background
background.css("background-image", "url('" + imagesArray[index] + "')");
changeImage(background, imagesArray, index, reverse);
});
});
}
jQuery(function () {
/* Preload Image */
preloadImg(imagesArray, function (imagesArray) {
jQuery(".mobile-menu").css("background-image", "url('" + imagesArray[0].src + "')")
jQuery('.mobile-menu').on('click', {imgs: imagesArray}, function (event) {
var background = jQuery(".mobile-menu");
var bi = background.css('background-image');
var index = 0;
var reverse = false;
if (imagesArray[0].src != bi.replace('url("', '').replace('")', '')) {
index = imagesArray.length - 1;
reverse = true;
}
changeImage(background, event.data.imgs, index, reverse);
});
});
});
The Issue:
This works fine in Firefox and Chrome, it transitions between the 7 different images on click, then does the reverse on the second click (toggling).
The problem is when I try this in Safari, it basically goes through the image replacement process then reverts back to the first image for some reason and I can't figure out why?
Any ideas?
It seems to be because Safari returns the background-image without double quotes, but your replace function checks for url(", so it doesn't replace and reverse never gets true.
Instead of replacing you can check using either indexOf or matching using a RegExp, it would be safer and more straightforward. Either:
if ( bi.indexOf(imagesArray[0].src) == -1) {
or
if (imagesArray[0].src != bi.match(/http.+png/)[0]) {
With indexOf: http://jsfiddle.net/u9ske14r/