iamthestreets
iamthestreets

Reputation: 773

Change color based on background color

I am trying to change the color based on the background color to make the text more readable.

During my search I found this:

var rgb = $('.external-event').css('background-color');
var c = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
var o = Math.round(((parseInt(rgb[0]) * 299) + (parseInt(rgb[1]) * 587) + (parseInt(rgb[2]) * 114)) / 1000);

//console.log(o);
if (o > 125) {
  $('.external-event').css('color', 'black');
} else {
  $('.external-event').css('color', 'white');
}

$('.external-event').css('background-color', c);

var r = Math.round(Math.random() * 255);
var g = Math.round(Math.random() * 255);
var b = Math.round(Math.random() * 255);

rgb[0] = r;
rgb[1] = g;
rgb[2] = b;

The problem is that it's only displaying white color and not changing to black.

What am I doing wrong here?

Here is a JSFiddle.

for some reason this example is doing the opposite then my actual development environment and keeping the text in black even with a black background.

EDIT: I forgot to add I am dynamically displaying multiple background colors with the same class .external-event and it seems to be only getting the rgb value of the first element.

Upvotes: 3

Views: 2679

Answers (2)

Sᴀᴍ Onᴇᴌᴀ
Sᴀᴍ Onᴇᴌᴀ

Reputation: 8297

The issue:

.css('background-color') returns a string literal representing the background color in Functional format - i.e. rgb(R,G,B), where R is the red value, G is the green value, and B is the blue value.

The first character (i.e. rgb[0]) would be r, the second character would be g, etc...Additionally, passing a character to parseInt() (i.e. parseInt(rgb[0])) yields NaN. Consequently, NaN > 0 will always evaluate to false. So that is why your code example always sets the (text foreground) color to white.

Solutions:

In order to get the individual values for the red, green and blue numbers, one option is to remove the rgb() using String.replace() and split the values into an array using String.split(), like this:

var rgbValues = rgb.replace('rgb(','').replace(')','').split(', ');
//rgbValues:  ["0", "0", "0"]

Another option is to use a regular expression to match it, using .match() like in the example below.

var rgb = $('.external-event').css('background-color');
var pattern = /rgb\((\d+),\s?(\d+),\s?(\d+)\)/;
//matches will be an array containing the elements:
//0- the group matched i.e. rgb(0, 0, 0)
//1- the r value
//2- the g value
//3- the b value
var matches = rgb.match(pattern);

Note: Also see the section down below about parseInt().

See a demonstration by expanding the snippet below.

var rgb = $('.external-event').css('background-color');
//remove the rgb() characters and split into an array
var rgbValues = rgb.replace('rgb(','').replace(')','').split(', ');
//or use a regular expression
var pattern = /rgb\((\d+),\s?(\d+),\s?(\d+)\)/;
var matches = rgb.match(pattern);
if (matches.length && matches.length > 3) {
  var c = 'rgb(' + matches[1] + ',' + matches[2] + ',' + matches[3] + ')';
  var o = Math.round(((parseInt(matches[1], 10) * 299) + (parseInt(matches[2], 10) * 587) + (parseInt(matches[3], 10) * 114)) / 1000);

  console.log('rgb: ', rgb, ' c: ', c, ' o : ', o, ' matches: [' + matches.join(", ")+']', ' rgbValues: [' + rgbValues.join(", ")+']');
  if (o > 125) {
    $('.external-event').css('color', 'black');
  } else {
    $('.external-event').css('color', 'white');
  }

  $('.external-event').css('background-color', c);
}
.external-event {
  background-color: #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="external-event">
  <br />external event
  <br />
</div>

Edit

The question has been edited to state "I forgot to add I am dynamically displaying multiple background colors with the same class *.external-event* and it seems to be only getting the rgb value of the first element."

Use the jQyery method .each() to iterate over the elements containing the class .external-event. The callback can accept two parameters: the index as well as the element (same as this in the context of the callback).

var externalEvents = $('.external-event');
    externalEvents.each(function(index, externalEvent) {
        //check background color of externalEvent - 
        //$(this) == $(externalEvent)

See this demonstrated by expanding the code snippet below.

$(function() { //jQuery DOM-loaded callback
  var externalEvents = $('.external-event');
  externalEvents.each(function(index, externalEvent) {
    var rgb = $(externalEvent).css('background-color');
    //remove the rgb() characters and split into an array
    var rgbValues = rgb.replace('rgb(', '').replace(')', '').split(', ');
    //or use a regular expression
    var pattern = /rgb\((\d+),\s?(\d+),\s?(\d+)\)/;
    var matches = rgb.match(pattern);
    if (matches.length && matches.length > 3) {
      var c = 'rgb(' + matches[1] + ',' + matches[2] + ',' + matches[3] + ')';
      var o = Math.round(((parseInt(matches[1], 10) * 299) + (parseInt(matches[2], 10) * 587) + (parseInt(matches[3], 10) * 114)) / 1000);

      console.log('rgb: ', rgb, ' c: ', c, ' o : ', o, ' matches: [' + matches.join(", ")+']', ' rgbValues: [' + rgbValues.join(", ")+']');
      if (o > 125) {
        $(externalEvent).css('color', 'black');
      } else {
        $(externalEvent).css('color', 'white');
      }
      //This isn't needed
     // $(externalEvent).css('background-color', c);
    }
  });
});
.external-event {
  background-color: #000;
}

.external-event.orange {
  background-color: #fa0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="external-event">
  <br />external event
  <br />
</div>
<div class="external-event orange">
  <br />external event orange
  <br />
</div>

Note about using parseInt()

It is a good practice to pass the radix (typically of 10) to parseInt(), given the following:

If radix is undefined or 0 (or absent), JavaScript assumes the following:

  • If the input string begins with "0x" or "0X", radix is 16 (hexadecimal) and the remainder of the string is parsed.
  • If the input string begins with "0", radix is eight (octal) or 10 (decimal). Exactly which radix is chosen is implementation-dependent. ECMAScript 5 specifies that 10 (decimal) is used, but not all browsers support this yet. For this reason always specify a radix when using parseInt.
  • If the input string begins with any other value, the radix is 10 (decimal).1

So pass 10 as the radix, like below:

parseInt(matches[1], 10)

Upvotes: 2

iamthestreets
iamthestreets

Reputation: 773

With the help of Sam Onela's answer I was able to figure it out.

Here is what I did:

$('.external-event').each(function() {

    var rgb = $(this).css('background-color');
    var pattern = /rgb\((\d+),\s?(\d+),\s?(\d+)\)/;
    var matches = rgb.match(pattern);

    var o = Math.round(((parseInt(matches[1]) * 299) + (parseInt(matches[2]) * 587) + (parseInt(matches[3]) * 114)) /1000);

    //console.log(o);
    if(o > 125) {
        $(this).css('color', '#444444');
    }else{
        $(this).css('color', 'white');
    }

});

Upvotes: 0

Related Questions