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'
Related
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.
I have a for loop that i need to increase the value of on each iteration, and a few examples of what I have tried so far. I need to increase the value of tp on each loop through:
for (var t = 9; t < 20; t++) {
//Tried the below:
var timePeriod = report_data[i] + '.tp' + t.toString();
venues[i].scan_times[t] = timePeriod;
//and:
venues[i].scan_times[t] = report_data[i].tp + t;
}
The manual way of doing it, which I am trying to use the for loop to accomplish:
venues[i].scan_times['9'] = report_data[i].tp9;
venues[i].scan_times['10'] = report_data[i].tp10;
....
venues[i].scan_times['19'] = report_data[i].tp19;
venues[i].scan_times['20'] = report_data[i].tp20;
In javascript you can always access attributes in two ways: with bracket notation and dot notation
var myObj = {};
// those 2 lines are equivalent
myObj.a = 1;
myObj['a'] = 1
If you want to dynamically access some attributes you can use whatever you want inside the brackets.
// those 2 lines are equivalent
myObj['a' + 4] = 1;
myObj.a4 = 1;
In your case you can write
for (var t = 9; t < 20; t++) {
venues[i].scan_times[t] = report_data[i]['tp' + t];
}
I have a table with several rows (row number is changeable). It is showing the users details like usernames and IPs and country ....
I want to show the rows with the same IPs with the same colors.
But the content is not static and maybe I have two groups of similar IPs how can I show each group with one color ?
I tried to give the name attribute of each TR the value of the IP so all the group with the same IP has the same name attribute.
$txt .='<tr name="'.$ip.'"><td>'.$username.'</td><td>'.$ip.'</td><td>'.$country.'</td><td>'.$platform.'</td><td>'.$browser.'</td><td>'.$version.'</td><td>'.$os.'</td><td>'.$lastseen.'</td></tr>';
then I use javascript to give a background color for the same name <tr>
<script>
$(document).ready(function(){
$('table').find('tr').each(function(){
ip = $(this).attr('name');
$('table').find('tr').each(function(){
ip1 = $(this).attr('name');
if(ip == ip1)
{ $(this).css("background-color","red");}
});
});
});
</script>
the color of the rows is keep changing and if i have two groups of similar IPs i can not figure out how to separate them into different colors?
You could use the ip's itself to color the rows, by treating the blocks as RGB values and assign them as background color for the row.
e.g
192.168.0.1
R G B
background-color: rgb (192,168,0)
I have two groups of similar IPs
Since similar isn't specified
I'm using only the first 3 blocks to calcualte the colors since that would group the ips which would be in the same /24 subnet.
Actually assigning those values is pretty straightforward.
var table = document.getElementById ("ips");
var rows = table.querySelectorAll ("tr td");
for (var i=0,r=rows;i<r.length;i++) {
var td = r[i];
var ip = td.childNodes [0].data.replace (/\s/g,""); //trim whitespaces
var rgb = ip.split (".").slice (0,-1); //get the first 3 blocks (RGB)
td.style.background = "rgb(" + rgb + ")"; //[].toString () gives a comma seperated values string
}
Here is a simple example on JSFiddle, producing these colors:
The resulting colors may not be perceived as beautiful as colors chosen randomly from a predefined set, but it's a simple way to make sure ips, sharing the same blocks get colored the same way.
Edit
As #MohammedJoraid pointed out, the color distributions for ips, that differ only slightly in the same block, are perceptionally hard to distinguish.
The problem doesn't fade if you change the distribution because we already cover the whole rgb range when even taking only the first three blocks of the address into account.
But we can use an approach that at makes it easier to distinguis such, above mentioned, ips, more easily, by using a HSV colorspace instead of RGB to distribute the colors.
var table = document.getElementById("ips");
var rows = table.querySelectorAll("tr td");
for (var i = 0, r = rows; i < r.length; i++) {
var td = r[i],
ip = td.childNodes[0].data.replace(/\s/g, ""),
rgb = ip.split(".").slice(0, -1); //get the first three blocks
var h = rgb.reduce(function (a, b, i) {
return a + (b * (0.103005665*(1+i*2))) //lastColor + (currentColor * (2*blockNr+1/6*phi)) Practically putting a weight on the blocks further to the right
}, 0) % 1,
s = 0.5,
v = 0.85;
td.style.background = "rgb(" + hsvToRgb(h, s, v) + ")"; //assign the calculated rgb value
}
function hsvToRgb(h, s, v) {
var sec = ~~ (h * 6);
var f = h * 6 - sec;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
var r = 0xFF,
g = 0xFF,
b = 0xFF;
switch (sec) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q
}
return [~~ (r * 256), ~~ (g * 256), ~~ (b * 256)];
};
The new approach colors the table the following way
Heres another Fiddle
You have to do this using the following steps:
Check which IP's are all available
Create random colors for each IP
Apply colors to html elements
The code for this would look something like this:
$(document).ready(function(){
// First read all available IP addresses
var ips = [];
$('table').find('tr').each(function(){
ip = $(this).attr('name');
if (ips.indexOf(ip) == -1)
ips.push(ip);
});
// Now create some colors for the ips
var colors = [];
for (var i = 0; i < ips.length; i++) {
var color = get_random_color(); // Create some random color here
colors.push(color);
}
// Last step, apply colors to rows
$('table').find('tr').each(function(){
var ip = $(this).attr('name');
var color = colors[ips.indexOf(ip)];
$(this).css("background-color", color);
});
});
function get_random_color() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
I made this functin as general as possible to suite more scenarios. JS FIDDLE
You can color any table row that has the same value by specifying the class name for the table data. You can search by class name and you may add a class to your IP e.g
<td class="_ip"> to make finding ip field easier.
var ips = $("._ip"); // get an array of all <td> with _ip class.
var ip_color = {};//object to save ips with their color
//loop thru all td with the class_ip
$.each(ips, function(index, table_data) {
var ip = $.trim($(table_data).html());//table_data is the actual <td>
var color = getColor();
if (!ip_color[ip])
{ //use the ip as the object key, therefore we will end up with unique ips.
ip_color[ip] = {"color": color, "ref": [table_data]};// add a color and an array of td associated with an ip
}
else
{
ip_color[ip]["ref"].push(table_data);// we already have the color, we just add the td
}
});
// here we give each tr a color
$.each(ip_color, function(ip, details) { //loop thru all unique ips
$.each(details.ref, function(index, td) {// loop thru all td
$(td).closest('tr').css({'background-color': details.color});
});
});
function getColor()
{
return '#' + ('000000' + parseInt(Math.random() * (256 * 256 * 256 - 1)).toString(16)).slice(-6);
}
First find unique ips from trs and then change colors accordingly. js is like
var ips = [];
$('table').find('tr').each(function(){
ip = $(this).attr('name');
ips.push(ip);
});
ips = jQuery.unique(ips);
var colors = ['red','blue','green'];
var ie = 0;
$.each(ips,function(i,e) {
if(ie > colors.length-1)
ie = 0;
$('table tr[name="' + e + '"]').css('background-color',colors[ie]);
ie++;
});
you can find example in http://jsfiddle.net/DhqQ5/
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).
I have created an array of color values which represents a smooth transition of colors from red to blue.
Now I want this array to take you from red to blue and back again. The obvious solution is to append the reverse of the array to the array.
I have written code to do it, but it isn't working as I understand it should. Instead, it's creating the reversed array, repeated. Instead of "Red to Blue, Blue to Red", it's going "Blue to Red, Blue To Red".
Clearly, there's some behavior of arrays in javascript that I haven't grasped yet.
What should I be doing?
My first attempt is this:
colors = colors.concat(colors.reverse());
Based on the first stackoverflow answer, I tried this:
var arrayCopy = colors;
arrayCopy.reverse();
colors = colors.concat(arrayCopy);
But this produces identical results!
For context, here's the surrounding code:
///////////////////////////////////////////////////////////
// Creating the array which takes you from Red to Blue
//
var colorSteps = 400;
var startColor = [255, 0, 0];
var endColor = [0, 127, 255];
var steps = new Array();
var j = 0;
for (j = 0; j < 3; ++j) {
steps[j] = (endColor[j] - startColor[j]) / colorSteps;
}
var colors = Array();
for (j = 0; j < colorSteps; ++j) {
colors[j] = [
Math.floor(startColor[0] + steps[0] * j),
Math.floor(startColor[1] + steps[1] * j),
Math.floor(startColor[2] + steps[2] * j)
];
}
////////////////////////////////////////////////////////
// Here's the bit where I'm trying to make it a mirror
// of itself!
//
// It ain't working
//
colors = colors.concat(colors.reverse());
///////////////////////////////////////////////////////
// Demonstrating what the colors are
//
j = 0;
var changeColorFunction = function () {
if (++j >= colors.length) {
j = 0;
}
var colorName = "rgb(" + colors[j][0] + ", " + colors[j][1] + ", " + colors[j][2] + ")";
debugText.style.background = colorName;
debugText.innerHTML = j;
}
setInterval(changeColorFunction, 10);
The problem with:
colors = colors.concat(colors.reverse());
... is that colors.reverse() mutates the colors array itself, meaning that you're appending a reversed array to an already-reversed array. Try this instead:
colors = colors.concat(colors.slice().reverse());
Copy your colors array to somewhere first. reverse changes the array itself, not merely returns a reverted one.
UPDATE
Code sample:
colors.concat(colors.slice(0).reverse());