4

I'm writing a script that needs to check if certain CSS properties are defined inside the <style> tag.

<style type="text/css">
#bar {width: 200px;}
</style>
<div id="foo" style="width: 200px;">foo</div>
<div id="bar">bar</div>
// 200px
console.log(document.getElementById("foo").style.width);

// an empty string
console.log(document.getElementById("bar").style.width);

if(property_width_defined_in_style_tag) {
    // ...
}

Is this possible?

I'm not trying to get the getComputedStyle(ele).width btw.

user1643156
  • 4,407
  • 10
  • 36
  • 59
  • *"I'm not trying to get the getComputedStyle(ele).width btw."* And yet, your code examples suggest that's exactly what you're trying to do. If it isn't, what *are* you trying to do? – T.J. Crowder Mar 27 '13 at 19:13
  • I'm trying to get some string (200px in this case) instead of "an empty string" from #bar since the style was defined in the style tag. – user1643156 Mar 27 '13 at 19:18
  • @ user: That's what `getComputedStyle` would do. – T.J. Crowder Mar 27 '13 at 19:20
  • 1
    Crowder, please read the title, that's exactly what I'm trying to do. `getComputedStyle` will for sure return some real value, but you cannot tell where the style was defined, either from the style tag or inline style. @dystroy's suggestion might be the solution. – user1643156 Mar 27 '13 at 19:25
  • @ user: I saw the title. You need to understand that people say X and mean Y **all the time**. Yes, absolutely, look through the stylesheets if you think that's the best approach. Another might be to compare the `style` object to the return value from `getComputedStyle`. – T.J. Crowder Mar 27 '13 at 19:27
  • Keep in mind that you could have the same property defined in both inline style *and* stylesheet, and depends by the "!important" they could be applied differently. Plus, that you could have something not defined for `#bar` in the stylesheet, but defined for `div`, and the `width` will be applied too (if you're planning to have generic selectors, you could have also "div#bar" or cascading selector and so on that won't match). – ZER0 Mar 27 '13 at 19:58

2 Answers2

6

I'm not sure this is what you want, it works closest to your first pseudo code where you had an element instance, anyway hope it helps:

var proto = Element.prototype;
var slice = Function.call.bind(Array.prototype.slice);
var matches = Function.call.bind(proto.matchesSelector || 
                proto.mozMatchesSelector || proto.webkitMatchesSelector ||
                proto.msMatchesSelector || proto.oMatchesSelector);

// Returns true if a DOM Element matches a cssRule
var elementMatchCSSRule = function(element, cssRule) {
  return matches(element, cssRule.selectorText);
};

// Returns true if a property is defined in a cssRule
var propertyInCSSRule = function(prop, cssRule) {
  return prop in cssRule.style && cssRule.style[prop] !== "";
};

// Here we get the cssRules across all the stylesheets in one array
var cssRules = slice(document.styleSheets).reduce(function(rules, styleSheet) {
  return rules.concat(slice(styleSheet.cssRules));
}, []);

// get a reference to an element, then...
var bar = document.getElementById("bar");

// get only the css rules that matches that element
var elementRules = cssRules.filter(elementMatchCSSRule.bind(null, bar));

// check if the property "width" is in one of those rules
hasWidth = elementRules.some(propertyInCSSRule.bind(null, "width"));

I think you can reuse all of this code for your purpose, or just some piece of it, it's modular on purpose – for instance, once you have all the cssRules flatten, or the elementRules, you can still use a for loop and check what you need. It uses ES5 functions and matchesSelector so in old browsers won't work without shims. Plus, you could also filter by priority and so on – you could for instance remove all the properties has a lower priority than the inline style ones, etc.

ZER0
  • 24,846
  • 5
  • 51
  • 54
  • ZER0, I still don't quite get how your code works. But, this is **exactly** what I want. And the best part is that it works on `parent child {prop: val}` where child is an element tag name, instead of a class or id selector. Answer Accpeted. Thank you very much. – user1643156 Apr 05 '13 at 15:52
  • I tried to put some meaningful comments in the code, but if there are some part you want to be explained better just let me know. Glad it was the code you was looking for! – ZER0 Apr 05 '13 at 18:23
  • I've updated a modified version of your code here: https://stackoverflow.com/questions/50567761/how-to-display-all-css-selectors-properties-in-a-textarea/50569764# Maybe you'll want to see it! – Takit Isy May 28 '18 at 19:13
  • @TakitIsy hey, thanks! Glad to see it's still used and helpful. :) – ZER0 May 29 '18 at 08:54
5

You can completely explore in javascript the styleSheets.

Start with the document.styleSheets array. The values are the different style elements or CSS files that are used by your document.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • I'm looking at the documentation...and it seems that I have to perform a regular expression on each of style sheets. one small correction...`document.styleSheets`, capitalized `S` for `Sheets`. – user1643156 Mar 27 '13 at 19:22
  • Why would you do a regular expression ? I certainly don't need regular expression when [reading styleSheets from js](https://github.com/Canop/braldop/blob/master/src/gui/SpriteSet.js). – Denys Séguret Mar 27 '13 at 19:37
  • That was my first thought when I console log `document.styleSheets`. Problem has been solved and question has been updated. Thanks. – user1643156 Mar 27 '13 at 19:51
  • dystroy, thanks for the styleSheets tip. I found ZER0's solution is exactly what I need. So I chose his answer as accepted. Sorry about that. – user1643156 Apr 05 '13 at 15:53