21

I'd like to:

  1. Find a style attribute for all elements in the page (for instance: all elements that have color:#333;)
  2. Change this attribute for all of them (for instance from color:#333 to color:#444).

Do you have any suggestion on doing so?

Braiam
  • 1
  • 11
  • 47
  • 78
slwr
  • 1,105
  • 6
  • 16
  • 35
  • 2
    Why don't you add a(nother) class for all such elements? :) – Andrius Naruševičius May 26 '12 at 15:55
  • 2
    I can't because the script is part of Chrome extension – slwr May 26 '12 at 15:56
  • Relying on a css attribute to find an element is really not a good idea. Even with a framework like jQuery, you're parsing through a ton of code. – Fluidbyte May 26 '12 at 16:31
  • Yes but some extension don't bother adding all their craps to inputs and that sometimes break the design which leaves no choice than finding what's been added by some extension and fix it – Tofandel May 21 '20 at 14:11

6 Answers6

21

My suggestion is avoid doing this if at all remotely possible. Instead, use a class to assign the color value, and then you can look up the elements using the class, rather than the color value.

As far as I'm aware, there's no selector (not even in CSS3) that you can use to query a specific style value, which means looping through all elements (or it looks like you can restrict it to all elements with a style attribute) and looking at the element.style.color property. Now, the thing is, even though you write color: #333; in your style attribute, different browsers will echo it back to you in different ways. It might be #333, it might be #333333, it might be rgb(51, 51, 51), it might even be rgba(51, 51, 51, 0).

So on the whole, a very awkward exercise indeed.


Since you've said this is for a Chrome extension, you probably don't have to worry as much about multiple formats, although I'd throw in the ones that we've seen in the wild in case Chrome changes the format (perhaps to be consistent with some other browser, which has been known to happen).

But for instance:

(function() {

  // Get all elements that have a style attribute
  var elms = document.querySelectorAll("*[style]");

  // Loop through them
  Array.prototype.forEach.call(elms, function(elm) {
    // Get the color value
    var clr = elm.style.color || "";

    // Remove all whitespace, make it all lower case
    clr = clr.replace(/\s/g, "").toLowerCase();

    // Switch on the possible values we know of
    switch (clr) {
      case "#333":
      case "#333333":
      case "rgb(51,51,51)": // <=== This is the one Chrome seems to use
      case "rgba(51,51,51,0)":
        elm.style.color = "#444";
        break;
    }
  });
})();

Live example using red for clarity | source - Note that the example relies on ES5 features and querySelectorAll, but as this is Chrome, I know they're there.

Note that the above assumes inline style, because you talked about the style attribute. If you mean computed style, then there's nothing for it but to loop through all elements on the page calling getComputedStyle. Other than that, the above applies.

Final note: If you really meant a style attribute with precisely the value color: #333 and not the value color:#333 or color:#333333; or color: #333; font-weight: bold or any other string, your querySelectorAll could handle that: querySelectorAll('*[style="color: #333"]'). But it would be very fragile.


From your comment below, it sounds like you're having to go through every element. If so, I wouldn't use querySelectorAll at all, I'd use recursive descent:

function walk(elm) {
    var node;

    // ...handle this element's `style` or `getComputedStyle`...

    // Handle child elements
    for (node = elm.firstChild; node; node = node.nextSibling) {
        if (node.nodeType === 1) { // 1 == Element
            walk(node);
        }
    }
}

// Kick it off starting with the `body` element
walk(document.body);

That way you don't build up large, unnecessary temporary structures. This is probably the most efficient way to walk the entire DOM of a document.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • well, the browser should convert all those colours to a consistent format internally, so if you're scanning the DOM you shouldn't need to look for more than one of them. – Spudley May 26 '12 at 16:01
  • @Spudley: You would if you're targeting multiple browsers, which is why I raise it. But now the OP has said this is a Chrome extension, it simplifies things. Provided Chrome doesn't change it from dot release to dot release, of course, which they could well do to be consistent with some other browser... – T.J. Crowder May 26 '12 at 16:02
  • Thanks a lot @T.J.Crowder, I actually need to loop through all elements. I'm trying this but without success: `// Get all elements that have a style attribute var elms = document.querySelectorAll(""); // Loop through them Array.prototype.forEach.call(elms, function(elm) { // Get the color value var crs = getComputedStyle(elm, ':after').getPropertyCSSValue('color'); alert(crs);` – slwr May 26 '12 at 19:17
  • @silviolor: Sounds like you worked it out (I assume, since you accepted the answer), but FWIW if you have to walk **all** elements, I wouldn't use `querySelectorAll` at all. See the very end of the updated answer. – T.J. Crowder May 26 '12 at 21:07
7

It's definitely more simple if you use jquery. In any case, the best would be to use classes and use the filter jquery method to get the objects you want.

But if you really want to get them you can do something like:

$(function () {
    $('p').filter(function () {
        return $(this).css('color') == '#333';
    }).css('color', '#444');
});

The above script get the elements with the desired css attribute and set a new css attribute (color #444).

Daniele B
  • 3,117
  • 2
  • 23
  • 46
  • I agree with you on A, the use of jquery is just a suggestion. Also B is a reasonable comment, even if it would be quite simple to reverse this change. About C, I don't agree. To me a massive array is something like a 10000 elements array, and a page containing 10000 elements is quite unusual.... – Daniele B May 26 '12 at 16:19
5

oneliner

[...document.querySelectorAll("[style*='color:#333']")].forEach(e=>e.style.color='#444')

function switchColors() {
  [...document.querySelectorAll("div[style*='color:#333']")]
    .forEach(el => el.style.color = '#f00')
}
<div>EXAMPLE:
  <div>
    <div style="background: #eee; color:#333;">aaa</div>
    <div style="background: gray; color:#00f;">
      bbb
      <div style="color:#333;">ccc</div>
    </div>

  </div>

  <div style="color:#040;">ddd</div>
  <div style="color:#333; font-size: 20px">eee</div>
</div>

<button onclick="switchColors()">Switch Colors</button>
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
0

You can't, if you don't add at least a specific CSS class to all this elements you want to track.

Or better, you can with very poor performances by looping on all the elements of the DOM until you find what you're looking for. But please, don't think of doing this

napolux
  • 15,574
  • 9
  • 51
  • 70
0

It's as already said really hard / inefficient to query all elements by color.

// refrence: http://stackoverflow.com/questions/5999209/jquery-how-to-get-the-background-color-code-of-an-element
var arr = [];

$('*').each(function (i, ele) {
   // is red => save
   if($(ele).css('backgroundColor') == ('rgb(0, 0, 255)')) arr.push(ele);
});

console.log(arr);

Here is an JSFiddle Example for it: http://jsfiddle.net/ddAg7/

My recommendation for this is: Don't do it!

0

Something like

$('selector').each(function() {
    if($(this).attr('style').indexOf('font-weight') > -1) {
        alert('got my attribute');
    }
});

in the if statement you could replace it with a different css... Not sure.. haven't tried on all browsers though :)

verisimilitude
  • 5,077
  • 3
  • 30
  • 35