Working with hex strings and hex values more easily in Javascript

77,626

Solution 1

No, there is no way to tell the JavaScript language to use hex integer format instead of decimal by default. Your code is about as concise as it gets but note that you do not need to prepend the "0x" base indicator when you use "parseInt" with a base.

Here is how I would approach your problem:

function addHexColor(c1, c2) {
  var hexStr = (parseInt(c1, 16) + parseInt(c2, 16)).toString(16);
  while (hexStr.length < 6) { hexStr = '0' + hexStr; } // Zero pad.
  return hexStr;
}

addHexColor('aaaaaa', '010101'); // => 'ababab'
addHexColor('010101', '010101'); // => '020202'

As mentioned by a commenter, the above solution is chock full of problems, so below is a function that does proper input validation and adds color channels separately while checking for overflow.

function addHexColor2(c1, c2) {
  const octetsRegex = /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i
  const m1 = c1.match(octetsRegex)
  const m2 = c2.match(octetsRegex)
  if (!m1 || !m2) {
    throw new Error(`invalid hex color triplet(s): ${c1} / ${c2}`)
  }
  return [1, 2, 3].map(i => {
    const sum = parseInt(m1[i], 16) + parseInt(m2[i], 16)
    if (sum > 0xff) {
      throw new Error(`octet ${i} overflow: ${m1[i]}+${m2[i]}=${sum.toString(16)}`)
    }
    return sum.toString(16).padStart(2, '0')
  }).join('')
}

addHexColor2('aaaaaa', 'bogus!') // => Error: invalid hex color triplet(s): aaaaaa / bogus!
addHexColor2('aaaaaa', '606060') // => Error: octet 1 overflow: aa+60=10a

Solution 2

How about this:

var hexValue = "aaaaaa";
hexValue = (parseInt(hexValue, 16) + 0x010101).toString(16);
document.writeln(hexValue); // outputs 'ababab'

There is no need to add the 0x prefix if you use parseInt.

Solution 3

I think accepted answer is wrong. Hexadecimal color representation is not a linear. But instead, 3 sets of two characters are given to R, G & B.

So you can't just add a whole number and expect to RGB to add up correctly.

For Example

n1 = '005500'; <--- green
n2 = '00ff00'; <--- brighter green

Adding these numbers should result in a greener green. In no way, adding greens should increase RED to increase. but by doing what accepted answer is doing, as in just treat whole number as one number then you'd carry over for numbers adding upto greater than f, f+1 = 10.

you get `015400` so by adding greens the RED increased .... WRONG

adding 005500 + 00ff00 should result in, = 00ff00. You can't add more green to max green.

Solution 4

For folks looking for a function that can add and subtract HEX colors without going out of bounds on an individual tuple, I wrote this function a few minutes ago to do just that:

export function shiftColor(base, change, direction) {
  const colorRegEx = /^\#?[A-Fa-f0-9]{6}$/;

  // Missing parameter(s)
  if (!base || !change) {
    return '000000';
  }

  // Invalid parameter(s)
  if (!base.match(colorRegEx) || !change.match(colorRegEx)) {
    return '000000';
  }

  // Remove any '#'s
  base = base.replace(/\#/g, '');
  change = change.replace(/\#/g, '');

  // Build new color
  let newColor = '';
  for (let i = 0; i < 3; i++) {
    const basePiece = parseInt(base.substring(i * 2, i * 2 + 2), 16);
    const changePiece = parseInt(change.substring(i * 2, i * 2 + 2), 16);
    let newPiece = '';

    if (direction === 'add') {
      newPiece = (basePiece + changePiece);
      newPiece = newPiece > 255 ? 255 : newPiece;
    }
    if (direction === 'sub') {
      newPiece = (basePiece - changePiece);
      newPiece = newPiece < 0 ? 0 : newPiece;
    }

    newPiece = newPiece.toString(16);
    newPiece = newPiece.length < 2 ? '0' + newPiece : newPiece;
    newColor += newPiece;
  }

  return newColor;
}

You pass your base color as parameter 1, your change as parameter 2, and then 'add' or 'sub' as the last parameter depending on your intent.

Share:
77,626

Related videos on Youtube

brentonstrine
Author by

brentonstrine

I like doing things manually. But I'm finally picking up on how incredibly useful all these new tools are.

Updated on March 03, 2022

Comments

  • brentonstrine
    brentonstrine about 2 years

    I have some code which takes strings representing hexadecimal numbers - hex colors, actually - and adds them. For example, adding aaaaaa and 010101 gives the output ababab.
    However, my method seems unnecessarily long and complicated:

    var hexValue = "aaaaaa";
    hexValue = "0x" + hexValue;
    hexValue = parseInt(hexValue, 16);
    hexValue = hexValue + 0x010101;
    hexValue = hexValue.toString(16);
    document.write(hexValue); // outputs 'ababab'

    The hex value is still a string after concatenating 0x, so then I have to change it to a number, then I can add, and then I have to change it back into hex format! There are even more steps if the number I'm adding to it is a hexadecimal string to begin with, or if you take into consideration that I am removing the # from the hex color before all this starts.

    Surely there's a simpler way to do such simple hexadecimal calculations! And just to be clear, I don't mean just putting it all on one line like (parseInt("0x"+"aaaaaa",16)+0x010101).toString(16) or using shorthand - I mean actually doing less operations.

    Is there some way to get javascript to stop using decimal for all of its mathematical operations and use hex instead? Or is there some other method of making JS work with hex more easily?

    • Ansel Halliburton
      Ansel Halliburton almost 12 years
      "get Javascript to stop using decimal for all of its mathematical operations and use hex instead" - No-no. JS uses neither. It's actually binary. You are talking about strings here.
  • Bob
    Bob over 7 years
    This is very neat. What happens if you add 0x010101 to 0xFFFFFF ? Or 0x000100 to 0x00FF00 - you're going to get some weird results because there are actually three octet pairs here and they should be summed separately with limits >00<FF and then concatenated back together.
  • Alacritas
    Alacritas almost 4 years
    Everything you say is true, but I think this is more of a comment than an answer, since you don't actually provide an answer to the question. If you provide a solution here -- to do it correctly, as you describe -- then it should even be the accepted answer. As it is I will downvote this...and will gladly change it if you either move this to a comment or answer the question.
  • Alacritas
    Alacritas almost 4 years
    This should be the accepted answer, it actually provides a working solution.