How to stop random color from repeating? - javascript

the title changes color randomly when clicked but it repeates the same color often (I don't want it to repeat the same color in the array twice in a row). I tried to fix this with an if statement but im not sure why it didn't work.
var title = document.querySelector(".title");
function changeColor() {
let newArray = ["DarkSalmon", "LightSalmon", "Crimson", "Red",
"DeepPink", "YellowGreen", "GhostWhite"
];
let random = Math.floor(Math.random() *
Math.floor(newArray.length - 1));
console.log(random);
if (title.style.color !== newArray[random]) {
title.style.color = newArray[random];
}
}
title.addEventListener("click", changeColor);
<h1 class='title'>Berserk</h1>

If don't want colors to get repeated in a row, you could just add an else statement to your if statement.
I found the error! It's in the capitalization of your color names... Change them to all lowercase and it will magically
var title = document.querySelector(".title");
function changeColor() {
let newArray = ["darksalmon", "lightsalmon", "crimson", "red", "deeppink", "yellowgreen", "ghostwhite"];
let random = Math.floor(Math.random() * Math.floor(newArray.length - 1));
if (title.style.color != newArray[random]) {
title.style.color = newArray[random];
console.log(title.style.color);
} else {
changeColor();
}
}
title.addEventListener("click", changeColor);
<h1 class='title'>Berserk</h1>

This will let each color be selected exactly once. Once all colors have been used, the list is refreshed so all colors can be randomly selected again, and so on.
// Identifies header element & defines initial colors array and colors array
const header = document.getElementsByClassName("title")[0];
const initialColors = ["DarkSalmon", "LightSalmon", "Crimson", "Red", "DeepPink", "YellowGreen"];
let colors = [];
// Calls `changeColor` when the header is clicked
header.addEventListener("click", changeColor);
// Defines the `changeColor` function
function changeColor(){
// Remembers the most recent color (formatted as lower-case)
let previousColor = header.style.color.toLowerCase();
// Makes all colors available whenever no colors are left (including on first click)
if(colors.length === 0){
// Copies the `initialColors` array, and returns the copy
// (See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)
colors = initialColors.slice();
}
// Cuts a random color out of the colors array
let newColor = removeRandFrom(colors);
// If removed color happens to match previous color, forces an additional change
// (Can only ever happen immediately after the `colors` array has been replenished)
if(newColor === previousColor){
newColor = removeRandFrom(colors);
}
// Sets text color to whichever color was just randomly removed
header.style.color = newColor;
}
// Defines the `removeRandFrom` helper function
function removeRandFrom(arr){
// Gets a psuedo-random number
const index = Math.floor(Math.random() * arr.length);
// Removes and returns the element at the randomly selected index
// (See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)
return arr.splice(index, 1);
}
<h1 class='title'>Berserk</h1>
References:
MDN - slice
MDN - splice

The problem is that when the color would repeat and your if statement prevents that from happening, nothing else happens, either. So the effect is the same as a repeating color.
Instead of the if statement, you could use a ẁhile loop to randomly select new colors until you have one that is different from the current.
let random;
do {
random = Math.floor(Math.random() * Math.floor(newArray.length - 1));
} while (title.style.color === newArray[random]);
title.style.color = newArray[random];

Related

How to generate an new Array using a return from another function? Javascript

So im trying to make a color guessing game
So i created a function that generates a color and returns it, which works fine
But since i have 3 buttons with #hex code on them 1 button has to be a correct answer and correct #hex has to be displayed in a color box
So im trying to put 3 #hex codes in each button and choose randomly which #hex code is the correct one and display it in HTML
i know the code is very bad but i still got a long way to go...
function randomColor(){
let randomColor = Math.floor(Math.random()*16777215).toString(16);
return randomColor
}
function arrayOfColors(){
let randColorArr = []
}
function generateColors(){
colorBox.style.backgroundColor = `#${correctColor}`
btn.forEach(button => button.innerHTML = `#${randomColor()}`)
}
generateColors()
randomColor - I made a correction to your function that will always return a valid color number,
arrayOfColors - I answered your question about the function and it now returns an array with three colors for your three buttons,
randomIntFromInterval - designed to generate numbers in a certain range that you determine,
generateColors -
I drew an array of three colors,
I drew a lottery for the position in the array that would be the correct color,
I gave the div the correct color, and the buttons all three colors including a click event that will check if the user won,
Immediately after clicking, a new game starts.
const colorBox = document.querySelector('div');
const btns = document.querySelectorAll('button');
function randomColor(){
let randomColor = `#${Math.floor(Math.random() * 0x1000000).toString(16).padStart(6, '0')}`;
return randomColor
}
function arrayOfColors(){
const arrayOfColors = [...Array(3).keys()].map(() => randomColor());
return arrayOfColors;
}
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min)
};
function generateColors(){
const arr = arrayOfColors();
const correctIndexColor = randomIntFromInterval(0, 2);
colorBox.style.backgroundColor = `${arr[correctIndexColor]}`;
btns.forEach((button, index) => {
button.innerHTML = `${arr[index]}`;
button.onclick = () => {
if(index === correctIndexColor)
alert('win');
else
alert('faild');
generateColors();
}
});
}
generateColors()
div {
height: 100px;
width: 100px;
}
<button></button>
<button></button>
<button></button>
<div></div>
I don't see the need to declare a arrayOfColors function, when you can just generate a random number between 1 and 3 and assign correctColor to the relative button:
const btn = document.querySelectorAll('button')
const colorBox = document.getElementById('cbox')
function randomColor(){
let randomColor = Math.floor(Math.random()*16777215).toString(16);
return randomColor
}
function generateColors(correctColor){
colorBox.style.backgroundColor = `#${correctColor}`
btn.forEach(button => button.innerHTML = `#${randomColor()}`)
// GET RANDOM NUMBER BETWEEN 1 AND 3
const rndBtnIndex = Math.floor(Math.random() * (3 - 1 + 1) + 1)
// ASSIGN THE CORRECT COLOR TO RANDOM BUTTON
document.querySelector('#btn' + rndBtnIndex).innerHTML = `#${correctColor}`
}
generateColors('ff0000')
<button id="btn1"></button>
<button id="btn2"></button>
<button id="btn3"></button>
<div id="cbox">
<p>colorBox</p>
</div>

javascript: clicking numerical boxes - increment not working

using the code below, I've created a grid of buttons, 5x5, with random 1-25 numbers assigned to each button. They are to be clicked in numerical order, each's background turns red when clicked in the correct order. I can't use a global variable for this prompt. Without a global variable, I can't figure out how to increment the correctNumbers function which checks whether the right number is clicked each time. I think I'm missing something, a js function or something that would enable an incrementing variable declared within the incrementing function. I'm not looking for the whole explanation, just tips on functions i might not know about, and whether or not what i'm trying to do just isn't logicly possible.
<div id="numbers" class="hidden"></div>
<div id="youWon" class="hidden">You Won!</div>
The relevant JS:
... /**
* Gives the numbers a random order
* the "Fisher-Yates shuffle" found at: https://www.frankmitchell.org/2015/01/fisher-yates/
* #param {*} array
*/
const shuffle = (array) => {
let i = 0,
j = 0,
temp = null
for (i = array.length - 1; i > 0; i -= 1) {
j = Math.floor(Math.random() * (i + 1))
temp = array[i]
array[i] = array[j]
array[j] = temp
}
}
/**
* Generates an array of numbers 1-25
*/
const generateNums = () => {
document.getElementById("youWon").classList.toggle("hidden", "visible");
const numberArray = [];
for (let a = 1; a <= 25; a++) {
numberArray.push(a);
}
shuffle(numberArray);
let numEl = document.getElementById('numbers'); //write into html div id "numbers"
for (let b = 0; b <= 24; b++) { //loop to create button array
let newBtn = document.createElement('button'); //create buttons
newBtn.className = 'number'; //assign newBtns 'number' class
newBtn.innerText = numberArray[b]; //assign numbers to each button
numEl.appendChild(newBtn); //match with number elements in "numbers" array
newBtn.addEventListener("click", onNumberClick) //create function trigger
}
}
/**
* Creates a function to decide correct and incorrect clicks
* When a user clicks a number, if it is the next number in order, then it turns a different color for the remainder of the test
* If it is the wrong number, nothing happens
* #param {*} event
*/
const incrementNum = (correctNumber) => {
correctNumber++;
}
const onNumberClick = (event) => {
let correctNumber = 1; //start at 1
let numberVal = event.target; //apply it to clicks on the numbers
if (Number(numberVal.innerHTML) + 1 == incrementNum(correctNumber)) {
incrementNum(correctNumber);
numberVal.classList.add("red");
}
if (correctNumber == 26) {
document.getElementById("youWon").classList.toggle("visible"); //show win message if 25 is the last button and gets clicked
}
}
I would suggest that you count the number of elements in the DOM that have the class "red" and add 1... checking if the innerHTML is equal to that number to get the sequence right. So, instead of this:
if (Number(numberVal.innerHTML) + 1 == incrementNum(correctNumber)) {
incrementNum(correctNumber);
numberVal.classList.add("red");
}
You can have something like this:
if(Number(numberVal.innerHTML) == document.getElementsByClassName('red').length + 1) {
numberVal.classList.add("red");
}

Simple way to avoid previous number in Math.random?

I am working on a random quote/color generator. I want it to avoid repeating previous color (in a simple/easy/understandable way as I'm a complete beginner in JS/jQuery).
Here is my code, I don't know what is wrong with it.
var colors = ["#8ee5ee", "#ee82ee", "#469649", "#ff4444", "#ffa500", "#dddddd", "#efc3c8", "#d2d449", "#f91589","#906161","#875d39","#ffdab9","#d1e529","#3a718b"];
var color = Math.floor(Math.random() * colors.length);
var lastcolor = 0;
while(color === lastcolor){
color = Math.floor(Math.random() * colors.length) + 1;
}
$("body").animate({backgroundColor: colors[color]}, 1000);
$("#new-quote").animate({backgroundColor: colors[color]}, 1000);
$("h6").fadeOut(1000);
$("p").fadeOut(1000);
});
});
Basically, when I click a button (#new-quote), the current background color changes to another random color and so on. But now and then the color doesn't change as the machine chose the same number/color as the current one. I'm trying to avoid that!
I guess there's some sort of loop around the code you provided. Then you shouldn't reset the color, but to assign it to the previous one.
var colors = ["#8ee5ee", "#ee82ee", "#469649", "#ff4444", "#ffa500", "#dddddd", "#efc3c8", "#d2d449", "#f91589","#906161","#875d39","#ffdab9","#d1e529","#3a718b"];
var color = Math.floor(Math.random() * colors.length);
while(color === lastcolor){
color = Math.floor(Math.random() * colors.length) + 1;
}
lastcolor = color;
And of course you should define your lastcolor at the beginning of the script. Place
var lastcolor = 0;
after <script> tag opens.
You could use simply a new random value, because if you add one, you might get undefined if the random value is the index of the last element.
while (color === lastcolor){
color = Math.floor(Math.random() * colors.length); // + 1;
// without ^^^^^^^
}
A better style would be one assignment with a do ... while loop.
var colors = ["#8ee5ee", "#ee82ee", "#469649", "#ff4444", "#ffa500", "#dddddd", "#efc3c8", "#d2d449", "#f91589", "#906161", "#875d39", "#ffdab9", "#d1e529", "#3a718b"],
color,
lastcolor = 0;
do {
color = Math.floor(Math.random() * colors.length);
} while(color === lastcolor);
lastcolor = color;
Simpler method: Use splice() method to remove the current element from your colors array so that every time you run the code, the previously occurring element wont occur again.
var color = Math.floor(Math.random() * colors.length);
//from index = color, delete that element using splice(index, # of elements to delete)
colors.splice(color, 1);
//now colors array will not contain the colors[color] element
for (var i = 0; i < colors.length; i++) {
console.log(colors[i]);
}

Calling a JS function to invert background-color inside a jQuery .each function

There is a question in this post about calling a JS function inside a jQuery document ready function; however, it does not answer my question.
I am trying to write a function that loops over the divs in an html doc, giving the div backgrounds a random color. This portion is working. In the next step, I want to invert the divs background color using the invertColor function; however, I cannot figure out how to incorporate this function into the jQuery code.
The working code should first assign each div a random color and then invert that color using the invertColor function.
I tried calling the invertColor function both inside and outside of the .each function.
Here is the current JS code:
function invertColor(hexTripletColor) {
var color = hexTripletColor;
color = color.substring(1); // remove #
color = parseInt(color, 16); // convert to integer
color = 0xFFFFFF ^ color; // invert three bytes
color = color.toString(16); // convert to hex
color = ("000000" + color).slice(-6); // pad with leading zeros
color = "#" + color; // prepend #return color;
}
$(document).ready(function() {
$("div").each(function() {
var color = ["#b27485", "#74b2a1", "#cedbd9", "#abece4", "#ecabb3", "#1cdb4f", "#7e8dab", ];
var rand = Math.floor(Math.random() * color.length);
$(this).css("background-color", color[rand]);
$(this).css(invertColor(color));
$(function() {
invertedColor(color);
});
});
});
The HTML code and output can be viewed in this fiddle:
invertColor does not return anything so you are setting css(undefined); and I am not sure where hexTripletColor is defined. And you are passing the array and not the color from the array to your method. $(this).css(invertColor(color));
So you are looking for something like this, I set the color to the inverted value that you calculate, not sure what exactly you were trying to set.
function invertColor(hexTripletColor) {
var color = hexTripletColor;
color = color.substring(1); // remove #
color = parseInt(color, 16); // convert to integer
color = 0xFFFFFF ^ color; // invert three bytes
color = color.toString(16); // convert to hex
color = ("000000" + color).slice(-6); // pad with leading zeros
color = "#" + color; // prepend #return color;
return {
color: color
};
}
$(document).ready(function() {
$("div").each(function() {
var color = ["#b27485", "#74b2a1", "#cedbd9", "#abece4", "#ecabb3", "#1cdb4f", "#7e8dab", ];
var rand = Math.floor(Math.random() * color.length);
$(this).css("background-color", color[rand]);
$(this).css(invertColor(color[rand]));
});
});
invertColor() doesn’t return anything, so just add a return statement at the end like so:
function invertColor(hexTripletColor) {
var color = hexTripletColor;
color = color.substring(1); // remove #
color = parseInt(color, 16); // convert to integer
color = 0xFFFFFF ^ color; // invert three bytes
color = color.toString(16); // convert to hex
color = ("000000" + color).slice(-6); // pad with leading zeros
color = "#" + color; // prepend #return color;
return color;
}
Then change $(this).css(invertColor(color)); to $(this).css("background-color", invertColor(color));

Assign random picked color with javascript

I have a grid in my page that I want to populate via javascript with 200 elements. The actual code that populate the .grid element is the following:
$(function () {
var $gd = $(".grid");
var blocks="";
for(i=0; i < 200; i++){
blocks += '<div class="block"></div>';
}
$gd.append(blocks);
});
What I'm trying to do now is assign to each element created a random picked color from a list. Lets say red, blue, yellow, green (unexpected eh?). I'd like the values to be the most random possible, and also to avoid the same color to be picked again twice consequentially (just to be clear, it's ok something like red-blue-red-green-blue and so on, NOT red-red-green-yellow).
Maybe this can help in the randomize process, Fisher–Yates Shuffle, but I don't how to implement the non-twice adjacent rule stated above (if it is possible to apply at all).
What would be the best way to achieve this result? I'm also wondering if I could apply a gradient to each .block instead of a flat hex color; I guess the better route for this would be to assign a random class to each element mapped in CSS for the gradient and so on..
If the above script can be optimized performance-wise, I apreciate any suggestions!
Additional info:
I'm using jQuery
The grid is composed with 20 elements per row, for 10 rows
The colors should be 4, but can be raised to 5-7 adding some neutral grey tones if it can help
Here is a pen to experiment with http://codepen.io/Gruber/pen/lDxBw/
Bonus feature request: as stated above I'd like to avoide duplicate adjacent colors. Is it possible to avoid this also "above and below"? I guess its very hard if not impossible to totally avoid this, but well if anyone can find a solution it would be awesome!
Something like this, where the "nope" marked element is prevented, while the "yep" diagonal marked are allowed:
$(function () {
var colors = ["red","blue","green","yellow"];
var $gd = $(".grid");
var previousColor;
var blocks="";
for(i=0; i < 200; i++){
var color = "";
while(color === previousColor) {
color= colors [Math.floor(Math.random()*colors .length)];
}
blocks += '<div class="block" style="color:' + color + '"></div>';
previousColor = color;
}
$gd.append(blocks);
});
First, I'd use classes for the colors:
CSS:
.red { background-color: red; }
.blue { background-color: blue; }
.green { background-color: green; }
.yellow { background-color: yellow; }
And then here's the javascript:
$(document).ready(function() {
var colors = ["red","blue","green","yellow"];
var $gd = $(".grid");
var previousColor;
var previousRow;
var rowSize = 10;
while(rowSize--) previousRow.push("none");
var blocks = "";
for(i=0; i < 200; i++){
var color = colors [Math.floor(Math.random()*colors .length)];
while((color == previousColor) || (color == previousRow[i%rowSize])) {
color = colors [Math.floor(Math.random()*colors .length)];
}
blocks += '<div class="block ' + color + '"></div>';
previousColor = color;
previousRow[i%rowSize] = color;
}
$gd.append(blocks);
});
I started off with something similar to MikeB's code but added a row element so we know what is above your current block.
The first thing I'd like to introduce is a filtered indexing function.
Given an array:
var options = ['red', 'green', 'blue', 'purple', 'yellow']; // never less than 3!
And a filter:
function filterFunc(val) {
var taken = { 'red': 1, 'blue': 1 };
return taken[val] ? 0 : 1;
}
We can take the nth item from the values permitted (==1) by the filter (not a quick way to do it, but until there is a performance constraint...):
// filteredIndex returns nth (0-index) element satisfying filterFunc
// returns undefined if insufficient options
function filteredIndex(options, filterFunc, n) {
var i=-1, j=0;
for(;j<options.length && i<n; ++j) {
i += filterFunc(options[j]);
if(i==n)
break;
}
return options[j];
}
So now we can pick up a value with index 2 in the filtered list. If we don't have enough options to do so, we should get undefined.
If you are populating the colours from, say, the top left corner, you can use as few as 3 colours, as you are only constrained by the cells above and to the left.
To pick randomly, we need to set up the filter. We can do that from a list of known values thus:
function genFilterFunc(takenValues) {
var takenLookup = {};
for(var i=0; i < takenValues.length; ++i) {
takenLookup[takenValues[i]] = 1;
}
var filterFunc = function(val) {
return takenLookup[val] ? 0 : 1;
};
return filterFunc;
}
We can choose a random colour, then, for a cell in a grid[rows][cols]:
function randomColourNotUpOrLeft(grid, row, col, options, ignoreColour) {
var takenlist = [];
if(row > 0 && grid[row-1][col] != ignoreColour) {
takenlist.push(grid[row-1][col]);
}
if(col > 0 && grid[row][col-1] != ignoreColour) {
takenlist.push(grid[row][col-1]);
}
var filt = genFilterFunc(takenlist);
var randomIndex = Math.floor(Math.random()*(options.length-takenlist.length));
var randomColour = filteredIndex(options, filt, randomIndex);
return randomColour;
}
Note here that the random index used depends on how many colours have been filtered out; if there are 4 left we can have 0-3, but if only 2 are left it must be 0-1, etc. When the adjacent cells are the same colour and/or we are near the boundary, there is less constraint about which colour is chosen. Finally fill in a grid:
function fillGridSpeckled(grid, options, nullColour) {
for(var row=0; row<grid.length; ++row) {
for(var col=0; col<grid[row].length; ++col) {
grid[row][col] = randomColourNotUpOrLeft(grid,row,col,options,nullColour);
}
}
}
I've put it all in this jsbin, along with a few bits to demo the code working.

Categories

Resources