I have an Array of partial hex values that get random letters appended to them forming a full hex value.
These are then randomly applied to div layers effectively shading them different colors. However what I get currently is a "Matisse" effect instead of variations of one color.
If you force var color = setHex(); to var color = '#CC0'; in the getRandomColor function you will see the effect I am after.
I want to know why the "Matisse" effect is happening when I should only be passing one hex value. How do I stop this?
See example here: http://jsfiddle.net/fyQhg/
// Set Hex
function setHex() {
var hexArray = ['#CC0','#FF9','#339'];
var randomHex = hexArray[Math.floor(Math.random() * hexArray.length)];
document.getElementById('inner').innerHTML = randomHex;
return randomHex;
}
// random color
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = setHex();
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 7)];
}
return color;
}
//ditribute random colors
function buttonClick() {
var i,j, colorblock = document.getElementsByClassName('shade');
for (i=0, j=colorblock.length; i<j; i++) {
colorblock[i].style.background = getRandomColor();
}
}
window.onload = buttonClick();
var base = setHex();
// random color
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = base;
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 7)];
}
return color;
}
Declaring the base outside of the method works. Example
Explaination
In this method:
for (i=0, j=colorblock.length; i<j; i++) {
colorblock[i].style.background = getRandomColor();
}
You are calling getRandomColor() repeatedly in that loop. Therefore you are also calling setHex() repeatedly which is creating a new random color each time the loop loops.
So by moving setHex() outside the method that is inside that loop into base you are effectively only calling setHex() once per load.
Why?
Because function getRandomColor() calls setHex() which returns something else every time.
How to stop?
var color = setHex();
for (i=0, j=colorblock.length; i<j; i++) {
colorblock[i].style.background = getRandomColor(color);
}
}
and
function getRandomColor(color) {
var letters = '0123456789ABCDEF'.split('');
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 7)];
}
return color;
}
Every time you call getRandomColor you are calling setHex and picking another random base color. Just set the base color once, store it in a variable and use it.
For example:
var hexBase;
function setHex() {
var hexArray = ['#CC0','#FF9','#339'];
if (!hexBase) {
hexBase = hexArray[Math.floor(Math.random() * hexArray.length)];
}
return hexBase;
}
Fiddle
It seems you were close to this solution because you were storing the result of setHex in a div, but you never checked it again. Also, you shouldn't need to store things in the DOM when you can just store them in JavaScript.
Finally, if you want to avoid the global variable, you can wrap the whole thing in a function (e.g. your onload function or a IIFE).
Related
The below code can display a single random letter in each box. However, a letter should not be able to appear on different boxes at the same time as one of the boxes. For example, box 1 displays "A", then box 2 and 3 cannot display "A" also.
function random() {
var letter = [];
for (var i = 65; i < 91; i++)
{
letter.push(String.fromCharCode(i));
}
return letter[Math.floor(Math.random() * letter.Length)];
}
function display()
{
document.getElementById("box1").textContent = random();
document.getElementById("box2").textContent = random();
document.getElementById("box3").textContent = random();
}
A good way to do this would be to overhaul your random function to generate all the letters at once, like so:
function randomN(n=3) {
const letters = new Set()
while (letters.size < 3) {
const i = Math.floor(Math.random() * (91-65)) + 65
letters.add(String.fromCharCode(i))
}
return [...letters]
}
function display() {
const [letter1, letter2, letter3] = randomN()
document.getElementById("box1").textContent = letter1
document.getElementById("box2").textContent = letter2
document.getElementById("box3").textContent = letter3
}
For a more modern approach you can utilize generators:
function* randomLetters() {
const letters = "QWERTYUIOPASDFGHJKLZXCVBNM".split('')
while (letters.length > 0) {
const i = Math.floor(Math.random() * letters.length)
yield letters[i]
letters.splice(i, 1)
}
}
function display() {
const letters = randomLetters()
document.getElementById("box1").textContent = letters.next()
document.getElementById("box2").textContent = letters.next()
document.getElementById("box3").textContent = letters.next()
/* and so on and so forth! */
}
function letterGenerator(n=1) {
var generated_letters = [];
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
for (var i=0; i<n; i++){
var random = Math.floor(Math.random() * alphabet.length);
var letter = alphabet.splice(random, 1)[0];
generated_letters.push(letter)
}
return generated_letters;
}
var letters = letterGenerator(3)
gives Array(3) [ "Q", "T", "I" ], for example.
by using splice, we are making sure the randomly chosen letters are removed from the alphabet variable.
you can then go over the letters with for (letter of letters) or something like that, and add each one to the desired element.
by the way, maybe run a document.querySelectorAll('[id^="box"]'); to get all elements and add to them with a for loop.
this, alongside the n parameter, allows for any number of generated letters.
(if you really want it to be generic, create the box elements using js as well)
the solutions attached in the comments are certainly clever.
I especially liked this one
I have a function that returns a random color. I push these colors into an array. I don't want the colors to be repeated in the array. So I did this:
$scope.getRandomColor = function getRandomColor(arrayToCheckIfAlreadyContains) {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
//check if array contains the generated color
if(arrayToCheckIfAlreadyContains.indexOf(color) >= 0){
let nonRepeatColor = $scope.getRandomColor(arrayToCheckIfAlreadyContains);
console.log("color repeated", color, arrayToCheckIfAlreadyContains);
return nonRepeatColor;
}
return color;
}
But I don't know if this is efficient or will even work for sure. Also, it would be great if the colors are distinguishable. Sometimes I get colors that are almost same. How do I make sure that doesn't happen.
hsl can help you produce distinguishable colors. Try this.
function makeColor(colorNum, colors){
if (colors < 1) colors = 1;
// defaults to one color - avoid divide by zero
return colorNum * (360 / colors) % 360;
}
// This could be length of your array.
var totalDIVs = 20;
var totalColors = totalDIVs;
for (var i = 0; i < totalDIVs; i++){
var element = document.createElement('div');
document.body.appendChild(element);
var color = "hsl( " + makeColor(i, totalColors) + ", 100%, 50% )";
element.style.backgroundColor = color;
element.innerHTML = color;
}
You might consider using hsl instead of hex notation - pick a number between 0 and 359 for the initial color, then select the other colors such that they're equidistant. For example:
function getColors(num) {
const initialColor = Math.floor(Math.random() * 360);
const increment = 360 / num;
const hsls = [];
for (let i = 0; i < num; i++) {
hsls.push(Math.round((initialColor + (i * increment)) % 360));
}
return hsls;
}
function displayNew() {
container.innerHTML = '';
const hsls = getColors(input.value);
hsls.forEach((hsl) => {
const div = container.appendChild(document.createElement('div'));
div.style.backgroundColor = 'hsl(' + hsl + ', 100%, 50%)';
});
}
#container > div {
height: 30px;
}
<input id="input" onkeyup="displayNew()" type="number">
<div id="container"></div>
From your code I don't quite understand what you are doing if the color is already in the array: do you want to pick another random color until you find one color that is not in the array?
Anyways, since your second goal (distinguishable colors), I guess you need some extra work: every time you pick a random color, you need to check its similarity against ALL the colors in the array!
Something like the following:
getRandomColor = function getRandomColor(arrayToCheckIfAlreadyContains) {
let colorFound = true;
let letters = '0123456789ABCDEF';
do {
colorFound = true;
var randomColor = '#';
for (var i = 0; i < 6; i++) {
randomColor += letters[Math.floor(Math.random() * 16)];
}
arrayToCheckIfAlreadyContains.some(color => {
if (distanceBetweenColor(color, randomColor) < TRESHOLD) {
/* Branch taken when randomColor is too similar
* to an already existing color. */
colorFound = false;
return true;
}
return false;
});
} while (!colorFound);
}
Now, how implementing distanceBetweenColor()? You should use Delta-E algorithm: I suggest you to read this answer in SO: https://stackoverflow.com/a/15189004/6070423
EDIT: Notice the use of some instead of forEach: doing this, you stop the iteration as soon as you find a color that is too similar.
Can you explain me what is wrong with this code ?
var a = document.getElementById("id1").style.color;
a = "blue" is supposed to set the text color of the element with id1 to blue
function randomColor() {
var cArr = '1234567890ABCDEF'.split('');
var c = "#";
for (var i = 0; i < 6; i++) {
c += cArr[Math.floor(Math.random() * 16)]
}
a = c;
return c;
}
randomColor();
Looks pretty good to me but for sure it isn't because it isn't working.
Can you explain me what am I doing wrong ?
You are setting a to a primitive value. Therfore you can not assign the color.
function randomColor() {
var c = "#";
for (var i = 0; i < 6; i++) {
c += (Math.random() * 16 | 0).toString(16);
}
return c;
}
var a = document.getElementById("id1").style;
a.color = randomColor();
<h1 id="id1">stackoverflow</h1>
a = "blue" is supposed to set the text color of the element with id1 to blue
No that not true a contain the current color of the element id1.
To set the color you could store the element in variable then after that set the blue color to it like following :
var a = document.getElementById("id1");
a.style.color = "blue";
Hope this helps.
Snippet
function randomColor() {
var cArr = '1234567890ABCDEF'.split('');
var c = "#";
for (var i = 0; i < 6; i++) {
c += cArr[Math.floor(Math.random() * 16)]
}
return c;
}
var a = document.getElementById("id1");
a.style.color = randomColor();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="id1">H1 element</h1>
To answer this question you need to understand how data is passed and stored in variables. You seem to know that objects are passed by reference, and if you store an object in several different variables you can modify one and it will be different in all of them.
However when you're storing an object's property in a variable, and that property has a plain data type such as number, string or boolean, that variable only stores the value not a reference. What you want to do is store the resulting element from your document.getElementById call in a el variable, then access its style.color within your function.
el.style.color = 'blue'
My requirement is I have to style a div dynamically with 3 colors.
I have done it with random colors applying this code
function randomColors() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 4; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
I have to repeat 3 Colors only, and that is going to be in a loop dynamically ! How can i do that?
I want only these 3 colors #09c5f9, #05f6d5, #a42cff dynamically set.
for(i=1; i <= pmap; i++){
$("#thumbnail"+i).css("background-color",randomColors());
}
I can think of two ways to do it.
1) The easiest if you're using jQuery (and In my opinion the correct way to do it) is to declare three classes and swap them using jQuery.
The CSS:
.teal{
background-color: #09c5f9;
}
.emerald{
background-color: #05f6d5;
}
.purple{
background-color: #a42cff;
}
and the javascript:
$("#some-element").removeClass("teal emerald").addClass("purple");
$("#some-element").removeClass("teal purple").addClass("emerald");
$("#some-element").removeClass("purple emerald").addClass("teal");
2) This way uses bare-metal JS and also uses the same programming style you used in the original post.
var colors = ["#09c5f9", "#05f6d5", "#a42cff"];
function getColor(colorNumber){
return colors[colorNumber];
}
Hope this helps.
function randomColors() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
you can see Demo, in which background-color of div will set randomly.
Demo
you can take hint from there.
So I've read a few similar questions, and I've managed to do things like change the background colour, but I have not yet been able to get this to work;
What I want, is for each subsequent letter on a page to be randomly coloured. The colour space used isn't really important, as that's easy to fix once it actually works (am using this one at the moment), but I can't even get the text to change colour yet. I'm hoping I'm just making a silly mistake somewhere...
This is what i'm trying at the moment; and it kind of works, but it's very dependant on what tagName i use, and because of the nature of most webpages, it can break a lot of things if i'm not careful...
jsFiddle
var elements = document.getElementsByTagName('p');
for(var i=0,l=elements.length;i<l;++i) {
var str = elements[i].textContent;
elements[i].innerHTML = '';
for(var j=0,ll=str.length;j<ll;++j) {
var n = document.createElement('span');
elements[i].appendChild(n);
n.textContent = str[j];
n.style.color = get_random_colour();
}
}
function get_random_colour() {
var letters = '0123456789ABCDEF'.split('');
var colour = '#';
for (var i = 0; i < 6; i++ ) {
colour += letters[Math.round(Math.random() * 15)];
}
return colour;
}
In this example, p works fine, and doesn't seem to break anything, but if I do * or html or body then it breaks the page. Is there a way to get all the text on the page, and not break it?
And another thing; I later changed the colour function to hopefully only pick colours that are in HSV(random,1,1) so that i only get nice bright colours, but it's not working. I'm presuming I just have some JS error in there, but I'm not that familiar with JS, so I'm finding it hard to find...
Here are the changes
To do this, you will want to recurse through just the text nodes, careful not to trash child HTML elements.
See the demo at jsFiddle.
var x = document.querySelector ("body"); // Etc.
buggerTextNodesIn (x);
function buggerTextNodesIn (node) {
var wrapClass = 'gmColorBarf';
function turnerizeTextNodes (node) {
if (node.nodeType === Node.TEXT_NODE) {
//--- Skip this node if it's already been wrapped.
if ( ! node.parentNode.classList.contains (wrapClass) ) {
var oldText = node.nodeValue;
var parent = node.parentNode;
for (var J = 0, len = oldText.length; J < len; ++J) {
var wrapSpan = document.createElement ("span");
wrapSpan.classList.add (wrapClass);
wrapSpan.textContent = oldText[J];
wrapSpan.style.color = getRandomColor ();
parent.insertBefore (wrapSpan, node);
}
parent.removeChild (node);
}
}
else if (node.nodeType === Node.ELEMENT_NODE) {
/*--- IMPORTANT! Start "bottom up" since we will be adding
gazillions of nodes and "top down" would skew our length measurement.
*/
for (var K = node.childNodes.length - 1; K >= 0; --K) {
turnerizeTextNodes (node.childNodes[K] );
}
}
}
turnerizeTextNodes (node);
}
function getRandomColor () {
var letters = '0123456789ABCDEF'.split ('');
var color = '#';
for (var J = 0; J < 6; ++J) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
Note that to get iframed content, the easiest way is to tune the #include, #exclude, and/or #match directives to trigger on the iframe URL(s) -- if they don't already.