+ The StyleSheet module normalizes the dynamic creation and modification
+ of CSS stylesheets on a page. This makes it easy to manage the
+ development, storage, and reapplication of personalized user
+ stylesheets. Because it targets styles at the CSS level, it also
+ allows you to modify styles applied to a CSS pseudo-element such as
+ p::first-letter, or pseudo-class such as
+ a:hover.
+
+ StyleSheet is capable of creating new stylesheets from scratch or
+ modifying existing stylesheets held as properties of
+ <link> or <style> elements. It
+ should be noted that not all browsers support reading or modifying
+ external stylesheets from other domains.
+
Getting Started
+ ++To include the source files for StyleSheet and its dependencies, first load +the YUI seed file if you haven't already loaded it. +
+ +<script src="http://yui.yahooapis.com/3.10.3/build/yui/yui-min.js"></script>+ + +
+Next, create a new YUI instance for your application and populate it with the
+modules you need by specifying them as arguments to the YUI().use() method.
+YUI will automatically load any dependencies required by the modules you
+specify.
+
<script>
+// Create a new YUI instance and populate it with the required modules.
+YUI().use('stylesheet', function (Y) {
+ // StyleSheet is available and ready for use. Add implementation
+ // code here.
+});
+</script>
+
+
+
+For more information on creating YUI instances and on the
+use() method, see the
+documentation for the YUI Global Object.
+
Using the StyleSheet Utility
+ +Instantiating a Y.StyleSheet
+
+
+ The Y.StyleSheet constructor is written to support both
+ function syntax and normal constructor syntax making the new
+ prefix unnecessary (but harmless).
+
+ The constructor has no required parameters. Passing no arguments will + create a new, empty StyleSheet instance. +
+ +// These are equivalent; both create new empty StyleSheets +var myStyleSheet = new Y.StyleSheet(); + +var myOtherStyleSheet = Y.StyleSheet();+ + +
+ To seed a new StyleSheet with a number of CSS rules, you can pass the + constructor any of the following: +
+ +-
+
-
+ a
<style>or<link>node + reference, +
+ -
+ the id of a
<style>or<link>+ node, or +
+ - a string of CSS +
<link id="local" type="text/css" rel="stylesheet" href="local_file.css">
+<style id="styleblock" type="text/css">
+ .some select.or {
+ margin-right: 2em;
+ }
+</style>
+
+
+YUI().use('node','stylesheet', function (Y) {
+
+ // Node or HTMLElement reference for a style or locally sourced link element
+ var sheet = Y.StyleSheet(Y.one("#styleblock"));
+ sheet = Y.StyleSheet(Y.DOM.byId('local'));
+
+ // OR the id of a style element or locally sourced link element
+ sheet = Y.StyleSheet('#local');
+
+ // OR string of css text
+ var css = ".moduleX .alert { background: #fcc; font-weight: bold; } " +
+ ".moduleX .warn { background: #eec; } " +
+ ".hide_messages .moduleX .alert, " +
+ ".hide_messages .moduleX .warn { display: none; }";
+
+ sheet = new Y.StyleSheet(css);
+
+ //...
+});
+
+
+
+ Be aware that the Same Origin
+ policy prevents access in some browsers to the style data of
+ <link> elements with hrefs pointing to
+ other domains. Attempts to seed a Y.StyleSheet instance with
+ a cross-domain <link> may result in a security
+ error.
+
<link id="remote" type="text/css" rel="stylesheet" href="http://other.domain.com/remote_file.css">+ + +
// ERROR - Same Origin Policy prevents access to remote stylesheets
+var styleSheet = Y.StyleSheet('remote');
+
+
+Getting a StyleSheet by registered name
+ +
+ Y.StyleSheet supports registering instances by name allowing
+ them to be recalled by that same name elsewhere in your code. Internally,
+ Y.StyleSheet maintains a registry of all created StyleSheet
+ instances, using a unique generated id that the host node is tagged with.
+ This allows future attempts to create a StyleSheet instance from the same
+ node to return the previously created instance associated with that id.
+
+ Register a StyleSheet instance manually using the static
+ register method or pass the desired name as a second parameter
+ to the constructor.
+
var sheetA = Y.StyleSheet('my_stylesheet');
+
+// Create a registry alias to sheetA. We'll call it bob.
+Y.StyleSheet.register(sheetA, 'bob');
+
+// Create another StyleSheet passing the name as the second parameter
+var css = ".some .css { white-space: pre-wrap; color: pink; }";
+var sheetB = Y.StyleSheet(css, 'my sheet');
+
+// Meanwhile, elsewhere in your code
+
+// sheetA is the same instance as sheet1 and sheet2
+var sheet1 = Y.StyleSheet(Y.one('#my_stylesheet')),
+ sheet2 = Y.StyleSheet('bob');
+
+// sheetB is the same instance as sheet3
+var sheet3 = Y.StyleSheet('my sheet');
+
+
++ If an unregistered name is passed as the first argument to the + constructor, a new empty StyleSheet will be created and registered with + that name. This allows you to use the following code pattern: +
+ +// Whichever of these executes first, an empty StyleSheet will be created
+// and registered as 'MyApp'.
+
+// In one area of your app
+Y.StyleSheet('MyApp').set('.module .messages', { display: 'none' });
+
+//...
+
+// In another area of your app
+Y.StyleSheet('MyApp').unset('.module .messages','display');
+
+
+Summary of how the constructor handles the first argument
+ ++ When nothing is passed as the first argument, a new StyleSheet instance is + created. +
+ +
+ When a <style> or <link> element is
+ passed as the first argument, it is inspected for the id stamp that
+ StyleSheet tags known host nodes with. If it finds one, it will return the
+ associated StyleSheet from the registry. If not, it will stamp the node
+ and seed the instance from the node's CSS content.
+
+ When a string is passed as the first argument, StyleSheet does the + following things in order: +
+ +-
+
- + Check the registry for an instance associated to that name. If found, + return the instance. + +
-
+ Check the DOM for a
<style>or +<link>node with that id. If found, check the + registry for an instance associated to its tagged id if present. If + found, return that instance. If not, use that node to seed a new + StyleSheet instance. +
+ -
+ Check the string for a curly brace { character. If found, create a new
+ instance seeded with the string as initial
cssText. +
+ - + Create a new empty StyleSheet and register the instance by the provided + string. + +
Creating and modifying CSS style rules
+ +
+ The core method of StyleSheet instances is set(selector,
+ style_properties). It will create or alter a CSS rule using the
+ property:value pairs in style_properties targeting the
+ provided selector. In essence, it looks very much like
+ natural CSS syntax, except style properties must be in JavaScript's
+ camelCase.
+
Y.StyleSheet('MyApp').set(
+ "q.uoted select.or[str=ing]", {
+ fontSize : "150%", // note the camel casing
+ background : "#030 url(/images/bg_image.png) scroll repeat-y top left",
+ cssFloat : "left",
+ opacity : 0.5
+ });
+
+
++ Rather than continually add new rules that will override one another, + StyleSheet manages one rule per selector and modifies them in place. This + may be relevant if you have two or more rules with selectors of the same + specificity. +
+ +
+ As with regular CSS syntax, comma-separated selectors are supported, but
+ internally StyleSheet splits them up into individual rules because browser
+ support for multiple selectors is not consistent. This means calling
+ set(..) with such a selector string will incur multiple
+ repaints or reflows, but limited to the number of atomic
+ selectors.
+
// This is valid, but will trigger 3 reflows
+Y.StyleSheet('MyApp').set(
+ '.foo, .bar, .baz', {
+ borderRight: "1em solid #f00"
+ });
+
+
+Some style properties are normalized
+ +
+ Two style properties have differing implementation between browsers, namely
+ float and opacity. StyleSheet instances will
+ normalize these properties for you.
+
+ Because "float" is a reserved word in JavaScript, it is supported
+ by the name cssFloat in W3C compliant browsers and
+ styleFloat in IE. StyleSheet will accept any of these to set
+ the float property.
+
// Any of these will work
+Y.StyleSheet('MyApp').set('.foo', {
+ "float" : "left", // "float" must be quoted
+ cssFloat : "right",
+ styleFloat : "none"
+});
+
+
+
+ IE does not support the opacity style property, but has
+ equivalent functionality offered by its proprietary filter
+ property, though using a different value syntax. StyleSheet will translate
+ opacity to filter for IE, but it will
+ not translate filter to opacity for
+ W3C-compliant browsers.
+
Removing and resetting CSS style rules
+ +
+ When you want to remove a particular rule or style property from affecting
+ the cascade, use unset(selector,propert[y|ies]).
+
+ unset(..) can be called in any of the following ways, with the
+ noted result:
+
-
+
-
+
unset('.foo')— removes the rule associated to the + selector entirely. +
+ -
+
unset('.foo','font')— unsets thefont+ property and any child properties (e.g. + 'font-weight','font-variant','font-size','line-height', and + 'font-family'). If there are no set properties left, the rule is + removed. +
+ -
+
unset('.foo',['font','border',...])— same as above, + but the rule is modified only once with the final applicable +cssText. +
+
+ It is important to note that there is a difference between setting a style
+ property to its default value and unsetting it. The former can be achieved
+ by calling set(selector, { property: "auto" }) (or
+ the respective default value for that property).
+
+ However, as the CSS is reapplied to the page, the "auto" value + will override any value for that property that may have cascaded in from + another rule. This is different than removing the property assignment + entirely, as this allows cascading values through. +
+ +Y.StyleSheet('MyApp').set('.foo', { background: 'auto' });
+
+// is NOT the same as
+
+Y.StyleSheet('MyApp').unset('.foo','background');
+
+
+A note on selector strings
+ ++ Though the StyleSheet Utility takes selector strings as input to its API, + it does not leverage the YUI selector engine. YUI's selector + functionality supplements native CSS support for DOM access, but + accomplishes this through efficient DOM traversal. Since the StyleSheet + Utility uses the browser's built-in stylesheet and rule objects, it can not + handle selectors that are not supported by the browser's native CSS + parser. +
+ +// This will not cause a style change in IE 6, for example
+Y.StyleSheet('MyApp').set('input[type=checkbox]:checked', {
+ verticalAlign : 'super'
+});
+
+
+Disabling and enabling a StyleSheet
+ ++ Disabling a StyleSheet effectively turns it off; no CSS from that + stylesheet is applied to the page. Disabling a StyleSheet does not remove + the host node from the page, and style can be reapplied by enabling the + StyleSheet again. +
+ +
+ When StyleSheets are disabled, it is still possible to change their style
+ rules via set and unset.
+
var sheet = Y.StyleSheet(styleNode);
+
+sheet.disable();
+sheet.set('.foo', { backgroundColor: '#900', color: '#fff' });
+sheet.set('.bar', { borderBottomColor: '#369' });
+sheet.unset('.baz');
+sheet.enable();
+
+
+Static methods
+ +
+ Y.StyleSheet exposes a few static methods.
+
| Method | +Use for | +
|---|---|
+Y.StyleSheet.register(instance, name)+ + |
+ Use to assign a named registry entry for a StyleSheet instance. | +
+Y.StyleSheet.toCssText(property_obj, starting_cssText)+ + |
+ Use to translate an object of style property:value pairs to a single cssText string. The optional second argument is a cssText string of a style's "before" state. |
+
+ Y.StyleSheet.toCssText is used internally to assemble the
+ cssText strings for updating the stylesheet rules. However,
+ it may also be helpful for avoiding reflow overhead when substantially
+ modifying a single element's style.
+
var el = Y.one('some_element'),
+ changes = { color : '#684', fontWeight: 'bold', padding: '2em' },
+ currentStyle = el.getStyle('cssText');
+
+el.setStyle('cssText',
+ Y.StyleSheet.toCssText(changes, currentStyle));
+
+
+How Y.StyleSheet works
+
+
+ Browsers grant access via the DOM API to stylesheets included in markup as
+ <link> or <style> elements. Despite
+ differing implementations across the browser spectrum, they all support
+ adding, removing, and modifying CSS rules.
+
+ CSS rules are comprised of a selector and collection of style + property:value pairs enclosed in curly braces. +
+ +/* | This is a CSS rule |
+ | selectorText | style properties | */
+ div.this-is a .rule { font-color: #f00; }
+
+
+
+ In JavaScript, each rule object has a selectorText property
+ and a style property that operates in the same way as the
+ style property on regular DOM elements, such as
+ <p> or <strong> elements.
+
+ Arguably the most valuable property of the style collection is
+ cssText which corresponds to the serialized summary of
+ property:value pairs applied by this collection (e.g. "font-size: 100%;
+ color: #FF0000;"). The reason this property is important is that
+ modifications to the string value will cause changes to repopulate the
+ individual style properties while only triggering a single repaint or
+ reflow by the browser.
+
var el = document.getElementById('some_element');
+
+el.style.borderBottom = '3px solid #eee'; // reflow
+el.style.borderTop = '3px solid #ccc'; // another reflow
+el.style.fontWeight = 'bold'; // another reflow
+
+// Vs. three changes in one reflow
+el.style.cssText += '; border-bottom: 3px solid #eee; border-top: 3px solid #ccc; font-weight: bold';
+
+
+
+ Y.StyleSheet leverages this mechanism in addition to applying
+ modifications at the CSS rule level rather than modifying each targeted DOM
+ node directly. This means changing multiple style properties on multiple
+ elements (that can be identified by a single selector) will only ever incur
+ one repaint or reflow.
+
+ It must be noted that all reflows are not the same. The scope of a reflow + is greatly affected by what element triggered it. For example, changing a + style of an absolutely positioned element will trigger a very limited + reflow because the browser understands that not much could change + as a result. Stylesheet modifications on the other hand are not tied to an + element, but the page as a whole. The CSS cascade must be recalculated and + applied, resulting in a full page reflow. This means it may be more + efficient to individually update many elements than to change the + stylesheet. +
+ +Known Issues
+ +
+ Unable to set style values with
+ !important.
+
+ CSS syntax for declaring that a style value has important
+ priority is to include the !important flag after the
+ value.
+
.some-class {
+ color: #000 !important;
+}
+
+
+
+ However, the DOM API for modifying stylesheets does not parse out the
+ !important flag from the assigned value string, and thus
+ considers the entire string to be an invalid value.
+
el.style.color = "#000 !important"; // Error+ + +
+ StyleSheet will support !important in the value string in a
+ future release, but for now the only way to assign an
+ !important style is by creating a new StyleSheet, passing a
+ CSS text string to the constructor.
+
var sheet = new Y.StyleSheet();
+sheet.set(".foo", { color: "#000 !important" }); // FAIL
+
+new Y.StyleSheet(".foo { color: #000 !important; }"); // WORKS
+
+