176

For example:

AA33FF = valid hex color

Z34FF9 = invalid hex color (has Z in it)

AA33FF11 = invalid hex color (has extra characters)

vsync
  • 118,978
  • 58
  • 307
  • 400
Alex
  • 66,732
  • 177
  • 439
  • 641
  • 13
    depending on context, the last one could be a valid color, if it includes alpha in `AARRGGBB` format. – J. Holmes Nov 06 '11 at 14:28
  • Does this answer your question? [How to identify a given string is hex color format](https://stackoverflow.com/questions/1636350/how-to-identify-a-given-string-is-hex-color-format) – xji Jun 13 '21 at 23:26

10 Answers10

410

Without transparent support:

/^#[0-9A-F]{6}$/i.test('#AABBCC')

With transparent support :

/^#[0-9A-F]{6}[0-9a-f]{0,2}$/i.test('#AABBCC80')

To elaborate:

^ -> match beginning
# -> a hash
[0-9A-F] -> any integer from 0 to 9 and any letter from A to F
{6} -> the previous group appears exactly 6 times
[0-9a-f]{0,2} -> adding support for transparent ( 00..FF)
$ -> match end
i -> ignore case

If you need support for 3-character HEX codes (no transparent supported) , use the following:

/^#([0-9A-F]{3}){1,2}$/i.test('#ABC')

The only difference here is that

 [0-9A-F]{6}

is replaced with

([0-9A-F]{3}){1,2}

This means that instead of matching exactly 6 characters, it will match exactly 3 characters, but only 1 or 2 times. Allowing ABC and AABBCC, but not ABCD

Combined solution :

var reg=/^#([0-9a-f]{3}){1,2}$/i;
console.log(reg.test('#ABC')); //true
console.log(reg.test('#AABBCC')); //true
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • 21
    By definition this is correct, but codes with a length of 3 are valid for browser interpretation, too. `color: #f00;` will be interpreted as red (#ff0000) aswell. – Smamatti Nov 06 '11 at 14:13
  • 14
    or another form: `/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test("#f00")` – J. Holmes Nov 06 '11 at 15:22
  • How to use this? I am getting the input hexadecimal color from a JS prompt. – Archisman Panigrahi Jun 15 '14 at 05:24
  • @ArchismanPanigrahithe prompt returns a value of string which you can test – Royi Namir Sep 28 '15 at 12:18
  • I added an option in mine to make the '#' character optional. (I just added a question mark after the pound char) If user is entering this hex value they may or may not know to include the pound char. with modification: /(^#?[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test('f00') – sitting-duck Oct 30 '15 at 17:32
  • 10
    I would also add `/^#([0-9a-f]{3}){1,2}$/i` to the mix. – MasterAM Mar 01 '16 at 12:57
  • 1
    @AndresSepar `/^#[0-9A-F]{3,6}$/i.test('#aabb')` also passes, but `#aabb` isn't a valid hex color. – Roman Boiko Sep 02 '16 at 07:33
  • 4
    var isOk = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i.test('#aabbcc'); @RomanBoiko this is right! Thanks! – Andres Separ Sep 05 '16 at 22:56
  • Great answer, I tested with this; #010A Should this be accepted, as it isn't? – Andy B Jan 15 '18 at 18:29
  • 2
    To include #AARRGGBB format (in addition to #RGB and #RRGGBB already covered): `/^#(([A-F0-9]{2}){3,4}|[A-F0-9]{3})$/i` (and non-capturing version for improved efficiency: `^#(?:(?:[A-F0-9]{2}){3,4}|[A-F0-9]{3})$`). https://regexr.com/3p38r – Maxim Paperno May 07 '18 at 21:08
  • Great answer! How to make the '#' optional? is this ok? `/^#?([0-9A-F]{3}){1,2}$/i.test('ABC')` – Gil Epshtain Apr 02 '20 at 12:23
  • Awesome work! It really works for both 3 and 6 hexadeximal codes. – alexventuraio Jan 23 '21 at 04:49
  • Use this to make it work for 3,4,6,8 hex: 3: #f00 (red using single char per color element) 4: #f000 (red using single char per color element with alpha channel at 0) 6: #ff0000 (red using two chars per color element) 8: #ff000000 (red using two chars per color element with alpha channel at 0) – Zuks Sep 26 '21 at 11:21
  • @MaximPaperno You’re missing `,4` in `{3}` (it should be `{3,4}`) in both of your examples to match `#abcd`. – kleinfreund Aug 27 '22 at 20:06
  • what about transparency? #000000 FF <=?? – garik Jul 27 '23 at 15:58
  • 1
    @garik edited. Thanks. – Royi Namir Aug 18 '23 at 06:29
42

// regular function
function isHexColor (hex) {
  return typeof hex === 'string'
      && hex.length === 6
      && !isNaN(Number('0x' + hex))
}

// or as arrow function (ES6+)
isHexColor = hex => typeof hex === 'string' && hex.length === 6 && !isNaN(Number('0x' + hex))

console.log(isHexColor('AABBCC'))   // true
console.log(isHexColor('AABBCC11')) // false
console.log(isHexColor('XXBBCC'))   // false
console.log(isHexColor('AAXXCC'))   // false

This answer used to throw false positives because instead of Number('0x' + hex), it used parseInt(hex, 16).
parseInt() will parse from the beginning of the string until it reaches a character that isn't included in the radix (16). This means it could parse strings like 'AAXXCC', because it starts with 'AA'.

Number(), on the other hand, will only parse if the whole string matches the radix. Now, Number() doesn't take a radix parameter, but luckily, you can prefix number literals to get a number in other radii.

Here's a table for clarification:

╭─────────────┬────────────┬────────┬───────────────────╮
│ Radix       │ Characters │ Prefix │ Will output 27    │
╞═════════════╪════════════╪════════╪═══════════════════╡
│ Binary      │ 0-1        │ 0b     │ Number('0b11011') │
│ Octal       │ 0-7        │ 0o     │ Number('0o33')    │
│ Decimal     │ 0-9        │ -      │ -                 │
│ Hexadecimal │ 0-9A-F     │ 0x     │ Number('0x1b')    │
╰─────────────┴────────────┴────────┴───────────────────╯
Gust van de Wal
  • 5,211
  • 1
  • 24
  • 48
fflorent
  • 1,596
  • 9
  • 11
  • 9
    +1 bcs much better to read and faster to understand than a regex – Chris Apr 11 '13 at 13:42
  • 16
    @Chris 'because' is also much better to read and faster to understand than 'bcs' ;-) – Chris Oct 09 '13 at 14:12
  • 1
    @Chris: i got so used to 'bcs' for me doesnt make a difference. anyways my comment was meant as a compliment so be happy. – Chris Oct 26 '13 at 21:23
  • 12
    This is wrong: parseInt('abcZab', 16) will output number and pass the test – Salvador Dali Feb 14 '14 at 02:05
  • 1
    @fflorent Because `parseInt` will take `"abcZab"`, find that `"Z"` is invalid (for radix 16), and ignore it and anything after it. It then takes the beginning `"abc"` and convert it to `2748` (which is also the result of `parseInt("abcZab", 16)`, proving that's the logic happening). As the name implies, `parseInt` **parses** a string. Just like if you were parsing a number with units on it with a radix of 10, like `parseInt("10px", 10)`, you'd get `10`. You can see it described here: http://es5.github.io/#x15.1.2.2 (step 11) – Ian Sep 22 '14 at 21:25
  • 1
    @fflorent Really appreciate the honesty! +1 for that! – Jessica Mar 03 '16 at 00:46
  • you can compare the result with `source.ToString(16)` and you don't get false positives anymore. – Bart Calixto Jul 12 '16 at 15:11
  • Why only compare with length of 6? Hex color code length can be 3 as well. – reika Oct 15 '17 at 10:18
  • Add a length check and it works and is probably easier to read than a regex – rotato poti Apr 08 '19 at 22:13
  • You can resolve the issue of abcZab by splitting the string into characters and applying the condition on every character. Then it picks Z up and returns false. `s.split('').every(char=>!isNaN(parseInt(char, 16)));` – George Aug 30 '19 at 13:18
  • "ddffaaa" returns false positive – Rik van Velzen Dec 15 '19 at 18:09
8

This can be a complicated problem. After several attempts I came up with a fairly clean solution. Let the browswer do the the work for you.

Step 1: Create a div with border-style set to none. The div can be positioned off screen or it can be any div on your page that doesn't use the borders.

Step 2: Set the border color to an empty string. The code might look something like this:

e=document.getElementbyId('mydiv');
e.style.borderColor="";

Step 3: Set the border color to the color you aren't sure about.

e.style.borderColor=testcol;

Step 4: Check to see if the color actually got changed. If testcol is invalid, no change will occur.

col2=e.style.borderColor;
if(col2.length==0) {alert("Bad Color!");}

Step 5: Clean up after yourself by setting the color back to an empty string.

e.style.borderColor="";

The Div:

<div id="mydiv" style="border-style:none; position:absolute; left:-9999px; top:-9999px;"></div>

Now the JavaScript function:

function GoodColor(color)
{
   var color2="";
   var result=true;
   var e=document.getElementById('mydiv');
   e.style.borderColor="";
   e.style.borderColor=color;
   color2=e.style.borderColor;
   if (color2.length==0){result=false;}
   e.style.borderColor="";
   return result;
}

In this case, the function is returning a true/false answer to the question, the other option is to have it return a valid color value. Your original color value, the value from borderColor or an empty string in place of invalid colors.

Terry Prothero
  • 121
  • 1
  • 2
6

If you are trying to use it in HTML Try using this pattern Directly :

 pattern="^#+([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"

like

<input id="hex" type="text" pattern="^#+([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$" />

It will give a validation to match the requested format.

Mohit Dhawan
  • 543
  • 6
  • 11
1
function validColor(color){
  var $div = $("<div>");
  $div.css("border", "1px solid "+color);
  return ($div.css("border-color")!="")
}

https://gist.github.com/dustinpoissant/22ce25c9e536bb2c5a2a363601ba261c

Note: This requires jQuery

This works for ALL color types not just hex values. It also does not append unnecessary elements to the DOM tree.

Dustin Poissant
  • 3,201
  • 1
  • 20
  • 32
  • Nice and easy and works very well. Personally I added if(hexString.indexOf('#') == -1) { return false; } to check for a hash as a rudimentary check that color was a hex value – 365SplendidSuns Mar 03 '18 at 06:37
1

If you need a function to tell you if a color is valid, you might as well have it give you something useful -- the computed values of that color -- and return null when it is not a valid color. Here's my stab at a compatible (Chrome54 & MSIE11) function to get the RGBA values of a "color" in any of the formats --be it 'green', or '#FFF', or '#89abcd', or 'rgb(0,0,128)', or 'rgba( 0, 128, 255, 0.5)'.

/* getRGBA:
  Get the RGBA values of a color.
  If input is not a color, returns NULL, else returns an array of 4 values:
   red (0-255), green (0-255), blue (0-255), alpha (0-1)
*/
function getRGBA(value) {
  // get/create a 0 pixel element at the end of the document, to use to test properties against the client browser
  var e = document.getElementById('test_style_element');
  if (e == null) {
    e = document.createElement('span');
    e.id = 'test_style_element';
    e.style.width = 0;
    e.style.height = 0;
    e.style.borderWidth = 0;
    document.body.appendChild(e);
  }

  // use the browser to get the computed value of the input
  e.style.borderColor = '';
  e.style.borderColor = value;
  if (e.style.borderColor == '') return null;
  var computedStyle = window.getComputedStyle(e);
  var c
  if (typeof computedStyle.borderBottomColor != 'undefined') {
    // as always, MSIE has to make life difficult
    c = window.getComputedStyle(e).borderBottomColor;
  } else {
    c = window.getComputedStyle(e).borderColor;
  }
  var numbersAndCommas = c.replace(new RegExp('[^0-9.,]+','g'),'');
  var values = numbersAndCommas.split(',');
  for (var i = 0; i < values.length; i++)
    values[i] = Number(values[i]);
  if (values.length == 3) values.push(1);
  return values;
}
Abacus
  • 2,041
  • 1
  • 18
  • 23
1

To write a good validation, I started from writing the positive and negative tests, establishing a baseline for which my validation code should work by, and mixing a few of the good answers here and my own logic, I came up with this:

/^#(([0-9A-Fa-f]{2}){3,4}|[0-9A-Fa-f]{3})$/.test(hex)

http://www.regexplained.co.uk

Tests Cases:

(hopefully covering over 90% of cases)

const isValidHex = hex => /^#(([0-9A-Fa-f]{2}){3,4}|[0-9A-Fa-f]{3})$/.test(hex);

// positive tests
[
  '#ffffff',   // 6-characters, valid range
  '#ffffff99', // 9-characters, last 2 for alpha channel
  '#fff',      // 3-characters
].forEach(c => console.log(isValidHex(c), c));

console.log('\nshould fail:\n\n');

// negative tests
[
  '#invalid',   // obviously not a color
  '#f',         // 1 character is not enough
  '#ff',        // 2 characters is not enough
  '#ffff',      // 4 characters is not enough
  '#fffff',     // 5 characters is not enough
  '#fffffff',   // 7 characters is not enough
  '#ffffff999', // 9 characters are too many
  '#ggg',       // HEX is base 16, so characters beyond "F" (16) are invalid
].forEach(c => console.log(isValidHex(c), c))
vsync
  • 118,978
  • 58
  • 307
  • 400
0

Add a length check to make sure that you don't get a false positive

function isValidHex(testNum){
  let validHex = false;
  let numLength = testNum.length;
  let parsedNum = parseInt(testNum, 16);
  if(!isNan(parsedNum) && parsedNum.length===numLength){
     validHex = true;
  }
  return validHex;

}

rotato poti
  • 121
  • 1
  • 7
0

I decided to try a different perspective. My rules were : 1)Arbitrarily long sequence of hex characters, 2)Usage of either "0x" or "#" at the front of a sequence, or 3)Just a hex number or string. I'm thinking back to when binhex was one of the big programs. Binhex would create really large files that could be transferred to anyplace and then made back in to whatever file it was you converted. These files would have 80 characters followed by a return. So in this function I look for returns and take them back out first. Modified the function so it looks for both the "\n" and "\r". Here is the code:

////////////////////////////////////////////////////////////////////////////////
//  isHex(). Is this a hex string/value?
//  Arguments   :   0   =   Item to test
//                  1   =   V(alue) or S(tring). Default is STRING.
////////////////////////////////////////////////////////////////////////////////
function isHex()
{
    var p = 0;
    var re1 = /(\n|\r)+/g;
    var re2 = /[\Wg-zG-Z]/;
    var re3 = /v/i;
//
//  Make sure the string is really a string.
//
    var s = arguments[0];
    if( typeof s != "string" ){ s = s.toString(); }
//
//  Check if this is a hex VALUE
//  and NOT a hex STRING.
//
    var opt = arguments[1];
    if( re3.test(opt) && (s.length % 2 > 0) ){ return "false"; }
//
//  Remove any returns. BinHex files can be megabytes in length with 80
//  column information. So we have to remove all returns first.
//
    s.replace( re1, "" );
//
//  IF they send us something with the universal "0x" or the HTML "#" on the
//  front of it - we have to FIRST move where we look at the string.
//
    if( s.substr(0,1) == "#" ){ p = 1; }
        else if( s.substr(0,2).toLowerCase() == "0x" ){ p = 2; }

   if( re2.test(s.substr(p,s.length)) ){ return "false"; }

   return "true";
}

alert("HELLO There!");
alert(isHex("abcdef"));
alert(isHex("0x83464"));
alert(isHex("#273847"));
alert(isHex("This is a test"));
alert(isHex(0x5346));

I have purposefully left the return values as strings but all you have to do is to remove the double quotes and the function will then just return TRUE or FALSE.

Mark Manning
  • 1,427
  • 12
  • 14
0

If ARGB is expected to be supported, the supported hexadecimal digits are 3, 4, 6, 8.

const isColor = (strColor) => {
  return /^#(([0-9A-Fa-f]{2}){3,4}|[0-9A-Fa-f]{3,4})$/.test(strColor)
}

Note that {2} should precede {3,4} to ensure that the 7-digit hexadecimal can be correctly checked as false.

cyanking
  • 1
  • 2