Which JavaScript-framework can search CSS stylesheet rules and edit their properties?
Asked Answered
O

4

16

The Question

Which JavaScript framework (prototype, script.aculo.us, Mootools, MochiKit...) has decent CSS rule editing support?

This is about changing a style rule. I want to have dynamic CSS classes which change. Example:

<style>
#answer, .reveal { display: none; color: blue; }
#answer { /* additional stuff */ }
</style>

Now, via JavaScript, I want to change the rule that has the “display: none” in it. – I’m convinced that sometimes this is the right way to go; I’m not looking for alternatives, when it is not the right way to do.

Which framework out there makes the following easy:

  1. select a rule from all rules (e.g. “#answer, .reveal”)
  2. what value does the selected rule(s) have for “display”?
  3. delete the “display” property from the rule(s)

(2. and 3. are easy with DOM alone, as long as I get a handle to the CSS rule back from the framework)

The Frameworks which are not good enough:

YUI’s StyleSheet for example can only search for rules in one sheet at a time (limited, but enough for me), but it can’t show, edit or retrieve multi-selector rules like my first example (too limited for my taste).

YUI has also no way to get individual properties (the underlying DOM can, but you can’t get that structure through YUI). You could delete the “display” property alone, though, if you get hold of the rule by YUI means.

Dojo has some badly documented and incomplete stuff under dojox.html.styles

Ext JS has Ext.util.CSS. I checked the code and found a bug in getRule()... It is otherwise pretty sloppy with selector-matching (bad IE influence), which makes it bad for multi-selector rules. It also can’t delete properties through the API, but can give you the CSSRule so you can do it yourself. – The CSS tree walking is as primitive as it could be: no descending on media rules or imports.

PD:

$('.reveal').css(...whatever...) is not the answer, because it does not touch the CSS rules at all (it touches the CSS attributes of some element(s) instead)!

Omland answered 13/7, 2011 at 17:28 Comment(4)
Might want to add the Dojo Toolkit javascript library in the list as well. I'm not sure it currently does what you are looking for though, though it has some pretty nice CSS accessor / modifying features. Once upon a time I would of recommended "behaviour-js", but the creator, Ben Nolan, states that it is widely adopted already in the frameworks you've already enumerated.Eggert
See: #622622Paulie
@Richard, I read that question before and it doesn’t even answer itself (how to edit CSS rules in jQuery), nor does it answer my question (which framework has decent CSS rule editing support). – I know already that there is a lengthy way to do it in JavaScript itself.Omland
This is an excellent question, also one I'm grappling with. There should be an easy way to do it. I can think of tons of use cases for it.Ariminum
O
3

None, but please prove me wrong...

A decent implementation would have to discuss/document the following first:

  • how it walks the CSS tree (including @imports and @media)
  • how the walk can be configured (which @media and which sheets are considered)
  • how cross domain access restrictions are handled/circumvented
  • as CSS has no IDs for the rules itself: only the selector can act as a decent rule identifiers
    • as IE8 and below split up multi-selectors, how smart is the framework to handle this?
    • IE9 is worse (see quirksmode.org)
  • as multiple rules could be selected by one selector, how are they ordered?
  • how can the rule in charge be found from a rule-set knowing the property we want to edit?

Until the frameworks catch up, consider:

  • get the style/link node where the rule you look for is in
  • use the node.sheet to go to the CSSStyleSheet directly
  • check quirksmode.org for quirks and f... IE
  • loop over the rules to find your rule(s) via known selector
  • DOM CSSStyleRule gives you all the power you need over any style rule
Omland answered 15/7, 2011 at 15:26 Comment(3)
Yes, someone please write this library :)Ariminum
what is this "non-standard node.sheet" you're talking about? Can't find it.Ariminum
I edited my answer: node.sheet is DOM standard. A DOM node (“node”) that holds style rules (e.g. a <style>...</style>) has a sheet attribute (“node.sheet”) which refers to the CSSStyleSheet object.Omland
B
1

As you noted, YUI has the StyleSheet utility http://developer.yahoo.com/yui/3/stylesheet/

Example:

var sheet = new Y.StyleSheet(styleNode);
sheet.set('.hover, .foo:hover', {
    background: 'red',
    color: '#fff'
});

It also supports creating stylesheets from scratch by passing the constructor a string of css rules.

Note that the same origin policy prevents accessing the rules of remotely sourced link nodes.

It doesn't yet support accessing the rules as an object tree or accessing specific properties of rules. It does support removing rules or properties of rules, though. Today, the best you can do for working with the rules as objects would be something like

var style = sheet.getCssText('.foo'),
    tmp = document.createElement('div');

tmp.style.cssText = sheet.getCssText('.foo');
if (tmp.style.display) { // does the rule include a setting for display
    ...
}

Since messing with stylesheets at runtime is pretty rarely the right solution, the utility hasn't had a lot of developer focus for adding features. Feature requests are welcome, of course.

Breault answered 14/7, 2011 at 8:9 Comment(4)
We have 2011, flying cars and spaceships. And there is no way to dynamically change a stylesheet? – No, at least not with your framework of choice. – How would you manually change a style of your HTML from blue to green? By adding “style” to all affected elements? No, of course not. You would go for a single point edit in the style sheet. But if you do it from a script, you call it “messing”. Why? – In my eyes it is the only right way to do it: change it where it needs to be changed, not overwriting it at ten different places and hoping it won’t break...Omland
Element style changes trigger a repaint or reflow that the browser tries very hard to limit the scope of. It also batches reflows, so changing ten element styles will only trigger one reflow or repaint if done properly, and the amount of the page that the repaint/reflow affects will be minimized. Any change to the stylesheet will trigger a full page reflow, the worst thing you can do for a responsive UI. Apps should be built to support a change of blue to green using classes and cascading rules.Breault
Also, cross browser stylesheet API support has a rocky history to say the least, whereas element style and class modification is pretty rock solid. I sympathize with your want of a more convenient API to manipulate stylesheets, but that doesn't make it the right tool for the job. And fwiw, YUI's StyleSheet util can edit multi-selector rules.Breault
A style sheet change is is one reflow for sure. If it is cheaper or not depends very much on the page and the browser. – YUI’s StyleSheet util does multi-selector according to IE rules only, on Chrome, etc. it can’t neither select nor change my first example. – And the cross-browser-problem is exactly why I look for a framework that does that for me!Omland
H
0

There exist libraries like postCSS, which allows you to use a modular ecosystem of plugins to change your CSS styles programatically.

Husein answered 13/11, 2015 at 9:11 Comment(1)
postCSS changes the styles before they reach the browser.—This question is about changing a style in the user’s browser with the browser’s JS.Omland
P
0

The question by Robert Siemer explained:

This is about changing a style rule. I want to have dynamic CSS classes which change.

I wanted to give an answer that better defines the question for the readers who are looking for a similar thing and for the writers who mixed up the issue with updating the style of an element.

The Beef

I think the first part of the beef is to note that the browsers do allow manipulation of the existing stylesheets, as implied in this comment and analyzed in this answer. For example, the following line modifies padding-top value from 20px to 300px on the third rule in the second stylesheet of the document:

document.styleSheets[1].cssRules[2].style.paddingTop = '300px'

... given index.html:

<html>
<head>
  <link type="text/css" href="foo.css"
  <link type="text/css" href="style.css">
<head>
<body><h1>Bar</h1></body>
</html>

... and style.css:

html, body { padding: 0; margin: 0 }
body { color: black; }
h1 { color: gray; padding-top: 20px; }

The second part of the beef is how to find rules that match a criterion. The both parts have challenges in compatibility with older browsers, as discussed by the answer. If we forget compatibility issues and focus on how such a framework should roughly work, here is my minimal implementation in vanilla JavaScript.

Microframework

function forEachCSSRule (iteratee) {
  var i, j, crs
  for (i = 0; i < document.styleSheets.length; i += 1) {
    crs = document.styleSheets[i].cssRules
    for (j = 0; j < crs.lenght; j += 1) {
      iteratee(crs[j])
    }
  }
}

function getCSSRulesBySelector (selectorString) {
  var matched = []
  forEachCSSRule(function (cr) {
    if (cr.selectorText.includes(selectorString)) {
      matched.push(cr)
    }
  })
  return matched
}

function getCSSRulesByStyle (styleString) {
  var matched = []
  forEachCSSRule(function (cr) {
    if (cr.cssText.includes(styleString)) {
      matched.push(cr)
    }
  })
  return matched
}

With such a framework you can for example:

var rules = getCSSRulesByStyle('display: none')
rules.forEach(function (rule) {
  rule.style.display = 'block'
})

... which is something the original question requested.

Privileged answered 12/3, 2018 at 1:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.