Why doesn't this work? Even though the colour is equal to #ECECF4 it still alerts "No".
It is selecting the corrent element as I have tested it. Is there a better way to write this?
<script type="text/javascript">
function weekclick() {
if (document.getElementById('w1').style.backgroundColor == "#ECECF4") {
alert("Yes");
} else {
alert("No");
}
}
</script>
Comparing colors as part of the business logic should be avoided at all cost.
Instead, keep the logic in JavaScript and act according to a state kept somewhere. Then, if you want to send a visual feedback to the user through a change of color, add a class to the element. That way, JavaScript only knows about class names and the styling is always isolated in the CSS as it should.
$(".list").on("click", "li", function(){
$(this).toggleClass('active');
});
.list {
width: 100%;
padding: 0;
}
.list li {
padding: 5px 10px;
list-style: none;
cursor: pointer;
}
.list li:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.list li.active {
background-color: #eeeecc;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="list">
<li>test 1</li>
<li>test 2</li>
<li>test 3</li>
</ul>
That being said, OP's code works for me with Chrome 17.0.963.56 and Firefox 8.0. When you don't know what to compare, just use console.log() to see what it looks like.
Note that #ECECF4 is the same color as rgb(236, 236, 244) but in a different representation.
IE7 and IE8 output the HEX value, IE9 and up and other browsers output the RGB format. So comparing color with cross-browser compatibility is a tricky task and the best way to do it isn't beautiful.
I made a simple function which works for most cases with most browser, even with color set through CSS.
function weekclick() {
var elemColor = getStyle(this, "backgroundColor"),
color = "rgb(238, 238, 204)";
console.log("Broswer returned elem color: ", elemColor);
// IE7 and IE8 output the hex value
if (elemColor.slice(0, 1) === '#') elemColor = hexToRgb(elemColor);
console.log(elemColor, " === ", color, "?", elemColor === color);
}
// Source: https://stackoverflow.com/a/41354868/1218980
// Inspired by: https://stackoverflow.com/a/22744598/1218980
function getStyle(el, prop) {
return (typeof getComputedStyle !== 'undefined' ?
getComputedStyle(el, null) :
el.currentStyle
)[prop];
}
// Slightly modified version to quickly return a string
// https://stackoverflow.com/a/5624139/1218980
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? "rgb(" + [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
].join(', ') + ")" : null;
}
#css-div {
background-color: #eeeecc;
}
<div style="Background-color:#eeeecc" onclick="javascript:weekclick.call(this);">#eeeecc</div>
<div style="Background-color:#EEC" onclick="javascript:weekclick.call(this);">#EEC</div>
<div style="background-color:hsla(60, 50%, 86.7%, 1)" onclick="javascript:weekclick.call(this);">hsla(60, 50%, 86.7%, 1)</div>
<div style="background-color:rgb(238, 238, 204)" onclick="javascript:weekclick.call(this);">rgb(238, 238, 204)</div>
<div id="css-div" onclick="javascript:weekclick.call(this);">css</div>
I call the weekclick function with javascript:weekclick.call(this) passing the element itself as the context of the callback, making it easy to access the element with this.
It could be extended to take alpha into account when using HSLA or RGBA.
When I originally wrote this answer back in 2012, I used navigator.appName === "Microsoft Internet Explorer" to know if it was IE7 and the color to compare was changed to its HEX equivalent. Don't do that as it won't work today.
Use a canvas to compare colors.
Fill one pixel using the color string
and the other pixel using the Element getComputedStyle color property.
Compare the two pixels data using Array.from and JSON.stingify
/**
* Test if Element color property holds a specific color.
* https://stackoverflow.com/a/60689673/383904
*
* #param {Object} el The DOM Node Element
* #param {String} prop A CSS property color property
* #param {String} color A valid CSS color value
* #return {Boolean} True if element color matches
*/
function isElPropColor(el, prop, color) {
const ctx = document.createElement('canvas').getContext('2d');
ctx.fillStyle = color;
ctx.fillRect( 0, 0, 1, 1 );
ctx.fillStyle = getComputedStyle(el, null).getPropertyValue(prop);
ctx.fillRect( 1, 0, 1, 1 );
const a = JSON.stringify(Array.from(ctx.getImageData(0, 0, 1, 1).data));
const b = JSON.stringify(Array.from(ctx.getImageData(1, 0, 1, 1).data));
ctx.canvas = null;
return a === b;
}
// TEST (PS: see CSS!):
const el = document.body;
const prop = 'background-color';
console.log(isElPropColor(el, prop, 'red')); // obviously true :)
console.log(isElPropColor(el, prop, '#f00')); // true
console.log(isElPropColor(el, prop, 'rgb(255, 0, 0)')); // true
console.log(isElPropColor(el, prop, 'hsl(0, 100%, 50%)')); // true
console.log(isElPropColor(el, prop, 'hsla(0, 100%, 50%, 1)')); // true
body {background: red; }
The buts and whys
Most modern browsers calculate and convert the color value to RGB/A.
But we should not rely on doing strict equality comparison like:
// (Say the element is really red)
const isRed = myElement.style.backgroundColor === "rgb(255,0,0)"; // false
False because it's actually "rgb(255, 0, 0)", with spaces.
No guarantee some browser tomorrow will not convert to "rgba(255, 0, 0, 1)", or HSLA perhaps?
Edge and IE will return the value used in CSS or overwritten by our JS
We cannot reliably compare two DIV styles using getComputedStyle
and we cannot even create an "in-memory" DIV element since we need to - append it to the DOM, than use getComputedStyle(tempDIVElement, null).getPropertyValue('background-color') and than remove it from the DOM. It's a waste of DOM recalculations, paints, and it's not guaranteed some wondering stylesheet !important did not messed up our temporary DIV styles div {background: fuchsia !important; } giving false-positives "Is red? True! blue is blue because of !important".
Why canvas?
A canvas can stay in memory. No need to append to DOM, set colors, remove element.
The returned pixel data is always guaranteed to be [R, G, B, A] (RGBA being 0-255 values)
A wondering CSS rule will not mess up the assigned color
Useful links:
Canvas_API MDN
CanvasRenderingContext2D/getImageData MDN
Array/from MDN
JSON/stringify MDN
Window/getComputedStyle MDN
I'm actually just learning javascript, but instead of creating a function that converted rgb() to hex you can create a div of the same background-color you are looking for and compared it to that div's background color. If you happen to already have a div of that hex value you can just do this:
//javascript
if (document.getElementId('desiredElementToCompare').style.backgroundColor === document.getElementId('elementWithTheDesiredHexString').style.backgroundColor)
//jQuery
if ($('#desiredElementToCompare').css('background-color') === $('#elementWithTheDesiredHexString').css('background-color')
You can create a div with a function and then do the comparison like this:
var compareHex = (hex) => {
var hexString = document.createElement('div')
hexString.style.backgroundColor = `${hex}`
return hexString.style.backgroundColor
}
//then compare
//javascript
if (document.getElementId('desiredElementToCompare').style.backgroundColor === compareHex("#ECECF4"))
//jQuery
if($('#desiredElementToCompare').css('background-color') === compareHex("#ECECF4")
This way suggests using JQuery's background-color which standardises the response to rgb(r, g, b).
How to get the background color of an element using javascript?
Maybe this answer could help. Convert the color code to hex and then compare each RGB color/channel (i don't know the word)
https://stackoverflow.com/a/25666938/1757214
I think is better and easy compare two numbers than two strings
I would advice using the '===' stric equal (i think this is the name).
Like:
if (document.getElementById('test').style.backgroundColor === "rgb(236, 236, 244)") {
Related
Was wondering if there was a way to use a spectrum to change the background color based off of variables such as temp the higher it is, the more the colour will become orange from blue? or would I actually have to assign values
if (temp < 10) {
document.body.className = "background-cold";
} else if (temp > 10) {
document.body.className = "background-warm";
}
.background-cold {
background: linear-gradient(
179.31deg,
rgba(48, 87, 89, 0.97) 9.28%,
#fdfeff 167.45%
);
}
.background-warm {
background: linear-gradient(
179.31deg,
rgba(204, 101, 26, 0.97) 9.28%,
#fdfeff 167.45%
);
}
var hue = temp * someScale; // use some formula to determine the appropriate hue from the temperature
var saturation = 1;
var lightness = 1;
document.body.style.backgroundImage = `linear-gradient(179.31deg, hsla(${hue}%,${saturation}%,${lightness}%) 9.28%, #fdfeff 167.45%)`;
For the approach you intend to do I don't recomemend using the .className attribution. It will be hardcoded and that's not what you want. You can just use style.setProperty(string, string) in wich the first parameter is the CSS atttribute and the second parameter is the CSS value. Both of them need to be strings in order to work.
let colorValue = 'linear-gradient(VALUES)';
document.body.style.setProperty('background', colorValue);
By using the previous code you can now set directly the background you want. However your still need a function to create a dynamic colorValue according to the temperature. This can become a little complex depending on the color values you intend to apply. A possible solution for this can be found in the following answer: https://stackoverflow.com/a/12934900/11860800.
I'm looking to find a way to change every instance of a color in stylesheets that get loaded into a site. I'm working with Prestashop and working with large theme style sheets and modules' stylesheets... so I'm looking to change font colors and background colors. Yes I can do a search and replace... but I'd like to have reusable code that I can use on multiple sites and multiple files and themes.
I've been checking out a bunch of articles on here and found some code that selects all the elements loaded and checks the ComputedStyle and if it equals one value, it changes it to a new value.
function colorToHex(color) {
if (color.substr(0, 1) === '#') {
return color;
}
var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color);
var red = parseInt(digits[2]);
var green = parseInt(digits[3]);
var blue = parseInt(digits[4]);
var rgb = blue | (green << 8) | (red << 16);
return digits[1] + '#' + rgb.toString(16);
};
function changeColor(from, to) {
var elements = document.getElementsByTagName('*');
var to = '#babaff';
var from = '#f66685';
for (var i=0;i<elements.length;i++) {
var color = window.getComputedStyle(elements[i]).color;
var hex = colorToHex(color);
console.log('color=' ,color, elements[i]);
if (hex == from) {
elements[i].style.color=to;
console.log('its a color match!', elements[i]);
}
var backgroundColor = window.getComputedStyle(elements[i]).background;
console.log('bgcolor=' ,backgroundColor);
if (backgroundColor.indexOf('rgba')<0) {
var hex = colorToHex(backgroundColor);
if (hex == from) {
elements[i].style.backgroundColor=to;
}
}
}
}
It's partially working, but doesn't seem to work on conditional values... like hover or active states. So ideally I'm just looking for something that just finds a string of text (the hex code) and changes it out... and it doesn't care what the element is (class, tag, id) and doesn't matter the rule (background, background-color, color, border, ect) because these themes are pre-built and not standardized.
As you stated:
"ideally I'm just looking for something that just finds a string of text (the hex code) and changes it out"
Thats basically what a search and replace does. (besides the hex stuff)
If you are looking at a problem like replacing a single color in your whole style, and it is very time consuming atm, then you are probably doing something wrong.
As #delinear mentions in his comment, you probably want to use a sass script.
Using some javascript to change all colors after the css has already loaded is performance heavy and not really the way to do it.
I would really suggest a Search&Replace and to use sass variables afterwards.
(If you maybe want to change colors again)
there you can just do something like
$color: #ff00ff;
h1{
background-color: $color;
}
h2{
color: $color;
}
h3{
background: linear-gradient(135deg, red, $color);
}
h2:hover{
color: $color;
}
For the following d3 codes:
var previousElement = d3.select(this);
console.log(previousElement.attr("style"));
The result is like:
fill: red;
or
fill: rgb(152, 223, 138);
Then how can I get the exact color of this element, so that I may doing a judge like:
if ( elementColor == 'red')
or
if (elementColor == 'rgb(152, 223, 138)')
I tried var elementColor = previousElement.attr("style").fill, it returns null.
I believe what you want is previousElement.style('fill') instead of using .attr('style'). See https://github.com/mbostock/d3/wiki/Selections#style for documentation.
You may also want to look at the D3 Colors documentation, https://github.com/mbostock/d3/wiki/Colors, as that may help you even more.
You can use d3.rgb() to parse whatever is specified as the style and then compare the R, G, and B components:
var colour = d3.rgb(previousElement.attr("style"));
if(colour.r == 255 && colour.g == 0 && colour.b == 0) {
// ...
}
I have a simple pdf file, containing the words "Hello world", each in a different colour.
I'm loading the PDF, like this:
PDFJS.getDocument('test.pdf').then( onPDF );
function onPDF( pdf )
{
pdf.getPage( 1 ).then( onPage );
}
function onPage( page )
{
page.getTextContent().then( onText );
}
function onText( text )
{
console.log( JSON.stringify( text ) );
}
And I get a JSON output like this:
{
"items" : [{
"str" : "Hello ",
"dir" : "ltr",
"width" : 29.592,
"height" : 12,
"transform" : [12, 0, 0, 12, 56.8, 774.1],
"fontName" : "g_font_1"
}, {
"str" : "world",
"dir" : "ltr",
"width" : 27.983999999999998,
"height" : 12,
"transform" : [12, 0, 0, 12, 86.5, 774.1],
"fontName" : "g_font_1"
}
],
"styles" : {
"g_font_1" : {
"fontFamily" : "serif",
"ascent" : 0.891,
"descent" : 0.216
}
}
}
However, I've not been able to find a way to determine the colour of each word. When I render it, it renders properly, so I know the information is in there somewhere. Is there somewhere I can access this?
As Respawned alluded to, there is no easy answer that will work in all cases. That being said, here are two approaches which seem to work fairly well. Both having upsides and downsides.
Approach 1
Internally, the getTextContent method uses whats called an EvaluatorPreprocessor to parse the PDF operators, and maintain the graphic state. So what we can do is, implement a custom EvaluatorPreprocessor, overwrite the preprocessCommand method, and use it to add the current text color to the graphic state. Once this is in place, anytime a new text chunk is created, we can add a color attribute, and set it to the current color state.
The downsides to this approach are:
Requires modifying the PDFJS source code. It also depends heavily on
the current implementation of PDFJS, and could break if this is
changed.
It will fail in cases where the text is used as a path to be filled with an image. In some PDF creators (such as Photoshop), the way it creates colored text is, it first creates a clipping path from all the given text characters, and then paints a solid image over the path. So the only way to deduce the fill-color is by reading the pixel values from the image, which would require painting it to a canvas. Even hooking into paintChar wont be of much help here, since the fill color will only emerge at a later time.
The upside is, its fairly robust and works irrespective of the page background. It also does not require rendering anything to canvas, so it can be done entirely in the background thread.
Code
All the modifications are made in the core/evaluator.js file.
First you must define the custom evaluator, after the EvaluatorPreprocessor definition.
var CustomEvaluatorPreprocessor = (function() {
function CustomEvaluatorPreprocessor(stream, xref, stateManager, resources) {
EvaluatorPreprocessor.call(this, stream, xref, stateManager);
this.resources = resources;
this.xref = xref;
// set initial color state
var state = this.stateManager.state;
state.textRenderingMode = TextRenderingMode.FILL;
state.fillColorSpace = ColorSpace.singletons.gray;
state.fillColor = [0,0,0];
}
CustomEvaluatorPreprocessor.prototype = Object.create(EvaluatorPreprocessor.prototype);
CustomEvaluatorPreprocessor.prototype.preprocessCommand = function(fn, args) {
EvaluatorPreprocessor.prototype.preprocessCommand.call(this, fn, args);
var state = this.stateManager.state;
switch(fn) {
case OPS.setFillColorSpace:
state.fillColorSpace = ColorSpace.parse(args[0], this.xref, this.resources);
break;
case OPS.setFillColor:
var cs = state.fillColorSpace;
state.fillColor = cs.getRgb(args, 0);
break;
case OPS.setFillGray:
state.fillColorSpace = ColorSpace.singletons.gray;
state.fillColor = ColorSpace.singletons.gray.getRgb(args, 0);
break;
case OPS.setFillCMYKColor:
state.fillColorSpace = ColorSpace.singletons.cmyk;
state.fillColor = ColorSpace.singletons.cmyk.getRgb(args, 0);
break;
case OPS.setFillRGBColor:
state.fillColorSpace = ColorSpace.singletons.rgb;
state.fillColor = ColorSpace.singletons.rgb.getRgb(args, 0);
break;
}
};
return CustomEvaluatorPreprocessor;
})();
Next, you need to modify the getTextContent method to use the new evaluator:
var preprocessor = new CustomEvaluatorPreprocessor(stream, xref, stateManager, resources);
And lastly, in the newTextChunk method, add a color attribute:
color: stateManager.state.fillColor
Approach 2
Another approach would be to extract the text bounding boxes via getTextContent, render the page, and for each text, get the pixel values which reside within its bounds, and take that to be the fill color.
The downsides to this approach are:
The computed text bounding boxes are not always correct, and in some cases may even be off completely (eg: rotated text). If the bounding box does not cover at least partially the actual text on canvas, then this method will fail. We can recover from complete failures, by checking that the text pixels have a color variance greater than a threshold. The rationale being, if bounding box is completely background, it will have little variance, in which case we can fallback to a default text color (or maybe even the color of k nearest-neighbors).
The method assumes the text is darker than the background. Otherwise, the background could be mistaken as the fill color. This wont be a problem is most cases, as most docs have white backgrounds.
The upside is, its simple, and does not require messing with the PDFJS source-code. Also, it will work in cases where the text is used as a clipping path, and filled with an image. Though this can become hazy when you have complex image fills, in which case, the choice of text color becomes ambiguous.
Demo
http://jsfiddle.net/x2rajt5g/
Sample PDF's to test:
https://www.dropbox.com/s/0t5vtu6qqsdm1d4/color-test.pdf?dl=1
https://www.dropbox.com/s/cq0067u80o79o7x/testTextColour.pdf?dl=1
Code
function parseColors(canvasImgData, texts) {
var data = canvasImgData.data,
width = canvasImgData.width,
height = canvasImgData.height,
defaultColor = [0, 0, 0],
minVariance = 20;
texts.forEach(function (t) {
var left = Math.floor(t.transform[4]),
w = Math.round(t.width),
h = Math.round(t.height),
bottom = Math.round(height - t.transform[5]),
top = bottom - h,
start = (left + (top * width)) * 4,
color = [],
best = Infinity,
stat = new ImageStats();
for (var i, v, row = 0; row < h; row++) {
i = start + (row * width * 4);
for (var col = 0; col < w; col++) {
if ((v = data[i] + data[i + 1] + data[i + 2]) < best) { // the darker the "better"
best = v;
color[0] = data[i];
color[1] = data[i + 1];
color[2] = data[i + 2];
}
stat.addPixel(data[i], data[i+1], data[i+2]);
i += 4;
}
}
var stdDev = stat.getStdDev();
t.color = stdDev < minVariance ? defaultColor : color;
});
}
function ImageStats() {
this.pixelCount = 0;
this.pixels = [];
this.rgb = [];
this.mean = 0;
this.stdDev = 0;
}
ImageStats.prototype = {
addPixel: function (r, g, b) {
if (!this.rgb.length) {
this.rgb[0] = r;
this.rgb[1] = g;
this.rgb[2] = b;
} else {
this.rgb[0] += r;
this.rgb[1] += g;
this.rgb[2] += b;
}
this.pixelCount++;
this.pixels.push([r,g,b]);
},
getStdDev: function() {
var mean = [
this.rgb[0] / this.pixelCount,
this.rgb[1] / this.pixelCount,
this.rgb[2] / this.pixelCount
];
var diff = [0,0,0];
this.pixels.forEach(function(p) {
diff[0] += Math.pow(mean[0] - p[0], 2);
diff[1] += Math.pow(mean[1] - p[1], 2);
diff[2] += Math.pow(mean[2] - p[2], 2);
});
diff[0] = Math.sqrt(diff[0] / this.pixelCount);
diff[1] = Math.sqrt(diff[1] / this.pixelCount);
diff[2] = Math.sqrt(diff[2] / this.pixelCount);
return diff[0] + diff[1] + diff[2];
}
};
This question is actually extremely hard if you want to do it to perfection... or it can be relatively easy if you can live with solutions that work only some of the time.
First of all, realize that getTextContent is intended for searchable text extraction and that's all it's intended to do.
It's been suggested in the comments above that you use page.getOperatorList(), but that's basically re-implementing the whole PDF drawing model in your code... which is basically silly because the largest chunk of PDFJS does exactly that... except not for the purpose of text extraction but for the purpose of rendering to canvas. So what you want to do is to hack canvas.js so that instead of just setting its internal knobs it also does some callbacks to your code. Alas, if you go this way, you won't be able to use stock PDFJS, and I rather doubt that your goal of color extraction will be seen as very useful for PDFJS' main purpose, so your changes are likely not going to get accepted upstream, so you'll likely have to maintain your own fork of PDFJS.
After this dire warning, what you'd need to minimally change are the functions where PDFJS has parsed the PDF color operators and sets its own canvas painting color. That happens around line 1566 (of canvas.js) in function setFillColorN. You'll also need to hook the text render... which is rather a character renderer at canvas.js level, namely CanvasGraphics_paintChar around line 1270. With these two hooked, you'll get a stream of callbacks for color changes interspersed between character drawing sequences. So you can reconstruct the color of character sequences reasonably easy from this.. in the simple color cases.
And now I'm getting to the really ugly part: the fact that PDF has an extremely complex color model. First there are two colors for drawing anything, including text: a fill color and stroke (outline) color. So far not too scary, but the color is an index in a ColorSpace... of which there are several, RGB being only one possibility. Then there's also alpha and compositing modes, so the layers (of various alphas) can result in a different final color depending on the compositing mode. And the PDFJS has not a single place where it accumulates color from layers.. it simply [over]paints them as they come. So if you only extract the fill color changes and ignore alpha, compositing etc.. it will work but not for complex documents.
Hope this helps.
There's no need to patch pdfjs, the transform property gives the x and y, so you can go through the operator list and find the setFillColor op that precedes the text op at that point.
When I use window.getComputedStyle on an element's color property, it returns a string with an rgb() or rgba() value, regardless of the syntax used to set the color originally. In Internet Explorer, when forced to use element.currentStyle instead, it apparently returns the exact color string that was originally set (although lowercased).
Here is a demonstration of my problem:
<div id=el> </div>
<script type="text/javascript">
var el = document.getElementById('el');
el.style.color = 'red';
if (window.getComputedStyle) {
el.innerHTML = getComputedStyle(el, null).color;
} else {
el.innerHTML = el.currentStyle.color;
}
</script>
Other browsers display "rgb(255, 0, 0)". IE displays "red". I want to determine the real RGB or RGBA value. So I have two questions:
Is there any way to get the real value in IE... other than by parsing all potential color syntaxes manually and including a mapping of color names to values?
Could the behavior of the other browsers be relied on? Are they supposed to return a value as an rgb() or rgba() string, or could they return, for example, #ff0000, or something else?
As best I can tell, the answer is "no". There is no simple way to get the color value in IE without knowing how to parse all possible color representations. When I tried to do this, I found that I even needed to recognized all possible color names like blue. It was a royal pain, but I did work out the code for it.
My code was trying to get the background color and it will even look at parent objects in order to find where the background color is set (so it's not doing exactly what you asked for) and it used the YUI version of getComputedStyle (you can substitute your own) but this is the code I used. It does handle these forms of color definition:
#fff
#ffffff
transparent
rgb(12,45,99)
rgba(12,45,99,30)
orange
And, the code:
JFL.GetBackgroundColor = function(o)
{
var colorNames = {
aliceblue: 'f0f8ff',
antiquewhite: 'faebd7',
aqua: '00ffff',
aquamarine: '7fffd4',
azure: 'f0ffff',
beige: 'f5f5dc',
bisque: 'ffe4c4',
black: '000000',
blanchedalmond: 'ffebcd',
blue: '0000ff',
blueviolet: '8a2be2',
brown: 'a52a2a',
burlywood: 'deb887',
cadetblue: '5f9ea0',
chartreuse: '7fff00',
chocolate: 'd2691e',
coral: 'ff7f50',
cornflowerblue: '6495ed',
cornsilk: 'fff8dc',
crimson: 'dc143c',
cyan: '00ffff',
darkblue: '00008b',
darkcyan: '008b8b',
darkgoldenrod: 'b8860b',
darkgray: 'a9a9a9',
darkgreen: '006400',
darkkhaki: 'bdb76b',
darkmagenta: '8b008b',
darkolivegreen: '556b2f',
darkorange: 'ff8c00',
darkorchid: '9932cc',
darkred: '8b0000',
darksalmon: 'e9967a',
darkseagreen: '8fbc8f',
darkslateblue: '483d8b',
darkslategray: '2f4f4f',
darkturquoise: '00ced1',
darkviolet: '9400d3',
deeppink: 'ff1493',
deepskyblue: '00bfff',
dimgray: '696969',
dodgerblue: '1e90ff',
feldspar: 'd19275',
firebrick: 'b22222',
floralwhite: 'fffaf0',
forestgreen: '228b22',
fuchsia: 'ff00ff',
gainsboro: 'dcdcdc',
ghostwhite: 'f8f8ff',
gold: 'ffd700',
goldenrod: 'daa520',
gray: '808080',
green: '008000',
greenyellow: 'adff2f',
honeydew: 'f0fff0',
hotpink: 'ff69b4',
indianred : 'cd5c5c',
indigo : '4b0082',
ivory: 'fffff0',
khaki: 'f0e68c',
lavender: 'e6e6fa',
lavenderblush: 'fff0f5',
lawngreen: '7cfc00',
lemonchiffon: 'fffacd',
lightblue: 'add8e6',
lightcoral: 'f08080',
lightcyan: 'e0ffff',
lightgoldenrodyellow: 'fafad2',
lightgrey: 'd3d3d3',
lightgreen: '90ee90',
lightpink: 'ffb6c1',
lightsalmon: 'ffa07a',
lightseagreen: '20b2aa',
lightskyblue: '87cefa',
lightslateblue: '8470ff',
lightslategray: '778899',
lightsteelblue: 'b0c4de',
lightyellow: 'ffffe0',
lime: '00ff00',
limegreen: '32cd32',
linen: 'faf0e6',
magenta: 'ff00ff',
maroon: '800000',
mediumaquamarine: '66cdaa',
mediumblue: '0000cd',
mediumorchid: 'ba55d3',
mediumpurple: '9370d8',
mediumseagreen: '3cb371',
mediumslateblue: '7b68ee',
mediumspringgreen: '00fa9a',
mediumturquoise: '48d1cc',
mediumvioletred: 'c71585',
midnightblue: '191970',
mintcream: 'f5fffa',
mistyrose: 'ffe4e1',
moccasin: 'ffe4b5',
navajowhite: 'ffdead',
navy: '000080',
oldlace: 'fdf5e6',
olive: '808000',
olivedrab: '6b8e23',
orange: 'ffa500',
orangered: 'ff4500',
orchid: 'da70d6',
palegoldenrod: 'eee8aa',
palegreen: '98fb98',
paleturquoise: 'afeeee',
palevioletred: 'd87093',
papayawhip: 'ffefd5',
peachpuff: 'ffdab9',
peru: 'cd853f',
pink: 'ffc0cb',
plum: 'dda0dd',
powderblue: 'b0e0e6',
purple: '800080',
red: 'ff0000',
rosybrown: 'bc8f8f',
royalblue: '4169e1',
saddlebrown: '8b4513',
salmon: 'fa8072',
sandybrown: 'f4a460',
seagreen: '2e8b57',
seashell: 'fff5ee',
sienna: 'a0522d',
silver: 'c0c0c0',
skyblue: '87ceeb',
slateblue: '6a5acd',
slategray: '708090',
snow: 'fffafa',
springgreen: '00ff7f',
steelblue: '4682b4',
tan: 'd2b48c',
teal: '008080',
thistle: 'd8bfd8',
tomato: 'ff6347',
turquoise: '40e0d0',
violet: 'ee82ee',
violetred: 'd02090',
wheat: 'f5deb3',
white: 'ffffff',
whitesmoke: 'f5f5f5',
yellow: 'ffff00',
yellowgreen: '9acd32'
};
function parseSingle(s)
{
s = s + s;
return(parseInt(s, 16));
}
var color;
while (o)
{
color = YD.getComputedStyle(o, "backgroundColor");
if (color && color != "transparent")
{
break;
}
if (o == document.body)
{
color = "#ffffff";
break;
}
o = o.parentNode;
}
color = color.replace(/ /g, "").toLowerCase();
if (colorNames[color])
{
color = "#" + colorNames[color];
}
var r = 256, g = 256, b = 256;
if (color.indexOf("#") == 0)
{
color = color.slice(1);
if (color.length == 3)
{
r = parseSingle(color.slice(0,1));
g = parseSingle(color.slice(1,2));
b = parseSingle(color.slice(2,3));
}
else if (color.length == 6)
{
r = parseInt(color.slice(0,2), 16);
g = parseInt(color.slice(2,4), 16);
b = parseInt(color.slice(4,6), 16);
}
}
else if (color.indexOf("rgb") == 0)
{
var results = color.match(/^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})/);
if (results && results.length >= 4)
{
r = parseInt(results[1], 10);
g = parseInt(results[2], 10);
b = parseInt(results[3], 10);
}
}
var luminance = (0.3 * r + 0.59 * g + 0.11 * b) / 256;
return({r: r, g: g, b: b, luminance: luminance});
}
As suggested I examined CMS's answer here which uses queryCommandValue. For the equivalent of CSS 'color' rather than 'backround-color', I had to use 'ForeColor' rather than 'BackColor', but this technique seems to work perfectly for getting the real value in IE.
Here is the amended, working version of the example in my question:
<div id=el> </div>
<script type="text/javascript">
var el = document.getElementById('el');
el.style.color = 'red';
if (window.getComputedStyle) {
el.innerHTML = getComputedStyle(el, null).color;
} else {
var oRG = document.body.createTextRange();
oRG.moveToElementText(el);
var iClr = oRG.queryCommandValue('ForeColor');
el.innerHTML = 'rgb('+(iClr & 0xFF)+','+((iClr & 0xFF00)>>8)+','+((iClr & 0xFF0000)>>16)+')';
}
</script>
I've been looking for an answer to my second question, but there doesn't seem to be one. If I'm reading it correctly, this page: http://www.w3.org/TR/css3-color/ explains that browsers may return, for the computed value, either a "six digit hex value or rgb(...) functional value, with an alpha value of 1" (?). It says transparent returns rgba(0,0,0,0). And finally it says, "for all other values, the computed value is the specified value". This is not clear at all. E.g., must it use RGB integers or can it use percentages? May there be space around the values? Can the "specified value"'s letter case be preserved? What about hsl() and hsla() colors?
In practice, it seems that browsers normalize the values to return rgb(...) or rgba(...), with the red-green-blue as integers and the alpha as a decimal, with a single space after each comma, although this doesn't seem to be formally specified. As an exception, Firefox seems to return transparent if the color's alpha value is 0, regardless of the other values or how it was set; e.g., rgba(255, 0, 255, 0).
So, IE's odd way for getting an RGB int is far simpler, if you can use it.