Luis Martinez
Luis Martinez

Reputation: 27

Comparing RGB colors in JavaScript

I'm trying to compare two RGB colors in a guessing game. There are 6 squares with 6 different colors. When the user clicks on the color that matches pickedColor, the result should be 'Correct!'. But I never get that result.

This is my code:

var colors = [
    "rgb(255,0,0)",
    "rgb(255,255,0)",
    "rgb(0,255,0)",
    "rgb(0,255,255)",
    "rgb(0,0,255)",
    "rgb(255,0,255)"
];

var squares = document.querySelectorAll(".square");
var pickedColor = colors[3];
var colorDisplay = document.getElementById("colorDisplay");

colorDisplay.textContent = pickedColor;

for (var i = 0; i < squares.length; i++) {
    //add initinal colors to squares
    squares[i].style.background = colors[i];

    //add the click listener to the squares
    squares[i].addEventListener("click", function () {
        var clickedColor = this.style.background;

        if (clickedColor === pickedColor) alert("Correct!");
        else alert("Incorrect!");
    });
}

Upvotes: 0

Views: 6328

Answers (4)

Michael Laszlo
Michael Laszlo

Reputation: 12239

The trouble is that the color expression 'rgb(0,255,255)' is formatted differently by the browser. In the test clickedColor === pickedColor, you're comparing two strings that no longer look the same even if they represent the same color.

Different browsers can represent an RGB color in different ways, so it's unsafe to pick a particular format. A better approach is to extract the color components from the strings and compare the component values one by one, as in the code below.

function rgbExtract(s) {
  var match = /^\s*rgb\(\s*(\d+),\s*(\d+),\s*(\d+)\)\s*$/.exec(s);
  if (match === null) {
    return null;
  }
  return { r: parseInt(match[1], 10),
           g: parseInt(match[2], 10),
           b: parseInt(match[3], 10) };
}

function rgbMatches(sText, tText) {
  var sColor = rgbExtract(sText),
      tColor = rgbExtract(tText);
  if (sColor === null || tColor === null) {
    return false;
  }
  var componentNames = [ 'r', 'g', 'b' ];
  for (var i = 0; i < componentNames.length; ++i) {
    var name = componentNames[i];
    if (sColor[name] != tColor[name]) {
      return false;
    }
  } 
  return true;
}

The rgbMatches function is demonstrated in the following snippet. You'll see that you can now click on the square with the correct color and you'll get the appropriate message even though the underlying RGB strings are formatted differently.

var colors = [
  "rgb(255,0,0)",
  "rgb(255,255,0)",
  "rgb(0,255,0)",
  "rgb(0,255,255)",
  "rgb(0,0,255)",
  "rgb(255,0,255)"
];

var squares = document.querySelectorAll(".square");
var pickedColor = colors[3];
var colorDisplay = document.getElementById("colorDisplay");
colorDisplay.textContent = pickedColor;

function message(s) {
  document.getElementById('messageContainer').innerHTML = s;
}

function rgbExtract(s) {
  var match = /^\s*rgb\(\s*(\d+),\s*(\d+),\s*(\d+)\)\s*$/.exec(s);
  if (match === null) {
    return null;
  }
  return { r: parseInt(match[1], 10),
           g: parseInt(match[2], 10),
           b: parseInt(match[3], 10) };
}

function rgbMatches(sText, tText) {
  var sColor = rgbExtract(sText),
      tColor = rgbExtract(tText);
  if (sColor === null || tColor === null) {
    return false;
  }
  var componentNames = [ 'r', 'g', 'b' ];
  for (var i = 0; i < componentNames.length; ++i) {
    var name = componentNames[i];
    if (sColor[name] != tColor[name]) {
      return false;
    }
  } 
  return true;
}

for (var i = 0; i < squares.length; ++i) {
  var square = squares[i];
  square.style.background = colors[i];
  square.addEventListener("click", function () {
    var clickedColor = this.style.background;
    if (rgbMatches(clickedColor, pickedColor)) {
        message('Correct! ' + clickedColor + ' matches ' + pickedColor);
    } else {
        message('Incorrect. ' + clickedColor + ' doesn\'t match ' + pickedColor);
    }
  });
}
body {
  font-family: sans-serif;
}
.square {
  display: inline-block;
  margin: 5px;
  width: 50px;
  height: 50px;
  border: 1px solid #888;
  cursor: pointer;
}
.output {
  margin: 10px 5px;
}
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>

<div class="output" id="colorDisplay"></div>

<div class="output" id="messageContainer"></div>

Upvotes: 1

Oriol
Oriol

Reputation: 288690

Do not trust the color returned by the browser. Different browsers will use different formats.

Instead, use classes or data-* attributes to set the color, and check that:

var colors = ["red", "yellow", "lime", "cyan", "blue", "fuchsia"],
    squares = document.querySelectorAll(".square"),
    pickedColor = colors[Math.floor(Math.random()*colors.length)],
    message = document.getElementById("messageContainer");
document.getElementById("colorDisplay").textContent = pickedColor;
for (var i = 0; i < squares.length; ++i) {
  squares[i].setAttribute('data-color', colors[i]);
  squares[i].addEventListener("click", function () {
    var clickedColor = this.getAttribute('data-color');
    message.textContent = clickedColor === pickedColor
      ? "Correct!" : "Incorrect!";
  });
}
.square {
  display: inline-block;
  margin: 5px;
  width: 50px;
  height: 50px;
  border: 1px solid #888;
  cursor: pointer;
}
.output {
  margin: 10px 5px;
}
[data-color=red] { background: rgb(255,0,0) }
[data-color=yellow] { background: rgb(255,255,0) }
[data-color=lime] { background: rgb(0,255,0) }
[data-color=cyan] { background: rgb(0,255,255) }
[data-color=blue] { background: rgb(0,0,255) }
[data-color=fuchsia] { background: rgb(255,0,255) }
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="output" id="colorDisplay"></div>
<div class="output" id="messageContainer"></div>

Upvotes: 0

jsdeveloper
jsdeveloper

Reputation: 4055

I would be wary of comparing two rgb strings like this. Different browsers may store style background colors differently (as you have already discovered).

I would suggest writing a custom comparison function which parses two rgb strings and compares them.

Or you could convert them to hex color values and compare those instead.

Upvotes: 0

Pabs123
Pabs123

Reputation: 3435

Ok so I set up an example here and it looks like the problem is that your initial set of colours don't have spaces between the commas:

var colors = [
  "rgb(255,0,0)",
  "rgb(255,255,0)",
  "rgb(0,255,0)",
  "rgb(0,255,255)",
  "rgb(0,0,255)",
  "rgb(255,0,255)"
];

clickedColor has no spaces and the pickedColor does, so changing this to:

var colors = [
  "rgb(255, 0, 0)",
  "rgb(255, 255, 0)",
  "rgb(0, 255, 0)",
  "rgb(0, 255, 255)",
  "rgb(0, 0, 255)",
  "rgb(255, 0, 255)"
];

Should do the trick.

Upvotes: 4

Related Questions