18

Currently I want to get the real background-color of a specified object, here, the real means what the people see, for instance, given the following code:

<div id="foo" style="background-color: red">
    I am red
    <span id="bar">
         I have no background, but I am red
    </span>
</div>

The real background-color of #bar should be rbg(255,0,0).

Here's what I have so far:

function color_visible(color_str) {
  return color_str && !(color_str.substr(0, 4) == "rgba" && color_str.substr(-2, 2) == "0)");
}

function get_bgcolor (obj) {
  var ret = $(obj).css("background-color");
  if (!color_visible(ret)) ret = $(obj).css("bgcolor");
  if (color_visible(ret)) return ret;
  if (!$(obj).is("body")) return get_bgcolor($(obj).parent());
  return "rgb(255, 255, 255)";
}

But is there a better way to do it?

Demo in Stack Snippet and jsFiddle

function color_visible(color_str) {
  return color_str && !(color_str.substr(0, 4) == "rgba" && color_str.substr(-2, 2) == "0)");
}

function get_bgcolor (obj) {
  var ret = $(obj).css("background-color");
  if (!color_visible(ret)) ret = $(obj).css("bgcolor");
  if (color_visible(ret)) return ret;
  if (!$(obj).is("body")) return get_bgcolor($(obj).parent());
  return "rgb(255, 255, 255)";
}

console.log(get_bgcolor($("#bar")));
console.log(get_bgcolor($("#baz")));
console.log(get_bgcolor($("#foo")));
console.log(get_bgcolor($("body")));
body {
  background-color: yellow;
}

.bg_white {
  background-color: #FFF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <div id="foo" style="background-color: red">
    I am red
    <span id="bar">
      I have no background
    </span>

    <span id="baz" class="bg_white">
      I am white
    </span>
  </div>
  I am yellow
</div>
KyleMit
  • 30,350
  • 66
  • 462
  • 664
Marcus
  • 6,701
  • 4
  • 19
  • 28
  • Think your example is pretty close with traversing the parents until you find a color. – Tim M. Sep 25 '12 at 04:22
  • 2
    @TimMedora, but what if the position of #bar is not on top of the #foo? I mean `#bar{ position: ...; left: ..., top: ...}`. – Marcus Sep 25 '12 at 04:30
  • Now that is a good question. You can examine the dimensions/positions of the elements on the page to determine intersection but I imagine that would become slow unless there were only a couple dozen elements on the page or you could find a way to confine it to a small region. – Tim M. Sep 25 '12 at 04:34
  • @TimMedora, yes, that's actually what a browser do... – Marcus Sep 25 '12 at 04:37
  • I think this question helps you http://stackoverflow.com/questions/1936021/javascript-eyedropper-tell-color-of-pixel-under-mouse-cursor – Charles Jourdan Sep 25 '12 at 10:22
  • Note that this question isn't exactly answerable, because for any element rectangle, other elements can partially overlap it. Thus, for any element rectangle, you can have many visible background colors within the same rectangle. And that's ignoring the complication of patterns and images. I expect the best you can do is get the color and opacity at a specific x-y point. – Memetican Oct 01 '21 at 09:56

5 Answers5

17

Javascript only version:

function realBackgroundColor(elem) {
    var transparent = 'rgba(0, 0, 0, 0)';
    var transparentIE11 = 'transparent';
    if (!elem) return transparent;

    var bg = getComputedStyle(elem).backgroundColor;
    if (bg === transparent || bg === transparentIE11) {
        return realBackgroundColor(elem.parentElement);
    } else {
        return bg;
    }
}
realBackgroundColor(document.querySelector('#element')); // rgb(255, 255, 255)

http://jsfiddle.net/qnLwsr7y/

Note that it does not take opacity or background images into account.

Simon Bengtsson
  • 7,573
  • 3
  • 58
  • 87
  • This method fails if parent has `background-image` set, see: https://jsfiddle.net/38ve0gdv/1/ – cronfy Mar 17 '17 at 14:03
  • 1
    Consider adding this check: https://jsfiddle.net/38ve0gdv/2/ , it returns `undefined` as background if finds parent's `background-image`. – cronfy Mar 17 '17 at 14:05
8

Try window.getComputedStyle:

window.getComputedStyle(element, null).getPropertyValue("background-color")

This approach is simple and native, but doesn't support IE8

KyleMit
  • 30,350
  • 66
  • 462
  • 664
gock
  • 120
  • 2
  • 3
  • Not always working well http://stackoverflow.com/questions/22876295/getcomputedstyle-gives-transparent-instead-of-actual-background-color – Sanghyun Lee Jun 03 '15 at 04:59
  • 2
    This always returns `rgba(0,0,0,0)` in my simplest case for Chrome. While traversing element tree works well (see below). – Pavel Vlasov Mar 18 '19 at 22:26
7

Try this:

var get_bgcolor = function(obj) {
    var real = obj.css('background-color');
    var none = 'rgba(0, 0, 0, 0)';
    if (real === none) {
        return obj.parents().filter(function() {
            return $(this).css('background-color') != none
        }).first().css('background-color');
    } else {
        return real
    }
}

http://jsfiddle.net/bqkwN/

Ram
  • 143,282
  • 16
  • 168
  • 197
3

This is a difficult thing to get right :( and I believe a 100% correct result in all cases is impossible.

background-color is not inherited. getComputedStyle only returns what is in elem.style.backgroundColor if there, or otherwise what is derived from the css stylesheets loaded. If these two still don't return a value, it returns rgba(0, 0, 0, 0) in which case you need to climb up the DOM to see what parents elements have. And this is further complicated in the case of frames which may derive their background from the (ie top) frame behind them.

Here is an attempt:

const getCbgcolor = (elem) => {
  if (!getCbgcolor.top) getCbgcolor.top= (() => { 
    try { return window.top.document.documentElement; } catch(e) { return null; /* CORS */}})()
  });

  while (true) {
    let cbg=window.getComputedStyle(elem).getPropertyValue('background-color');
    if (cbg && cbg!='rgba(0, 0, 0, 0)' && cbg!='transparent') return cbg;
    if (elem===getCbgcolor.top) return 'initial';
    elem = elem.parentElement;
    if (!elem) return '';
  }
}

(An issues with this is that if someone explicitly set an element's background to rgba(0, 0, 0, 0) in the element's style or a css stylesheet you may want that value instead of the calculated value which will not work with this code.)

kofifus
  • 17,260
  • 17
  • 99
  • 173
-2

Try this:

function hexc(colorval) {
    var parts = colorval.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
    delete(parts[0]);
    for (var i = 1; i <= 3; ++i) {
        parts[i] = parseInt(parts[i]).toString(16);
        if (parts[i].length == 1) parts[i] = '0' + parts[i];
    }
    color = '#' + parts.join('');
}

var color = '';
$('div#foo').click(function() {
    var x = $(this).css('backgroundColor');
    hexc(x);
    alert(color);
})
Eli
  • 14,779
  • 5
  • 59
  • 77