I made a simple slideshow using only CSS, where on the radio button click, the margin of the element changes so it displays the desired slide. You can see how it works in the code snippet below. I also made this slideshow auto play with JavaScript. The script checks the next radio button in the list every 3 seconds.
Now I need help to make the slideshow auto play in a loop and to also stop auto play when you check any radio button manually.
$(document).ready(function() {
function autoplay() {
$("input[name=radio-button]:checked").nextAll(':radio:first').prop('checked', true);
};
setInterval(autoplay, 3000);
});
body {
margin: 0;
padding: 0;
}
.slider {
width: 300%;
transition: margin 1s;
height: 100px;
}
#radio-button1:checked ~ .slider {
margin-left: 0%;
}
#radio-button2:checked ~ .slider {
margin-left: -100%;
}
#radio-button3:checked ~ .slider {
margin-left: -200%;
}
.slider-item {
float: left;
width: 33.33%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<input type="radio" name="radio-button" id="radio-button1" checked />
<input type="radio" name="radio-button" id="radio-button2" />
<input type="radio" name="radio-button" id="radio-button3" />
<div class="slider">
<div class="slider-item" style="background: #F00"></div>
<div class="slider-item" style="background: #0F0"></div>
<div class="slider-item" style="background: #00F"></div>
</div>
Create a curr variable, increment it inside the interval and use Modulo % to loop it back to 0:
jQuery(function($) { // DOM ready and $ alias in scope
var curr = 0;
function autoplay() {
curr = ++curr % 3; // Increment and Reset to 0 when 3
$("[name=radio-button]")[curr].checked = true;
}
setInterval(autoplay, 3000);
});
body {
margin: 0;
padding: 0;
}
.slider {
width: 300%;
transition: margin 1s;
height: 100px;
}
#radio-button1:checked~.slider {
margin-left: 0%;
}
#radio-button2:checked~.slider {
margin-left: -100%;
}
#radio-button3:checked~.slider {
margin-left: -200%;
}
.slider-item {
float: left;
width: 33.33%;
height: 100%;
}
<input type="radio" name="radio-button" id="radio-button1" checked />
<input type="radio" name="radio-button" id="radio-button2" />
<input type="radio" name="radio-button" id="radio-button3" />
<div class="slider">
<div class="slider-item" style="background: #F00"></div>
<div class="slider-item" style="background: #0F0"></div>
<div class="slider-item" style="background: #00F"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
The better way:
Overflow a super parent so you don't get scrollbars
Use display: flex; instead of ugly floats.
Use $('.slider').each( so you can have multiple sliders in a single page!
Create a anim() play() and stop() function to control what happens.
Animate using transform: and transition, since transition is GPU accelerated. Whilst margins are not, and require reflow and repaint.
Animate the transform by using curr * -100 %
Use curr %= tot (Modulo operator) to loopback the curr index to 0 when needed.
Why create buttons manually? Create your slides manually and let JS create the buttons for you.
Use setInterval by storing it into a variable itv
To stop your auto-animation use clearInterval(itv)
Use .hover(stop, play) to stop on mouseenter and autoplay on mouseleave
$('.slider').each(function(slider_idx) { // Use each, so you can have multiple sliders
let curr = 0; // Set current index
let itv = null; // The interval holder
const $slider = $(this);
const $nav = $('.slider-nav', $slider);
const $items = $('.slider-items', $slider);
const $item = $('.slider-item', $slider);
const tot = $item.length; // How many
const btns = [...new Array(tot)].map((_, i) => $('<input>', {
type: 'radio',
name: `slider-btn-${slider_idx}`,
checked: curr == i,
change() { // On button change event
curr = i; // Set current index to this button index
anim();
}
})[0]);
function anim() {
$items.css({transform: `translateX(-${curr*100}%)`}); // Animate
btns[curr].checked = true; // Change nav btn state
}
function play() {
itv = setInterval(() => {
curr = ++curr % tot; // increment curr and reset to 0 if exceeds tot
anim(); // and animate!
}, 3000); // Do every 3sec
}
function stop() {
clearInterval(itv);
}
$nav.empty().append(btns); // Insert buttons
$slider.hover(stop, play); // Handle hover state
play(); // Start autoplay!
});
/*QuickReset*/ * {margin: 0; box-sizing: border-box;}
.slider {
position: relative;
height: 100px;
}
.slider-overflow {
overflow: hidden; /* use an overflow parent! You don't want scrollbars */
height: inherit;
}
.slider-items {
display: flex; /* Use flex */
flex-flow: row nowrap;
height: inherit;
transition: transform 1s; /* Don't use margin, use transform */
}
.slider-item {
flex: 0 0 100%;
height: inherit;
}
.slider-nav {
position: absolute;
width: 100%;
bottom: 0;
text-align: center;
}
<div class="slider">
<div class="slider-overflow">
<div class="slider-items"> <!-- This one will transition -->
<div class="slider-item" style="background:#0bf;">curr = 0</div>
<div class="slider-item" style="background:#fb0;">curr = 1</div>
<div class="slider-item" style="background:#f0b;">curr = 2</div>
</div>
</div>
<div class="slider-nav">
<!-- JS will populate buttons here -->
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
So both DIV elements .slider-nav and .slider-overflow need to be inside of the common parent .slider - so that we properly stop (pause) the auto-animation after any interaction with those elements.
Looping the Slideshow
To loop the slideshow, you'll want to modify your autoplay function to:
Get the list of all radio buttons,
Determine which one is checked,
Uncheck that and check the next one in the list,
Paying special attention to wrap back to the first one if already at the end.
I'll rename the function next.
function next() {
// get all the radio buttons.
var buttons = $('input[name="radio-button"]');
// look at each one in the list.
for (let i = 0; i < buttons.length; i++) {
// if this one isn't the checked one, move on to the next.
if (!buttons[i].checked) {
continue;
}
// okay, so this one is checked. let's uncheck it.
buttons[i].checked = false;
// was this one at the end of the list?
if (i == buttons.length - 1) {
// if so, the new checked one should be the first one.
buttons[0].checked = true;
} else {
// otherwise, just the new checked one is the next in the list.
buttons[i + 1].checked = true;
}
// now that we've made that change, we can break out of the loop.
break;
}
}
As a bonus, you can easily make a similar function called prev to go in the opposite direction.
Stopping the Slideshow
When you manually click a radio button, the slideshow should stop. To stop the slideshow, you need to clear the interval that you already set.
The .setInterval() function returns an "interval id". This id can be used to make changes to the interval later on -- like stopping it.
var timer = setInterval(next, 3000);
Then, later on, you'll want to pass that timer value back into clearInterval to stop the timer.
clearInterval(timer);
It would be easier to factor that into two functions, start and stop and let the timer value be global:
var timer;
function start() {
timer = setInterval(next, 3000);
}
function stop() {
clearInterval(timer);
}
So now you can call the stop function whenever any of the radio buttons receive a click event:
$('input[name="radio-button"]').on('click', stop);
Full Example
This is your code with the modifications described above.
I've added buttons for "start", "stop", "prev", and "next" -- these aren't necessary for things to function. They're just there for demonstration purposes.
$(document).ready(function() {
/* the list of buttons is not dynamic, so rather than fetching the list of
* buttons every time `next` or `prev` gets executed, we can just fetch it
* once and store it globally. */
var buttons = $('input[name="radio-button"]');
var timer;
function start() {
timer = setInterval(next, 3000);
}
function stop() {
clearInterval(timer);
}
function next() {
for (let i = 0; i < buttons.length; i++) {
if (!buttons[i].checked) {
continue;
}
buttons[i].checked = false;
if (i == buttons.length - 1) {
buttons[0].checked = true;
} else {
buttons[i + 1].checked = true;
}
break;
}
}
function prev() {
for (let i = 0; i < buttons.length; i++) {
if (!buttons[i].checked) {
continue;
}
buttons[i].checked = false;
if (i == 0) {
buttons[buttons.length - 1].checked = true;
} else {
buttons[i - 1].checked = true;
}
break;
}
}
start();
buttons.on('click', stop);
/* these next lines are unnecessary if you aren't including the buttons */
$('#start').on('click', start);
$('#stop').on('click', stop);
$('#next').on('click', next);
$('#prev').on('click', prev);
});
body {
margin: 0;
padding: 0;
}
.slider {
width: 300%;
transition: margin 1s;
height: 100px;
}
#radio-button1:checked~.slider {
margin-left: 0%;
}
#radio-button2:checked~.slider {
margin-left: -100%;
}
#radio-button3:checked~.slider {
margin-left: -200%;
}
.slider-item {
float: left;
width: 33.33%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<input type="radio" name="radio-button" id="radio-button1" checked />
<input type="radio" name="radio-button" id="radio-button2" />
<input type="radio" name="radio-button" id="radio-button3" />
<div class="slider">
<div class="slider-item" style="background: #F00"></div>
<div class="slider-item" style="background: #0F0"></div>
<div class="slider-item" style="background: #00F"></div>
</div>
<!-- these buttons are unnecessary, they are only for demonstration purposes -->
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="prev">Prev</button>
<button id="next">Next</button>
Related
The purpose of this is to be able to track the number of times a button with class testButton or incButton has been clicked and if either has been clicked twice to show a overlay.
There are 2 main issues:
1: I'm not sure how to select 2 different classes of buttons
2: Once there are more than 1 button with the same class name the existing JS code does only works on the first button with the testButton class.
The code I have is:
<style>
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
</style>
<div id="winOverlay" style="display:none"></div>
<div id="buttonContainer">
<button class="testButton">1</button>
<button class="incButton">2</button>
<button class="testButton">3</button>
<button class="incButton">4</button>
<button class="testButton">5</button>
</div>
<script>
var count = 0;
var btn = document.getElementById("buttonContainer").querySelector(".testButton");
btn.onclick = function () {
count++;
if (count == 2) {
document.getElementById('winOverlay').style.display = "block";
}
}
</script>
Any help would be greatly appreciated.
You can make use of event Delegation where you add event listener on the common parent container with class buttonContainer and you can check if the button clicked with id only testButton and incButon
1) This code will work if you have to calculate of count of irrespective of which button is clicked.
var count = 0;
var btn = document.getElementById("buttonContainer");
const winOverlay = document.getElementById('winOverlay');
btn.addEventListener("click", e => {
const classes = e.target.classList;
if (classes.contains("testButton") || classes.contains("incButon")) {
count++;
if (count === 2) winOverlay.style.display = "block";
}
})
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
<div id="winOverlay" style="display:none"></div>
<div id="buttonContainer">
<button class="testButton">1</button>
<button class="incButon">2</button>
<button class="testButton">3</button>
<button class="incButon">4</button>
<button class="testButton">5</button>
</div>
2) This code will work if you have to calculate the count of specif key on which you clicked and show overlay if it's count is 2
var btn = document.getElementById("buttonContainer");
const winOverlay = document.getElementById("winOverlay");
const dict = {};
btn.addEventListener("click", (e) => {
const classes = e.target.classList;
const addOverlay = () => (winOverlay.style.display = "block");
if (classes.contains("testButton") || classes.contains("incButon")) {
const key = e.target.dataset.key;
dict[key] = (dict[key] || 0) + 1;
if (dict[key] === 2) addOverlay();
}
});
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
button {
color: white;
border: none;
padding: 1rem;
cursor: pointer;
}
button.testButton {
background-color: teal;
}
button.incButon {
background-color: orange;
}
<div id="winOverlay" style="display: none;"></div>
<div id="buttonContainer">
<button class="testButton" data-key="testButton">1</button>
<button class="incButon" data-key="incButon">2</button>
<button class="testButton" data-key="testButton">3</button>
<button class="incButon" data-key="incButon">4</button>
<button class="testButton" data-key="testButton">5</button>
</div>
You need to select all buttons with querySelectorAll add listener to all of them.
var count = 0;
const buttons = document.querySelectorAll("#buttonContainer > button");
for (let index = 0; index < buttons.length; index++) {
const e = buttons[index];
e.onclick = function() {
count++;
if (count == 2) {
document.getElementById('winOverlay').style.display = "block";
}
}
}
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
<div id="winOverlay" style="display:none"></div>
<div id="buttonContainer">
<button class="testButton">1</button>
<button class="incButon">2</button>
<button class="testButton">3</button>
<button class="incButon">4</button>
<button class="testButton">5</button>
</div>
To select 2 class you should do as in css:
querySelector(class1 class2)
But don't work because you can't use querySelector for two or more classes.
This code say only select class1 or class2 and take the first Element.
Use querySelectorAll() to have all of them
As the others have suggested querySelectorAll provides support for multiple selectors. It will return an array-like nodelist which you can then iterate over.
document.querySelectorAll('testButton', 'incButton');
I'm going to offer an alternative approach using event delegation which allows you to attach one listener to a parent element that captures events as they bubble up the DOM.
This example also uses a closure (basically a function that's returned from another function but that can carry any variables set outside it in the local lexical environment with it when it's returned. This is a useful pattern if you want to avoid global variables. In this case we create an object to hold the totals of the two types of buttons.
// Cache your container and overlay elements
const container = document.querySelector('.buttonContainer');
const overlay = document.querySelector('.overlay');
// Add one listener to the container which calls `handleClick`.
// `handleClick` sets up the object and returns a new function
// (the closure) that carries the object with it.
container.addEventListener('click', handleClick(), false);
function handleClick() {
// An object that holds the button totals
const cases = {
testButton: 0,
incButton: 0
};
// The function that will be added to the listener
// It has the event argument
return function (e) {
// Destructure the nodeName/className from the
// element that was clicked
const { nodeName, className } = e.target;
// Check to see if the element was a button
if (nodeName === 'BUTTON') {
// Increase the value in the object where
// the key matches the className
++cases[className];
console.log(JSON.stringify(cases));
// If that value is 2 show the overlay
if (cases[className] === 2) {
overlay.classList.add('show');
}
}
}
}
.overlay { display: none; margin: 1em; background-color: #acaccc; black: white; padding: 2em; }
.show { display: block; }
button { padding: 0.7em; }
button:hover { cursor: pointer; background-color: #acacac; }
<div class="buttonContainer">
<button class="testButton">1</button>
<button class="incButton">2</button>
<button class="testButton">3</button>
<button class="incButton">4</button>
<button class="testButton">5</button>
</div>
<div class="overlay">Overlay</div>
Additional documentation
Destructuring assignment
nodeName
classList
I have one outer box and an inner box and there are some identical boxes with the same class names inside the second box. I want all of these boxes to appear one after the other with this fade-in effect. So far I have done it for the outer and inner div, and I wanted to use the same function for all the identical boxes inside. I tried to do the same for outer and inner div since they too just need the exact same function. But I wasn't successful. Here is my code :
html:
<div class="div1">
<div class="div2">
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
</div>
</div>
javascript:
let div1 = document.getElementsByClassName("div1")[0];
let div2 = document.getElementsByClassName("div2")[0];
let div3 = document.getElementsByClassName("div3");
div1.style.visibility = "hidden";
div2.style.visibility = "hidden";
function first() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
div1.style.animation = "fadein 5s";
div1.style.visibility = "visible";
resolve("div1 worked!");
}, 1000);
});
}
function second() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
div2.style.animation = "fadein 5s";
div2.style.visibility = "visible";
resolve("div2 worked!");
}, 1000);
});
}
function abc(element) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
element.style.animation = "fadein 5s";
element.style.visibility = "visible";
resolve("third");
}, 1000);
});
}
first()
.then(second)
.then((div3) => {
div3.forEach((element) => {
abc(element);
});
});
css
.div1 {
width: 400px;
height: 500px;
background-color: yellow;
}
.div2 {
width: 350px;
height: 400px;
background-color: green;
}
.div3 {
width: 300px;
height: 50px;
background-color: grey;
margin: 10px;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
The first two works and I don't know how I can reuse the function for the remaining div3 class divs. I tried to reuse the function for the first two as well, but it didn't work and ended up writing same functions again and again. I want to call function abc for each element in div3 and only execute the next one after the first element is done - like how it executes for first and second but using the same function. Not sure how to do that and I'm stuck. Here is a codepen link. As of now all the div3 divs appear together with the second div.
You can use loops and animation-delay to apply the animation as per your need. The following code will work for this case. Code is full with comments to explain what is happening at each point. I have also slightly modified the css so that we don't get any weird blinking effect while executing the code.
//Declare all the classes -
let divs = ["div1", "div2", "div3"];
//Initiate a delay for each iteration
let delay = 0;
//Run a loop for each class
for(let i = 0; i<divs.length; i++){
//Get the element
let div = document.getElementsByClassName(divs[i]);
//Run a loop for element with the class
//(We only have one div with the classes div1 and div2. So it will run one time for them.
//We have 5 divs with div3 class. It will run 5 times in that case
for(let j = 0; j<div.length; j++){
//Get the individual element and add animation with delay
//The delay will also ensure that the animation starts only when the previous element has finished the animation
div[j].style.animation = `fadein 5s ${delay}s forwards` ;
div[j].classList.add("show");
//Increase delay with every iteration
delay+=5;
}
}
div {
visibility: hidden;
}
.div1 {
width: 400px;
height: 500px;
background-color: yellow;
}
.div2 {
width: 350px;
height: 400px;
background-color: green;
}
.div3 {
width: 300px;
height: 50px;
background-color: grey;
margin: 10px;
}
.show {
opacity: 0;
visibility: visible;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<div class="div1">
<div class="div2">
<div class="div3">1</div>
<div class="div3">2</div>
<div class="div3">3</div>
<div class="div3">4</div>
<div class="div3">5</div>
</div>
</div>
The script may seem very long but it is only 10 lines long without the comments. This will also work if you increase or decrease the number of divs
This is one way to solve the problem using setInterval for executing a piece of code every x seconds.
The function fadeIn takes an array of elements that will be faded in by adding the class "show" (se details for "show" in CSS-code below). This will animate the given elements.
The function start collects the elements that will be faded and fades in the first collection of element, then countinuesly fades in the rest of the elements in the array "elements" every 3 seconds.
function fadeIn(el) {
// For every element in the collection
for (var i = 0; i < el.length; i++) {
// Add the class "show"
el[i].classList.add('show');
}
}
function start() {
/*
Collects all the elements that we want to fadeIn in order. First collection of elements will be animated firstly, second collection of elements will be animated secondly, etc.
*/
var elements = [];
elements.push(document.getElementsByClassName("div1"));
elements.push(document.getElementsByClassName("div2"));
elements.push(document.getElementsByClassName("div3"));
// Show the first collection of elements
fadeIn(elements[0]);
// Show the rest of the element collections in array "elements"
var i = 1;
fadeInInterval = setInterval(function() {
fadeIn(elements[i]);
// If there is no more collections to fade in, end the setInterval
if (i == elements.length-1) {
clearInterval(fadeInInterval)
}
i++
}, 3000) // Every 3 seconds
}
start();
div {
visibility: hidden;
}
.div1 {
width: 400px;
height: 500px;
background-color: yellow;
}
.div2 {
width: 350px;
height: 400px;
background-color: green;
}
.div3 {
width: 300px;
height: 50px;
background-color: grey;
margin: 10px;
}
.show {
animation: fadein 5s;
visibility: visible;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<div class="div1">
<div class="div2">
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
<div class="div3"></div>
</div>
</div>
The following code makes my div slide from right to left in my screen. I want to reverse the animation on second button click . But this simply makes my div disappear and appear instantaneously without any animation.Function slider3 is my failed attempt at reversing the animation. The login box right margin is initially -570px .
function call_slider() {
setTimeout("slider()", 50)
}
function slider() {
var label = document.getElementById("container1");
if (label.style.display == 'block') {
alert('this Element is block');
document.getElementById("login_box").style.right = "-570px";
label.style.display = "none";
} else {
alert('this Element is hidden');
setInterval(slider2, 10);
label.style.display = "block";
}
}
function slider2() {
if (document.getElementById("login_box").style.right != "10px") {
document.getElementById("login_box").style.right = parseInt(document.getElementById("login_box").style.right || 0) + 10 + 'px';
}
}
function slider3() {
if (document.getElementById("login_box").style.left != "-570px") {
document.getElementById("login_box").style.left = parseInt(document.getElementById("login_box").style.left || 0) + 10 + 'px';
}
}
.login-box {
width: 320px;
position: absolute;
top: 0px;
margin: 0;
background: white;
padding: 0 0 0 0;
}
.container1 {
width: 100%;
height: 100%;
overflow: hidden;
display: none;
display: flex;
justify-content: flex-end;
opacity: 0.9;
}
<div class="container1" id="container1" style="height:900px;position:absolute; z-index: 1;">
<form method="post" id="myform" onsubmit="mySubmit() " style="">
{% csrf_token %}
<div class="login-box" id="login_box" style=" right:-570px;">
</div>
</form>
</div>
Changes are commented in your code
function call_slider() {
setTimeout("slider()", 50)
}
function slider() {
var label = document.getElementById("container1");
if (label.style.display == 'block') {
alert('this Element is block');
//replaced your code with slider 3 function
//assign interval function as a proprty of global window so it can be accessed by another function
window.moveRight = setInterval(slider3, 10);
} else {
alert('this Element is hidden');
//make your element visible before start animation
label.style.display = "block";
window.moveLeft = setInterval(slider2, 10);
}
}
function slider2() {
//parse the right property as integer
var right = parseInt(document.getElementById("login_box").style.right,10)
if ( right < 10) {
document.getElementById("login_box").style.right = right + 10 + 'px';
} else {
//important -- cancel the interval after animation finished else it will run infinitely and interfere with other other functions
clearInterval(window.moveLeft)
}
}
function slider3() {
//user right here instead of left
//use the same property in other animation
var right = parseInt(document.getElementById("login_box").style.right,10)
if ( right > -570) {
document.getElementById("login_box").style.right = right - 10 + 'px';
} else {
// This is where you get stuck
// in your code the container element is hidden before animation is performed therefore you didn't see box moving to right
//hide container1 element only after entire login-box is moved to right
var label = document.getElementById("container1").style.display = "none";
clearInterval(window.moveRight)
}
}
.login-box{
width:320px;
position: absolute;
top : 0px;
margin: 0 ;
background:red;
padding:0 0 0 0 ;
height : 100px;
}
.container1{
width:100%;
height: 100%;
overflow:hidden;
display:none;
display:flex;
justify-content:flex-end;
opacity: 0.9;
}
<div class="container1" id="container1" >
<form method="post" id="myform" onsubmit="mySubmit() " style="">
<div class = "login-box" id="login_box" style=" right:-570px;">
</div>
</form>
</div>
<!-- Button for performing animation -->
<button onclick="call_slider()" >Show/Hide Login Box</button>
JS newbie here. I have an issue that is probably has a fairly simple answer, but I haven't been able to figure it out yet. I wasn't sure exactly what to call this thing.
I have text in a div and when you hover over it, it displays a picture in another div. This is working fine, but I would like to have it scroll through the images automatically when the page loads. Once the user hovers over one of the text divs, I'd like the auto scroll to stop.
I have a Codepen of how I have it set up here: https://codepen.io/johnballman/pen/dwEwRz
HTML:
<div class="app-screen">
<img src="http://placehold.it/350x150">
</div>
<div id="features">
<article data-src="http://placehold.it/350x150">Link 1</article>
<article data-src="http://placehold.it/350x250">Link 2</article>
<article data-src="http://placehold.it/350x350">Link 3</article>
</div>
CSS:
.app-screen {
float: left;
margin-right: 100px;
display: block;
width: 350px;
height: 200px;
background-color: grey;
padding-top: 100px;
}
img.active{
z-index: 2 !important;
opacity: 1 !important;
transition:opacity 1s linear;
}
JS:
$("#features article").hover( function() {
var value=$(this).attr('data-src');
$(".app-screen img").attr("src", value);
});
$(this).switchClass("", "active", 1000);
Any help would be great. Thanks.
Use setInterval to loop a c current counter.
Use ++c % tot (where tot is the number of links) to: increment-loop the counter.
Use only Classes. That way you can have multiple .Features elements in a single page!
Create show, stop and play functions. show is to show a c image; stop is to stop the interval, and play to start your magic.
/**
* Features
* Auto-change articles featured images
*/
$('.Features').each((i, el) => {
const $this = $(el);
const $image = $this.find('.Features-image');
const $link = $this.find('.Features-link');
const tot = $link.length;
let c = 0; // Counter to keep track of Current image
let itv = null; // Interval loop
const show = () => {
$image.css({backgroundImage: `url("${$link.eq(c).data().src}")`});
$link.removeClass('is-active').eq(c).addClass('is-active');
}
const stop = () => clearInterval(itv);
const play = () => itv = setInterval(() => {
c = ++c % tot; // Preincrement + loop (% = reminder operator)
show(); // Show c image
}, 3000);
// Link mouseenter
$link.on({
mouseenter() {
c = $link.index(this);
stop(); // Stop ongoing auto-play
show(); // Show c image
},
mouseleave() {
play(); // Play on mouseleave
}
});
// Init
show(); // Show c image
play(); // Start play!
});
/*QuickReset*/ *{margin:0;box-sizing:border-box;}html,body{height:100%;font:14px/1.4 sans-serif;}
/**
* Features
* jQuery-handled articles with featured images
*/
.Features {
display: flex;
min-height: 200px;
}
.Features-image {
background: #aaa 50% / cover no-repeat none;
transition: background 0.5s;
flex: 0 1 40%;
}
.Features-links {
display: flex;
flex: 1;
flex-flow: column;
}
.Features-link {
flex: 1;
padding: 10px;
transition: background 0.3s;
border-bottom: 1px solid #ddd;
}
.Features-link:hover,
.Features-link.is-active{
background: #eee;
}
<div class="Features">
<div class="Features-image"></div>
<div class="Features-links">
<article class="Features-link" data-src="//placehold.it/350x350/0bf">Link 1</article>
<article class="Features-link" data-src="//placehold.it/350x350/f0b">Link 2</article>
<article class="Features-link" data-src="//placehold.it/350x350/0fb">Link 3</article>
</div>
</div>
<div class="Features">
<div class="Features-image"></div>
<div class="Features-links">
<article class="Features-link" data-src="//placehold.it/350x350/28a">Lorem</article>
<article class="Features-link" data-src="//placehold.it/350x350/a28">Ipsum</article>
<article class="Features-link" data-src="//placehold.it/350x350/8a2">Dolor</article>
</div>
</div>
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
I am using javascript to change my css class background image every few seconds. It is working great the problem is it just stops after it shows the last image. Can anyone show me what to add to this code so that it will continuously loop itself?
$(window).load(function() {
$(document).ready(function() {
setInterval(fadeDivs, 5000); //call it every 2 seconds
function fadeDivs() {
var visibleDiv = $('.bckgnd:visible:first'); //find first visible div
visibleDiv.fadeOut(400, function() { //fade out first visible div
var allDivs = visibleDiv.parent().children(); //all divs to fade out / in
var nextDivIndex = (allDivs.index(visibleDiv) + 1) % allDivs.length; //index of next div that comes after visible div
var nextdiv = allDivs.eq(nextDivIndex); //find the next visible div
var lastDiv = $('.backgnd3');
var firstDiv = $('.backgnd1');
if (currentDiv != lastDiv) {
var nextdiv = allDivs.eq(nextDivIndex); //find the next visible div
} else {
var nextdiv = firstDiv; //the next div will be the first div, resulting in a loop
}
nextdiv.fadeIn(400); //fade it in
});
};
});
});
.backgnd1 {
width: 100%;
height: 452px;
background: url ('http://quaaoutlodge.com/sites/all/themes/marinelli/img/backgrounds/backgnd1.jpg');
background-size: cover;
background-repeat: no-repeat;
background-color: #000;
}
.backgnd2 {
width: 100%;
height: 452px;
background-image: url ('http://quaaoutlodge.com/sites/all/themes/marinelli/img/backgrounds/the_lodge.jpg');
background-size: cover;
background-repeat: no-repeat;
background-color: #000;
}
.backgnd3 {
width: 100%;
height: 452px;
background-image: url('http://quaaoutlodge.com/sites/all/themes/marinelli/img/backgrounds/getting_here.jpg');
background-size: cover;
background-repeat: no-repeat;
background-color: #000;
}
.index_roof_background {
background-color: #000;
width: 1600px;
height: 452px;
margin: 0px;
padding-top: 0px;
margin-left: auto;
margin-right: auto;
}
<div class="index_roof_background">
<div style="position:absolute; z-index: 2;display:block; background-color:#000;" class="bckgnd backgnd1"></div>
<div style="position:absolute; z-index: 2;display:none; background-color:#000;" class="bckgnd backgnd2"></div>
<div style="position:absolute; z-index: 2;display:none; background-color:#000;" class="bckgnd backgnd3"></div>
</div>
A better approach:
You don't need all those backgnd2 classes since you have only those DIVs inside a common parent.
Don't use inline styles! Use your stylesheet.
Don't use fixed width (px). Use % for responsive design.
2000*1331px images are
not suited for the web. Specially not for mobile devices. Care about
your user's bandwidth. When setting a background-image to cover you
don't need to worry about it being repeated.
Make your JS more flexible to element's indexes, count your elements using length.
Create a "current index counter", iterate over it increment it and
resetting using % (reminder).
For a better UX, allow the user to pause on hover.
Here's an eample:
jQuery(function($) { // DOM ready. $ alias in scope.
$('.gallery').each(function() {
var $gal = $(this),
$sli = $gal.find(">*"),
tot = $sli.length,
c = 0,
itv = null;
$sli.hide().eq(c).show(); // Hide all but first slide
function anim() {
c = ++c % tot; // increment/reset counter
$sli.fadeOut().eq(c).stop().fadeIn();
}
function play() {
itv = setInterval(anim, 3000);
}
function pause() {
clearInterval(itv);
}
$gal.hover(pause, play); // Pause on hover
play(); // Start loop
});
});
.gallery {
position: relative;
height: 200px;
}
.gallery>* {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: none 50%;
background-size: cover;
}
<div class="gallery">
<div style="background-image:url(http://placehold.it/800x600/0bf?text=1)"></div>
<div style="background-image:url(http://placehold.it/800x600/f0b?text=2)"></div>
<div style="background-image:url(http://placehold.it/800x600/0fb?text=3)"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
First put the firstDiv, lastDiv in their own variables.
Then you will need something like this
if (currentDiv != lastDiv) {
var nextdiv = allDivs.eq(nextDivIndex); //find the next visible div
} else {
var nextdiv = firstDiv; //the next div will be the first div, resulting in a loop
}
nextdiv.fadeIn(400); //fade it in
Tell me if you need more help.
You need to use 2 timeouts to make it loop. A timeout only fires once. The FadeOutDivs function counts down, each time setting a timeout to call itself. Then at zero it fades sets a timeout the call fadeInDivs which start the whole cycle over.
I've got this running on codepen.
$(document).ready(function () {
var interval = 2000;
var fadeDuration = 400;
var allImages = $('.bckgnd');
var count = allImages.length - 1;
var imageCount = allImages.length;
setTimeout(fadeOutDivs, interval);
function fadeOutDivs() {
allImages.eq(count).fadeOut(fadeDuration);
console.log(count);
if (count > 1) {
count--;
setTimeout(fadeOutDivs, interval);
} else {
count = allImages.length - 1;
setTimeout(fadeInDivs, interval)
}
}
function fadeInDivs() {
allImages.fadeIn(fadeDuration);
setTimeout(fadeOutDivs, interval);
}
});