Skip to content

Instantly share code, notes, and snippets.

@Sjeiti
Last active December 3, 2019 09:28
Show Gist options
  • Save Sjeiti/11355844 to your computer and use it in GitHub Desktop.
Save Sjeiti/11355844 to your computer and use it in GitHub Desktop.
Javascript to manipulate existing stylesheets
/**
* Manipulate existing stylesheets.
* @name rootStyle
*/
window.rootStyle = (function() {
'use strict';
// StyleSheet.addRule shim
if (document.styleSheets[0].addRule===undefined) {
window.StyleSheet.prototype.addRule = function(selector,value){
return this.insertRule(selector + '{' + value + '}', this.cssRules.length);
};
}
var forEach = Array.prototype.forEach
,aoStyleSheets = document.styleSheets
,bReversedSelectors = (function(){
// creates a new selector and checks if the rule comes out in reverse
var oSheet = aoStyleSheets[0]
,aRules = oSheet.cssRules
,iNumRules = aRules.length
,sSelector = 'span#a.c.d.b'
,sSelectorResult
,bIsReversed
;
oSheet.addRule(sSelector, 'font-weight:inherit');
sSelectorResult = aRules[iNumRules].selectorText;
bIsReversed = sSelectorResult!=sSelector;
oSheet.deleteRule(iNumRules);
return bIsReversed;
})()
;
/**
* Reverses a selector (because IE rearanges selectors)
* Turns span#a.c.d.b into span.b.c.d#a
* @param {String} selector
* @returns {String} Reversed selector
*/
function getReverse(selector){
var oSheet = aoStyleSheets[0]
,aRules = oSheet.cssRules
,iNumRules = aRules.length
,sSelectorResult
;
oSheet.addRule(selector, 'font-weight:inherit');
sSelectorResult = aRules[iNumRules].selectorText;
oSheet.deleteRule(iNumRules);
return sSelectorResult;
}
/**
* Get an array of CSSStyleRules for the selector string.
* @param {String} selector
* @returns {iddqd.style.rule}
*/
function select(selector){
selector = bReversedSelectors?getReverse(parseSelector(selector)):parseSelector(selector);
var aStyles = rule();
forEach.apply(aoStyleSheets,[function(styleSheet){
var aRules = styleSheet.cssRules;
aRules && forEach.apply(aRules,[function (oRule) {
if (oRule.constructor===window.CSSStyleRule) {
if (oRule.selectorText.split(' {').shift()==selector) {
aStyles.push(oRule);
}
}
}]);
}]);
return aStyles;
}
/**
* Parse a selector string correctly
* @param {string} selector
* @returns {string}
*/
function parseSelector(selector) {
return selector.replace('>',' > ').replace(' ',' ');
}
/**
* Change an existing selector with the rules parsed in the object.
* @param {String} selector
* @param {Object} rules
* @returns {CSSStyleRule[]} The Array created by select
*/
function changeRule(selector,rules) {
return select(selector).set(rules);
}
/**
* Adds a new rule to the selector
* @param selector
* @param rules
* @returns {CssRule}
*/
function addRule(selector,rules) {
var oSheet,sRules = '';
iddqd.loop(rules,function(prop,val){sRules+=prop+':'+val+';';});
oSheet = aoStyleSheets[0];
oSheet.addRule(selector,sRules);
return oSheet.cssRules[oSheet.cssRules.length-1];
}
/**
* A array of CSSStyleRules enhanced with some methods.
* @returns {CSSStyleRule[]}
*/
function rule() {
var aStyles = [];
aStyles.set = function (key,prop) {
if (typeof key==='string') {
set.apply(aStyles,[key,prop]);
} else {
for (var s in key) {
set.apply(aStyles,[s,key[s]]);
}
}
};
function set(key,prop) {
aStyles.forEach(function (rule) {
var oStyle = rule.style;
oStyle.removeProperty(key);
oStyle[key] = prop;
});
}
return aStyles;
}
// Expose private methods
return {
toString: function(){return '[object rootStyle]';}
,select: select
,changeRule: changeRule
,addRule: addRule
};
})();
@Zibri
Copy link

Zibri commented Nov 30, 2019

I think it's easier to write:

StyleSheetList.prototype.forEach=Array.prototype.forEach
and then

document.styleSheets.forEach(console.log)

@Sjeiti
Copy link
Author

Sjeiti commented Dec 2, 2019

Changing existing prototypes is generally considered bad practice.
Plus these days spreading also makes it a little easier to read and write: [...styleSheets].forEach(styleSheet=>{})

@Zibri
Copy link

Zibri commented Dec 3, 2019

probably... [...styleSheets].forEach(styleSheet=>{}) is easier to write even if a little less compatible...

perhaps Array.prototype.forEach.apply(document.styleSheets,[(ss)=>ss.ownerNode.parentNode.removeChild(ss.ownerNode)]);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment