What I'm trying to achieve is to loop and toggle a class on every individual div every second.
Below is the code to create 4 divs with colour from an array.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
const ball = container.querySelectorAll("div");
for (let i = 0; i < colors.length; i++) {
const balls = document.createElement("div");
balls.style.backgroundColor = colors[i];
container.appendChild(balls);
}
Now I want to loop through the containers element every second and Google a class on the elements.
I tried using the setInterval() method
I've added a class "ball" you could as well refer to element by container > div, but easier to understand this way.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
const ball = container.querySelectorAll("div");
for (let i = 0; i < colors.length; i++) {
const balls = document.createElement("div");
balls.style.backgroundColor = colors[i];
balls.classList.add('ball');
container.appendChild(balls);
}
setInterval(function() {
toggle();
}, 1000)
function toggle() {
const tog = document.querySelector('.class-to-toggle');
if (tog) {
tog.classList.remove('class-to-toggle');
const nextTog = tog.nextElementSibling;
if (nextTog) {
nextTog.classList.add('class-to-toggle');
} else {
document.querySelector('.ball').classList.add('class-to-toggle');
}
} else {
document.querySelector('.ball').classList.add('class-to-toggle');
}
}
<div class="container">
</div>
if you inspect in dev tools you'll see class changed
Here is another example of the way you could do this.
Your question lacks some details about what you need to do so the example is quite generic.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
colors.forEach(color => {
const ball = document.createElement("div");
ball.classList.add("ball");
// Added some stylnig value for the example
ball.style.width = "20px";
ball.style.height = "20px";
ball.style.backgroundColor = color;
container.appendChild(ball);
});
const balls = container.querySelectorAll(".ball");
let cursor = 0;
const int_id = setInterval(selectNextBall, 1000);
function selectNextBall() {
const selectedBall = balls[cursor];
// I do this just to illustrate the example,
// could be anything you need to do with your ball.
for (const ball of balls) {
ball.style.border = ball === selectedBall ? "1px solid black" : "none";
}
// for an endless loop
cursor = cursor === balls.length - 1 ? 0 : cursor + 1;
// Or if the loop need to iterate just once
/*
if (cursor === balls.length - 1) {
clearInterval(int_id);
}
cursor++
*/
}
<div class="container">
</div>
I have a carousel of slides in my react.js project without using any libraries. When I use an odd amount of images everything works. But when I use even amount, although currentIndex is changing properly only odd images are displayed like 1,3,5 in this example with six images. Can anyone spot what is wrong with my code so it would work with ane amount of images not only with odd ones? Thanks very much
import React from 'react';
import Slide from './Slide';
import img1 from "../assets/img1.jpg";
import img2 from "../assets/img2.jpg";
import img3 from "../assets/img3.jpg";
import img4 from "../assets/img4.jpg";
import img5 from "../assets/img5.jpg";
import img6 from "../assets/img6.jpg";
class Test extends React.Component {
state = {
currentIndex: 0,
images: [img1, img2, img3, img4, img5, img6]
}
prevSlide = () => {
const lastIndex = this.state.images.length - 1;
const resetIndex = this.state.currentIndex === 0;
const index = resetIndex ? lastIndex : this.state.currentIndex - 1;
this.setState({
currentIndex: index
});
};
nextSlide = () => {
const lastIndex = this.state.images.length - 1;
const resetIndex = this.state.currentIndex === lastIndex;
const index = resetIndex ? 0 : this.state.currentIndex + 1;
this.setState({
currentIndex: index
});
};
render() {
const index = this.state.currentIndex;
let newImagesArray = this.state.images.slice(index, index + 6);
if (newImagesArray.length < 6) {
newImagesArray = newImagesArray.concat(
this.state.images.slice(0, 6 - newImagesArray.length)
);
}
return (
<div className="paint__container">
{newImagesArray.map((image, i) =>
this.state.currentIndex === i ? (
<Slide key={i} url={image} alt="" />
) : null
)}
<div className="left__arrow" onClick={this.prevSlide}></div>
<div className="right__arrow" onClick={this.nextSlide}></div>
</div>
);
}
}
export default Test;
okay, thank you for providing the full code, looking at the component on github
we can find
you have nextSlide defined twice, where the second I guess will overwrite the first declaration
while you have the currentIndex in state why you are searching for the target slide in your render function? you don't have to do this my friend, while currentIndex correctly calculate the index then you just render the slide at that index, that's why we are using react after all
render() {
const index = this.state.currentIndex;
const images = this.state.images;
return (
<div className="paint__container">
<Slide url={images[index]} alt="" />
<div className="left__arrow" onClick={this.prevSlide}></div>
<div className="right__arrow" onClick={this.nextSlide}></div>
</div>
);
}
I have a problem with my script, and that is that I want to play an audio when I click on a .bbp button, but this button is inside a hidden div that is then cloned.
Only when the cloned div becomes visible in the DOM, I want to play an audio when I click on .bbp, but it does not work for me.
SEE DEMO LIVE (Codepen) - The Snippet does not run on Stackoverflow
Note that if you comment #products, the audio assigned to .bbp yes will play, otherwise it will NOT play, since the audio
script can not identify if #products is visible in the DOM or not.
So, first I need to know that .bbp is visible, and I can not find how I can do it.
Any idea...?
Thanks in advance!
//-----------------
HTML & CSS
#products {display:none}
#derecha {display:none}
<div class="comprar">Clone 1</div> <!--Clone the div from "products" to "derecha"-->
<div class="bbp">X</div> <!--Delete the cloned div placed into "derecha"-->
SCRIP (Play Audio)
let audioHolderComprar = {};
$('.comprar').click(()=>{
let tempIdentifier = Date.now();
audioHolderComprar[tempIdentifier] = new Audio('comprar.mp3');
audioHolderComprar[tempIdentifier].play();
setTimeout(() => {
delete audioHolderComprar[tempIdentifier];
}, audioHolderComprar[tempIdentifier].duration + 1200);
});
//------------------
let audioHolderBorrar = {};
$('.bbp').click(()=>{
let tempIdentifier = Date.now();
audioHolderBorrar[tempIdentifier] = new Audio('borrar.mp3');
audioHolderBorrar[tempIdentifier].play();
setTimeout(() => {
delete audioHolderBorrar[tempIdentifier];
}, audioHolderBorrar[tempIdentifier].duration + 1200);
});
As I've mentioned in my comment, you have two places where you handle the click event for .bpp - these interfere with each other.
Also you're mixing the places where you should add html and javascript code. Though it works, it's a little bit messy.
Replace all of the content in your HTML pane on the left by this:
<div id="container">
<div id="productos">
<!-- =============== -->
<div id="cont-p1" class="cont-p">
<div id="producto-1">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>cont-p1 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div></div>
</div> <!-- // productos -->
<div class="derecha" id="derecha"></div> <!-- // div derecha -->
<div id="comp-p1" data-clone="cont-p1" class="comp-clone comprar">Clone 1</div>
<div class="cont-num" id="clicks">0</div>
<div class="cont-num" id="clicksdos">0</div>
<div id="cont-resultado">
<input name="total" id="total">
</div>
<div id="cont-note">How to play the audio on the button to close the cloned div <span>.bbp</span><br>( <span class="red">X</span> ),<br>if the audio script can not know that it has been cloned...?
<br><br>
Note the CSS (line 3) that the div container of the all divĀ“s that must be cloned is in <span>display=none</span>, but if you comment this line it can reproduce the audio onclick in the X button</div>
</div> <!-- // container -->
and all of the following goes into the JS pane to the right:
/*
https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js
*/
let audioHolderComprar = {};
$('.comprar').click(()=>{
let tempIdentifier = Date.now();
audioHolderComprar[tempIdentifier] = new Audio('https://notificationsounds.com/soundfiles/8b16ebc056e613024c057be590b542eb/file-sounds-1113-unconvinced.mp3');
audioHolderComprar[tempIdentifier].play();
// removing after play process gets over so if won't consume memory
setTimeout(() => {
delete audioHolderComprar[tempIdentifier];
}, audioHolderComprar[tempIdentifier].duration + 1200 /* you can remove threshold value if you wants to */);
});
//------------------
let audioHolderBorrar = {};
let clicks = 0;
let clicksdos = 0;
const safeInt = (key) => {
let value = parseInt(getValue(key));
return (isNaN(value) || value < 0) ? 0 : value;
}
// This loads our clicks from the LocalStorage
const loadClicks = () => {
clicks = safeInt('clicks');
clicksdos = safeInt('clicksdos');
}
const loadHTML = () => {
return getValue('html', '');
}
const loadFromStorage = () => {
let html = loadHTML();
if (html !== '') {
loadClicks();
}
displayClicks();
document.querySelector(".derecha").innerHTML = html;
}
// Display the clicks on the screen
const displayClicks = () => {
clicks = (clicks === NaN) ? 0 : clicks;
clicksdos = (clicksdos === NaN) ? 0 : clicksdos;
document.querySelector('#clicks').innerHTML = clicks;
document.querySelector('#clicksdos').innerHTML = clicksdos;
// Hide / Show Result
let display = (clicks > 0) ? 'block' : 'none';
document.querySelector('#cont-resultado').style.display = display;
document.querySelector('.derecha').style.display = display;
//document.querySelector('#aviso-producto-agregado').style.display = "block";
}
const adjustClicks = (value) => {
clicks += value;
clicksdos += value;
storeValue('clicks', clicks);
storeValue('clicksdos', clicksdos);
displayClicks();
}
const addClick = () => adjustClicks(1);
const removeClick = () => adjustClicks(-1);
// Manage localStorage
const storeValue = (key, value) => (localStorage) ? localStorage.setItem(key, value) : '';
const getValue = (key, defaultValue) => (localStorage) ? localStorage.getItem(key) : defaultValue;
const storeHTML = () => storeValue("html", document.getElementsByClassName("derecha")[0].innerHTML);
// Add a node to the Derecha
const addToDerecha = (nodeId) => {
let node = document.querySelector(`#${nodeId}`);
document.querySelector('.derecha').appendChild(node.cloneNode(true));
storeHTML();
displaySuma();
};
// Monitor ALL click events
document.addEventListener('click', (event) => {
let target = event.target;
// Add
if (target.matches('.comp-clone')) {
addClick();
addToDerecha(event.target.dataset.clone);
}
// Remove
if (target.matches('.bbp')) {
let tempIdentifier = Date.now();
audioHolderBorrar[tempIdentifier] = new Audio('https://notificationsounds.com/soundfiles/99c5e07b4d5de9d18c350cdf64c5aa3d/file-sounds-1110-stairs.mp3');
audioHolderBorrar[tempIdentifier].play();
// removing after play process gets over so if won't consume memory
setTimeout(() => {
delete audioHolderBorrar[tempIdentifier];
}, audioHolderBorrar[tempIdentifier].duration + 1200 /* you can remove threshold value if you wants to */);
getParent('.derecha', target).removeChild(target.parentNode);
removeClick();
storeHTML();
displaySuma();
}
});
// This is just a helper function.
const getParent = (match, node) => (node.matches(match)) ? node : getParent(match, node.parentNode);
// New Script for sum inputs
//const displaySuma = () => document.getElementById("total").value = suma();
const displaySuma=()=>document.getElementById("total").value=suma().toLocaleString("es-ES");
const suma = function() {
return Array.from(document.querySelectorAll(".derecha div .add-prod"))
.reduce((a, v) => a + parseFloat(v.value), 0);
}
// Code to run when the document loads.
document.addEventListener('DOMContentLoaded', () => {
if (localStorage) {
loadFromStorage();
}
displaySuma();
});
</script>
<script>
// Displays the new product alert added when the scroll is detected in the div #derecha
var displaced = document.getElementById('derecha')
if (displaced.scrollHeight > displaced.offsetHeight) {
document.getElementById("notice-product-added").style.display = "block";
};
// LocalStorage for the div #notice-product-added
const showMsgCart=localStorage.getItem('showMsgCarrito');if(showMsgCart==='false'){$('#notice-product-added').hide();}$('#notice-product-added').on('click',function(){$('#notice-product-added').fadeOut('slow');localStorage.setItem('showMsgCarrito','false');});
After that you should hear the closing sound.
I am working on an exercise in the Udemy Advanced Webdeveloper Bootcamp. The exercise asked to come up with a page of 32 boxes that randomly change colour (every x seconds). My solution is not exactly that. I change the color of all 32 boxes at the same time. It almost works. I get random 32 boxes initially, but does not change the color later. My console tells me I am doing something wrong with the setState. But I cannot figure out what. I think my changeColor is a pure function:
import React, { Component } from 'react';
import './App.css';
class Box extends Component {
render() {
var divStyle = {
backgroundColor: this.props.color
}
return(
<div className="box" style={divStyle}></div>
);
}
}
class BoxRow extends Component {
render() {
const numOfBoxesInRow = 8;
const boxes = [];
for(var i=0; i < numOfBoxesInRow; i++) {
boxes.push(<Box color={this.props.colors[i]} key={i+1}/>);
}
return(
<div className="boxesWrapper">
{boxes}
</div>
);
}
}
class BoxTable extends Component {
constructor(props) {
super(props);
this.getRandom = this.getRandom.bind(this);
this.changeColors = this.changeColors.bind(this);
this.state = {
randomColors: this.getRandom(this.props.allColors, 32) // hardcoding
};
this.changeColors();
}
changeColors() {
setInterval(
this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
render () {
const numOfRows = 4;
const rows = [];
for(let i=0; i < numOfRows; i++) {
rows.push(
<BoxRow colors={this.state.randomColors.slice(8*i,8*(1+i))} key={i+1}/>
)
}
return (
<div className="rowsWrapper">
{rows}
</div>
);
}
}
BoxTable.defaultProps = {
allColors: ["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige",
"Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood",
"CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk",
"Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGrey",
"DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","Darkorange",
"DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue",
"DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink",
"DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite",
"ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray",
"Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo",
"Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon",
"LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray",
"LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen",
"LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue",
"LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon",
"MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple",
"MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise",
"MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin",
"NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed",
"Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed",
"PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple",
"Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown",
"SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray",
"SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle",
"Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"]
}
export default BoxTable
You are calling this.setState before the component has mounted (from the constructor). Try instead making your first this.ChangeColors from the componentDidMount lifecycle function.
Additionally, it's not a bad idea to clear the interval when it unmounts in componentWillUnMount
Edit: By changing the interval to call after the first wait you do prevent the error, for now. I'd recommend using the lifecycle functions to build good habits. It's just a temporary assignment, but in a full project you'd be putting yourself at risk to break the component again later by making it possible to call this.setState before it is reliably mounted.
You need to use a lambda function in order to use setState inside setInterval
setInterval(() => {
this.setState({randomColors: this.getRandom(this.props.allColors,
32)});
}, 5000)
try to change your changeColors function to like this:
changeColors() {
setInterval(() => this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
the first param of setInterval is function, in your original code you already executed this setState and didn't passed the function itself
You will need to update the state after the component creation phase, inside componentDidMount()
class BoxTable extends Component {
constructor(props) {
super(props);
this.getRandom = this.getRandom.bind(this);
this.changeColors = this.changeColors.bind(this);
this.state = {
randomColors: this.getRandom(this.props.allColors, 32) // hardcoding
};
// delete this line
//this.changeColors();
}
// replace changeColors by componentDidMount,
// this function will be called automatically by react
componentDidMount() {
setInterval(
this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
render () {
const numOfRows = 4;
const rows = [];
for(let i=0; i < numOfRows; i++) {
rows.push(
<BoxRow colors={this.state.randomColors.slice(8*i,8*(1+i))} key={i+1}/>
)
}
return (
<div className="rowsWrapper">
{rows}
</div>
);
}
}