Related
I need to create a CSS stylesheet class dynamically in JavaScript and assign it to some HTML elements like - div, table, span, tr, etc and to some controls like asp:Textbox, Dropdownlist and datalist.
Is it possible?
It would be nice with a sample.
Here is an option:
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.cssClass { color: #f00; }';
document.getElementsByTagName('head')[0].appendChild(style);
document.getElementById('someElementId').className = 'cssClass';
<div id="someElementId">test text</div>
Found a better solution, which works across all browsers.
Uses document.styleSheet to add or replace rules. Accepted answer is short and handy but this works across IE8 and less too.
function createCSSSelector (selector, style) {
if (!document.styleSheets) return;
if (document.getElementsByTagName('head').length == 0) return;
var styleSheet,mediaType;
if (document.styleSheets.length > 0) {
for (var i = 0, l = document.styleSheets.length; i < l; i++) {
if (document.styleSheets[i].disabled)
continue;
var media = document.styleSheets[i].media;
mediaType = typeof media;
if (mediaType === 'string') {
if (media === '' || (media.indexOf('screen') !== -1)) {
styleSheet = document.styleSheets[i];
}
}
else if (mediaType=='object') {
if (media.mediaText === '' || (media.mediaText.indexOf('screen') !== -1)) {
styleSheet = document.styleSheets[i];
}
}
if (typeof styleSheet !== 'undefined')
break;
}
}
if (typeof styleSheet === 'undefined') {
var styleSheetElement = document.createElement('style');
styleSheetElement.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(styleSheetElement);
for (i = 0; i < document.styleSheets.length; i++) {
if (document.styleSheets[i].disabled) {
continue;
}
styleSheet = document.styleSheets[i];
}
mediaType = typeof styleSheet.media;
}
if (mediaType === 'string') {
for (var i = 0, l = styleSheet.rules.length; i < l; i++) {
if(styleSheet.rules[i].selectorText && styleSheet.rules[i].selectorText.toLowerCase()==selector.toLowerCase()) {
styleSheet.rules[i].style.cssText = style;
return;
}
}
styleSheet.addRule(selector,style);
}
else if (mediaType === 'object') {
var styleSheetLength = (styleSheet.cssRules) ? styleSheet.cssRules.length : 0;
for (var i = 0; i < styleSheetLength; i++) {
if (styleSheet.cssRules[i].selectorText && styleSheet.cssRules[i].selectorText.toLowerCase() == selector.toLowerCase()) {
styleSheet.cssRules[i].style.cssText = style;
return;
}
}
styleSheet.insertRule(selector + '{' + style + '}', styleSheetLength);
}
}
Function is used as follows.
createCSSSelector('.mycssclass', 'display:none');
Short answer, this is compatible "on all browsers" (specifically, IE8/7):
function createClass(name,rules){
var style = document.createElement('style');
style.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(style);
if(!(style.sheet||{}).insertRule)
(style.styleSheet || style.sheet).addRule(name, rules);
else
style.sheet.insertRule(name+"{"+rules+"}",0);
}
createClass('.whatever',"background-color: green;");
And this final bit applies the class to an element:
function applyClass(name,element,doRemove){
if(typeof element.valueOf() == "string"){
element = document.getElementById(element);
}
if(!element) return;
if(doRemove){
element.className = element.className.replace(new RegExp("\\b" + name + "\\b","g"));
}else{
element.className = element.className + " " + name;
}
}
Here's a little test page as well: https://gist.github.com/shadybones/9816763
The key little bit is the fact that style elements have a "styleSheet"/"sheet" property which you can use to to add/remove rules on.
There is a light jQuery plugin which allows to generate CSS declarations: jQuery-injectCSS
In fact, it uses JSS (CSS described by JSON), but it's quite easy to handle in order to generate dynamic css stylesheets.
$.injectCSS({
"#test": {
height: 123
}
});
YUI has by far the best stylesheet utility I have seen out there. I encourage you to check it out, but here's a taste:
// style element or locally sourced link element
var sheet = YAHOO.util.StyleSheet(YAHOO.util.Selector.query('style',null,true));
sheet = YAHOO.util.StyleSheet(YAHOO.util.Dom.get('local'));
// OR the id of a style element or locally sourced link element
sheet = YAHOO.util.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 YAHOO.util.StyleSheet(css);
There are obviously other much simpler ways of changing styles on the fly such as those suggested here. If they make sense for your problem, they might be best, but there are definitely reasons why modifying CSS is a better solution. The most obvious case is when you need to modify a large number of elements. The other major case is if you need your style changes to involve the cascade. Using the DOM to modify an element will always have a higher priority. It's the sledgehammer approach and is equivalent to using the style attribute directly on the HTML element. That is not always the desired effect.
As of IE 9. You can now load a text file and set a style.innerHTML property. So essentially you can now load a css file through ajax (and get the callback) and then just set the text inside of a style tag like this.
This works in other browsers, not sure how far back. But as long as you don't need to support IE8 then it would work.
// RESULT: doesn't work in IE8 and below. Works in IE9 and other browsers.
$(document).ready(function() {
// we want to load the css as a text file and append it with a style.
$.ajax({
url:'myCss.css',
success: function(result) {
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.innerHTML = result;
document.getElementsByTagName("head")[0].appendChild(s);
},
fail: function() {
alert('fail');
}
})
});
and then you can have it pull an external file like the myCss.css
.myClass { background:#F00; }
Using google closure:
you can just use the ccsom module:
goog.require('goog.cssom');
var css_node = goog.cssom.addCssText('.cssClass { color: #F00; }');
The javascript code attempts to be cross browser when putting the css node into the document head.
Here is Vishwanath's solution slightly rewritten with comments :
function setStyle(cssRules, aSelector, aStyle){
for(var i = 0; i < cssRules.length; i++) {
if(cssRules[i].selectorText && cssRules[i].selectorText.toLowerCase() == aSelector.toLowerCase()) {
cssRules[i].style.cssText = aStyle;
return true;
}
}
return false;
}
function createCSSSelector(selector, style) {
var doc = document;
var allSS = doc.styleSheets;
if(!allSS) return;
var headElts = doc.getElementsByTagName("head");
if(!headElts.length) return;
var styleSheet, media, iSS = allSS.length; // scope is global in a function
/* 1. search for media == "screen" */
while(iSS){ --iSS;
if(allSS[iSS].disabled) continue; /* dont take into account the disabled stylesheets */
media = allSS[iSS].media;
if(typeof media == "object")
media = media.mediaText;
if(media == "" || media=='all' || media.indexOf("screen") != -1){
styleSheet = allSS[iSS];
iSS = -1; // indication that media=="screen" was found (if not, then iSS==0)
break;
}
}
/* 2. if not found, create one */
if(iSS != -1) {
var styleSheetElement = doc.createElement("style");
styleSheetElement.type = "text/css";
headElts[0].appendChild(styleSheetElement);
styleSheet = doc.styleSheets[allSS.length]; /* take the new stylesheet to add the selector and the style */
}
/* 3. add the selector and style */
switch (typeof styleSheet.media) {
case "string":
if(!setStyle(styleSheet.rules, selector, style));
styleSheet.addRule(selector, style);
break;
case "object":
if(!setStyle(styleSheet.cssRules, selector, style));
styleSheet.insertRule(selector + "{" + style + "}", styleSheet.cssRules.length);
break;
}
One liner, attach one or many new cascading rule(s) to the document.
This example attach a cursor:pointer to every button, input, select.
document.body.appendChild(Object.assign(document.createElement("style"), {textContent: "select, button, input {cursor:pointer}"}))
https://jsfiddle.net/xk6Ut/256/
One option to dynamically create and update CSS class in JavaScript:
Using Style Element to create a CSS section
Using an ID for the style element so that we can update the CSS
class
.....
function writeStyles(styleName, cssText) {
var styleElement = document.getElementById(styleName);
if (styleElement)
document.getElementsByTagName('head')[0].removeChild(
styleElement);
styleElement = document.createElement('style');
styleElement.type = 'text/css';
styleElement.id = styleName;
styleElement.innerHTML = cssText;
document.getElementsByTagName('head')[0].appendChild(styleElement);
}
...
var cssText = '.testDIV{ height:' + height + 'px !important; }';
writeStyles('styles_js', cssText)
An interesting project which could help you out in your task is JSS.
JSS is an authoring tool for CSS which allows you to use JavaScript to describe styles in a declarative, conflict-free and reusable way. It can compile in the browser, server-side or at build time in Node.
JSS library allows you to inject in the DOM/head section using the .attach() function.
Repl online version for evaluation.
Further information on JSS.
An example:
// Use plugins.
jss.use(camelCase())
// Create your style.
const style = {
myButton: {
color: 'green'
}
}
// Compile styles, apply plugins.
const sheet = jss.createStyleSheet(style)
// If you want to render on the client, insert it into DOM.
sheet.attach()
I was looking through some of the answers here, and I couldn't find anything that automatically adds a new stylesheet if there are none, and if not simply modifies an existing one that already contains the style needed, so I made a new function (should work accross all browsers, though not tested, uses addRule and besides that only basic native JavaScript, let me know if it works):
function myCSS(data) {
var head = document.head || document.getElementsByTagName("head")[0];
if(head) {
if(data && data.constructor == Object) {
for(var k in data) {
var selector = k;
var rules = data[k];
var allSheets = document.styleSheets;
var cur = null;
var indexOfPossibleRule = null,
indexOfSheet = null;
for(var i = 0; i < allSheets.length; i++) {
indexOfPossibleRule = findIndexOfObjPropInArray("selectorText",selector,allSheets[i].cssRules);
if(indexOfPossibleRule != null) {
indexOfSheet = i;
break;
}
}
var ruleToEdit = null;
if(indexOfSheet != null) {
ruleToEdit = allSheets[indexOfSheet].cssRules[indexOfPossibleRule];
} else {
cur = document.createElement("style");
cur.type = "text/css";
head.appendChild(cur);
cur.sheet.addRule(selector,"");
ruleToEdit = cur.sheet.cssRules[0];
console.log("NOPE, but here's a new one:", cur);
}
applyCustomCSSruleListToExistingCSSruleList(rules, ruleToEdit, (err) => {
if(err) {
console.log(err);
} else {
console.log("successfully added ", rules, " to ", ruleToEdit);
}
});
}
} else {
console.log("provide one paramter as an object containing the cssStyles, like: {\"#myID\":{position:\"absolute\"}, \".myClass\":{background:\"red\"}}, etc...");
}
} else {
console.log("run this after the page loads");
}
};
then just add these 2 helper functions either inside the above function, or anywhere else:
function applyCustomCSSruleListToExistingCSSruleList(customRuleList, existingRuleList, cb) {
var err = null;
console.log("trying to apply ", customRuleList, " to ", existingRuleList);
if(customRuleList && customRuleList.constructor == Object && existingRuleList && existingRuleList.constructor == CSSStyleRule) {
for(var k in customRuleList) {
existingRuleList["style"][k] = customRuleList[k];
}
} else {
err = ("provide first argument as an object containing the selectors for the keys, and the second argument is the CSSRuleList to modify");
}
if(cb) {
cb(err);
}
}
function findIndexOfObjPropInArray(objPropKey, objPropValue, arr) {
var index = null;
for(var i = 0; i < arr.length; i++) {
if(arr[i][objPropKey] == objPropValue) {
index = i;
break;
}
}
return index;
}
(notice that in both of them I use a for loop instead of .filter, since the CSS style / rule list classes only have a length property, and no .filter method.)
Then to call it:
myCSS({
"#coby": {
position:"absolute",
color:"blue"
},
".myError": {
padding:"4px",
background:"salmon"
}
})
Let me know if it works for your browser or gives an error.
Looked through the answers and the most obvious and straight forward is missing: use document.write() to write out a chunk of CSS you need.
Here is an example (view it on codepen: http://codepen.io/ssh33/pen/zGjWga):
<style>
#import url(http://fonts.googleapis.com/css?family=Open+Sans:800);
.d, body{ font: 3vw 'Open Sans'; padding-top: 1em; }
.d {
text-align: center; background: #aaf;
margin: auto; color: #fff; overflow: hidden;
width: 12em; height: 5em;
}
</style>
<script>
function w(s){document.write(s)}
w("<style>.long-shadow { text-shadow: ");
for(var i=0; i<449; i++) {
if(i!= 0) w(","); w(i+"px "+i+"px #444");
}
w(";}</style>");
</script>
<div class="d">
<div class="long-shadow">Long Shadow<br> Short Code</div>
</div>
For the benefit of searchers; if you are using jQuery, you can do the following:
var currentOverride = $('#customoverridestyles');
if (currentOverride) {
currentOverride.remove();
}
$('body').append("<style id=\"customoverridestyles\">body{background-color:pink;}</style>");
Obviously you can change the inner css to whatever you want.
Appreciate some people prefer pure JavaScript, but it works and has been pretty robust for writing/overwriting styles dynamically.
function createCSSClass(selector, style, hoverstyle)
{
if (!document.styleSheets)
{
return;
}
if (document.getElementsByTagName("head").length == 0)
{
return;
}
var stylesheet;
var mediaType;
if (document.styleSheets.length > 0)
{
for (i = 0; i < document.styleSheets.length; i++)
{
if (document.styleSheets[i].disabled)
{
continue;
}
var media = document.styleSheets[i].media;
mediaType = typeof media;
if (mediaType == "string")
{
if (media == "" || (media.indexOf("screen") != -1))
{
styleSheet = document.styleSheets[i];
}
}
else if (mediaType == "object")
{
if (media.mediaText == "" || (media.mediaText.indexOf("screen") != -1))
{
styleSheet = document.styleSheets[i];
}
}
if (typeof styleSheet != "undefined")
{
break;
}
}
}
if (typeof styleSheet == "undefined") {
var styleSheetElement = document.createElement("style");
styleSheetElement.type = "text/css";
document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
for (i = 0; i < document.styleSheets.length; i++) {
if (document.styleSheets[i].disabled) {
continue;
}
styleSheet = document.styleSheets[i];
}
var media = styleSheet.media;
mediaType = typeof media;
}
if (mediaType == "string") {
for (i = 0; i < styleSheet.rules.length; i++)
{
if (styleSheet.rules[i].selectorText.toLowerCase() == selector.toLowerCase())
{
styleSheet.rules[i].style.cssText = style;
return;
}
}
styleSheet.addRule(selector, style);
}
else if (mediaType == "object")
{
for (i = 0; i < styleSheet.cssRules.length; i++)
{
if (styleSheet.cssRules[i].selectorText.toLowerCase() == selector.toLowerCase())
{
styleSheet.cssRules[i].style.cssText = style;
return;
}
}
if (hoverstyle != null)
{
styleSheet.insertRule(selector + "{" + style + "}", 0);
styleSheet.insertRule(selector + ":hover{" + hoverstyle + "}", 1);
}
else
{
styleSheet.insertRule(selector + "{" + style + "}", 0);
}
}
}
createCSSClass(".modalPopup .header",
" background-color: " + lightest + ";" +
"height: 10%;" +
"color: White;" +
"line-height: 30px;" +
"text-align: center;" +
" width: 100%;" +
"font-weight: bold; ", null);
Here is my modular solution:
var final_style = document.createElement('style');
final_style.type = 'text/css';
function addNewStyle(selector, style){
final_style.innerHTML += selector + '{ ' + style + ' } \n';
};
function submitNewStyle(){
document.getElementsByTagName('head')[0].appendChild(final_style);
final_style = document.createElement('style');
final_style.type = 'text/css';
};
function submitNewStyleWithMedia(mediaSelector){
final_style.innerHTML = '#media(' + mediaSelector + '){\n' + final_style.innerHTML + '\n};';
submitNewStyle();
};
You basically anywhere in your code do:
addNewStyle('body', 'color: ' + color1); , where color1 is defined variable.
When you want to "post" the current CSS file you simply do submitNewStyle(),
and then you can still add more CSS later.
If you want to add it with "media queries", you have the option.
After "addingNewStyles" you simply use submitNewStyleWithMedia('min-width: 1280px');.
It was pretty useful for my use-case, as I was changing CSS of public (not mine) website according to current time. I submit one CSS file before using "active" scripts, and the rest afterwards (makes the site look kinda-like it should before accessing elements through querySelector).
This is what worked for me in Angular:
In HTML I have button with programmatically created CSS with specific ID:
<button [id]="'hoverbutton1'+item.key" [ngClass]="getHoverButtonClass()">
<mat-icon class="icon">open_in_new</mat-icon>
</button>
In typescript I created CSS and assign it to specific element with given ID:
addClasses(){
var style1 = document.createElement('style');
style1.innerHTML = '.hoverbutton'+this.item.key+' { display: none; }';
document.getElementsByTagName('head')[0].appendChild(style1);
}
getHoverButtonClass() {
return "hoverbutton"+this.item.key
}
This way I can create as many CSS classes as I want and assign them to elements individually. :)
To sum up the effects of the filter property we have to do as for this 'union class.'
.class1{filter:brightness(125%);}
.class2{filter:blur(5px);}
.unionClass{filter:brightness(125%) blur(5px);}
But what if the code is written this way?
//i want this
<p class="class1 class2">Hello</p>
//instead of
<p class="unionClass">Hello</p>
In the first example as a result we would only apply the class2 class, so filter class1 class property will be lost; While in the second the 'unionClass' class will display all the united properties, as they are already contained in it.
I would like to see the same effect by writing 'class = "class1 class2' ', how can i do it? Does css not have a right way? So with javascript what would be the right way?
That is not possible to do using CSS.
When splitt'd like that, the latter will overwrite the former, as it will with any other property.
From a maintenance and easy-to-read perspective it could be interesting to do something like this
.fil-bright-125 {
filter:brightness(125%);
}
.fil-blur-5 {
filter:blur(5px);
}
.fil-bright-125.fil-blur-5 {
filter:brightness(125%) blur(5px);
}
And then use it like this
p {
color: blue;
background: yellow;
}
.fil-bright-175 {
filter:brightness(175%);
}
.fil-blur-1 {
filter:blur(1px);
}
.fil-bright-175.fil-blur-1 {
filter:brightness(175%) blur(1px);
}
<p class="fil-bright-175">I am bright</p>
<p class="fil-blur-1">I am blurry</p>
<p class="fil-bright-175 fil-blur-1">I am bright and blurry</p>
Updated
For completeness, here is a version using script, which look up the classes in the style sheet and then creates a new, where they are combined.
Compared with the above, I don't find the below more maintainable, almost the opposite actually.
Note, this script can of course be optimized, though the purpose was to show a sample of how it could be done using script
(function(d, w) {
w.addEventListener("load", function() {
function getStyle(cls) {
var classes = d.styleSheets[0].rules || d.styleSheets[0].cssRules;
var val = '';
for (var x = 0; x < classes.length; x++) {
for (var y = 0; y < cls.length; y++) {
if (classes[x].selectorText == cls[y]) {
val += ' ' + ((classes[x].cssText) ? classes[x].cssText : classes[x].style.cssText).split('filter')[1].replace(/[:;} ]/g, '');
}
}
}
return val;
}
var val = getStyle(['.fil-bright-175','.fil-blur-1']);
val = '.fil-bright-175.fil-blur-1 {filter:' + val + '}';
var head = d.head || d.getElementsByTagName('head')[0],
style = d.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = val;
} else {
style.appendChild(d.createTextNode(val));
}
head.appendChild(style);
}, false);
}(document, window));
p {
color: blue;
background: yellow;
}
.fil-bright-175 {
filter: brightness(175%);
}
.fil-blur-1 {
filter: blur(1px);
}
<p class="fil-bright-175">I am bright</p>
<p class="fil-blur-1">I am blurry</p>
<p class="fil-bright-175 fil-blur-1">I am bright and blurry</p>
I would like to know how can I define a bigger variable for a set of variables that I have in javascript: showFootnotesPanel();, showReferencesPanel();, showImagesPanel();, showInformationPanel();.
Would it be something like this?
function showPanel() {
var x = [showFootnotesPanel();showReferencesPanel();showImagesPanel();showInformationPanel();]
}
Update:
I have this function that used to open a side panel on the right side and color the content:
var els = document.getElementsByClassName('change-color'),
target = document.getElementsByClassName('resources'),
changeColor = function(a) {
elements = document.getElementsByClassName("note");
for (var i = 0; i < elements.length; i++) {
console.log(elements[i])
elements[i].style.backgroundColor = "";
}
target = a.getAttribute('href');
element = document.querySelector('[data-id="' + target.substring(1, target.length) + '"]');
element.style.backgroundColor = a.getAttribute('data-color');
};
for (var i = els.length - 1; i >= 0; --i) {
els[i].onclick = function() {
showFootnotesPanel();
changeColor(this);
}
Now I have 4 side panels that need to respond to the same script, and I thought that by defining something like showPanel() is showFootnotesPanel() or showReferencesPanel() or showImagesPanel() or showInformationPanel() I might simplify things, so the last line of the script would be this instead just:
els[i].onclick = function(){showPanel();changeColor(this);}
Update 2:
Or is it possible to do this with the logical operator OR?
els[i].onclick = function(){showFootnotesPanel(); || showReferencesPanel(); || showImagesPanel(); || showInformationPanel();changeColor(this);}
Update 3:
This is the new script that I am using to hide and show the panels:
function showPanel(myPanel) {
var elem = document.getElementById(myPanel);
if (elem.classList) {
console.log("classList supported");
elem.classList.toggle("show");
} else {
var classes = elem.className;
if (classes.indexOf("show") >= 0) {
elem.className = classes.replace("show", "");
} else {
elem.className = classes + " show";
}
console.log(elem.className);
}
}
function hideOthers(one, two, three, four) {
if (one > "") {
var elem1 = document.getElementById(one);
var classes = elem1.className;
elem1.className = classes.replace("show", "");
}
if (two > "") {
var elem2 = document.getElementById(two);
var classes = elem2.className;
elem2.className = classes.replace("show", "");
}
if (three > "") {
var elem3 = document.getElementById(three);
var classes = elem3.className;
elem3.className = classes.replace("show", "");
}
if (four > "") {
var elem4 = document.getElementById(four);
var classes = elem4.className;
elem4.className = classes.replace("show", "");
}
return;
}
And this is the script that calls the panels and highlights the text on them:
var els = document.getElementsByClassName('change-color'),
target = document.getElementsByClassName('resources'),
changeColor = function(a) {
elements = document.getElementsByClassName("note");
for (var i = 0; i < elements.length; i++) {
console.log(elements[i])
elements[i].style.backgroundColor = "";
}
target = a.getAttribute('href');
element = document.querySelector('[data-id="' + target.substring(1, target.length) + '"]');
element.style.backgroundColor = a.getAttribute('data-color');
};
for (var i = els.length - 1; i >= 0; --i) {
els[i].onclick = function() {
hideOthers('footnotes-section', 'references-section', 'images-section', 'information-section');
showPanel('references-section');
changeColor(this);
}
}
Thank you!
Updated with a final solution.
In javascript you can declare variables by this way:
var text = ""; // String variable.
var number = 0; //Numeric variable.
var boolValue = true; //Boolean variable.
var arrayValue = []; // Array variable. This array can contain objects {}.
var obj = {}; // Object variable.
Check this version of your code.
// var text = ""; => String variable.
// var number = 0; => Numeric variable.
// var boolValue = true; => Boolean variable.
// var arrayValue = []; => Array variable. This array can contain objects {}.
// var obj = {}; => Object variable.
// This section of code is only to explain the first question.
(function() {
function showFootnotesPanel() {
return 10; // Random value.
}
function showReferencesPanel() {
return 30; // Random value.
}
function showImagesPanel() {
return 50; // Random value.
}
function showInformationPanel() {
return 90; // Random value.
}
function showPanel() {
return [
showFootnotesPanel(), // Index = 0
showReferencesPanel(), // Index = 1
showImagesPanel(), // Index = 2
showInformationPanel() // Index = 3
];
}
var bigVariable = showPanel(); // bigVariable is array of numeric values.
// Using logical operator to check status of variable about this demo code.
if (bigVariable[0] === 10 || bigVariable[1] === 30) {
console.log("Hey, with these values can show the FootnotesPanel and ReferencesPanel.");
} else {
console.log("With the current values can't show anything...");
}
console.log(bigVariable);
})();
// https://jsfiddle.net/dannyjhonston/t5e8g22b/
// This section of code attempts to answer the question of this post.
(function() {
// This function can be executed when the page is loaded.
function showPanel(panels) {
var panel, panelVisible = "";
var selPanels = document.getElementById("selPanels");
// In panels array...
for (var i = 0; i < panels.length; i++) {
// panels[0] = "ReferencesPanel";
panel = document.getElementById(panels[i]); // Get in the DOM tag context of the panel to set in the variable "panel".
panelVisible = panel.getAttribute("data-visible"); // HTML5 data attribute.
if (panelVisible == "true") {
panel.setAttribute("class", "show");
} else {
panel.setAttribute("class", "hide");
}
}
}
// This function is for set panel visibilty.
function setPanel(panelId, status) {
panel = document.getElementById(panelId);
panel.setAttribute("data-visible", status);
// Calling the showPanel function to check in the DOM.
showPanel(["ReferencesPanel", "InformationPanel", "ImagesPanel", "FootnotesPanel"]);
}
// Binding the change event to the select tag.
selPanels.addEventListener("change", function() {
// Executes setPanel function with panelId and true to update the data-visible attribute in the DOM.
setPanel(this.options[this.selectedIndex].value, "true");
});
// Executes showPanel function with array argument with panels Id. You need to specify every panel that want to handle.
showPanel(["ReferencesPanel", "InformationPanel", "ImagesPanel", "FootnotesPanel"]);
})();
#global {
border: solid 1px #6291AD;
}
.tools {
background-image: linear-gradient(#FFFFFF, #8999CE);
}
#global div[data-visible] {
height: 80px;
padding: 5px 0;
}
#global div p {
padding: 10px;
}
#ReferencesPanel {
background-image: linear-gradient(#FFFFFF, #FD9A9A);
float: left;
width: 20%;
}
#InformationPanel {
background-image: linear-gradient(#FFFFFF, #A1C7F1);
float: left;
width: 80%;
}
#ImagesPanel {
background-image: linear-gradient(#C6E9FB, #FFF);
width: 100%;
}
#FootnotesPanel {
background-image: linear-gradient(#C6E999, #FFF);
width: 100%;
}
.clear {
clear: both;
}
.show {
display: block;
}
.hide {
display: none;
}
<div id="global">
<div class="tools">Show Panel:
<br />
<!-- Demo -->
<select id="selPanels">
<option value="">[SELECT]</option>
<option value="ReferencesPanel">ReferencesPanel</option>
<option value="InformationPanel">InformationPanel</option>
<option value="ImagesPanel">ImagesPanel</option>
<option value="FootnotesPanel">FootnotesPanel</option>
</select>
</div>
<!-- You need to set data-visible attribute with true or false to show or hide a panel. -->
<div id="ReferencesPanel" data-visible="false">
<p>References Panel</p>
</div>
<div id="InformationPanel" data-visible="false">
<p>Information Panel</p>
</div>
<div class="clear"></div>
<div id="ImagesPanel" data-visible="false">
<p>Images Panel</p>
</div>
<div id="FootnotesPanel" data-visible="false">
<p>Foot notes Panel</p>
</div>
</div>
I dont understand your question exactly, but if you want to define a variable that contains other variables then you can use an object.
e.g:
var footNotesPanel = true;
var referencesPanel = true;
var imagesPanel = true;
var showPanels = {
footNotesPanel: footNotesPanel,
referencesPanel: referencesPanel,
imagesPanel: imagesPanel
}
/*
Option 2 - for showing/hiding side panels
1 ) create all your panels as they would appear, with all the data, but hide them with display:none;
2 ) call show panel function to show a panel.
*/
var showPanel(panel_id) {
var panel_element = $("#" + panel_id); /*panel that you want to show ( is hidden atm but somewhere on the page */
if (!panel_element.length) {
return false; //no panel with this id currently on page
} else {
//check the panel id and do some custom editing if needed, eg.
if (panel_id == "main_side_panel") {
//add some additional classes to body element etc
}
panel_element.show();
//Or Another option that you probably are looking for is below
if (panel_id == "footnotes_panel") {
showFootnotesPanel();
} else if (panel_id == "images_panel") {
showImagesPanel();
}
}
}
// And use it like this:
<div id="footnotes_panel" onclick="showPanel('footnotes_panel')"></div>
// Or simply get the element id from `event.target` and use `showPanel()` without arguments.
I have the following text as a JavaScript string
.mybox {
display: block;
width: 20px;
height: 20px;
background-color: rgb(204, 204, 204);
}
I want to convert to a JavaScript Object
var mybox = {
'display': 'block',
'width': '20px',
'height': '20px';
'background-color': 'rgb(204, 204, 204)';
};
Any ideas or already made scripts?
Year 2017 answer
function parseCSSText(cssText) {
var cssTxt = cssText.replace(/\/\*(.|\s)*?\*\//g, " ").replace(/\s+/g, " ");
var style = {}, [,ruleName,rule] = cssTxt.match(/ ?(.*?) ?{([^}]*)}/)||[,,cssTxt];
var cssToJs = s => s.replace(/\W+\w/g, match => match.slice(-1).toUpperCase());
var properties = rule.split(";").map(o => o.split(":").map(x => x && x.trim()));
for (var [property, value] of properties) style[cssToJs(property)] = value;
return {cssText, ruleName, style};
} /* updated 2017-09-28 */
Description
Passing the following cssText (string) to the function:
.mybox {
display: block;
width: 20px;
height: 20px;
background-color: rgb(204, 204, 204);
}
...will give the following object:
{ cssText: ... /* the original string including new lines, tabs and spaces */,
ruleName: ".mybox",
style: {
"": undefined,
display: "block",
width: "20px",
height: "20px",
backgroundColor: "rgb(204, 204, 204)"
}
}
User can also pass a cssText such as:
display: block; width: 20px; height: 20px; background-color: rgb(204, 204, 204);
Features:
Works both with CSSRule.cssText and
CSSStyleDeclaration.cssText.
Converts CSS property names
(background-color) to JS property names (backgroundColor).
It handles even very erratic names,
such as back%gr- -ound---color: red; (converts to backGrOundColor).
Enables mass modification of existing
CSSStyleDeclarations (such as document.body.style) using
a single call
Object.assign(document.body.style, parseCSSText(cssText).style).
Does not fail when a property name comes without a value (an entry
without a colon) nor even vice versa.
Update 2017-09-28: Handles new lines also in rule names,
collapses white spaces.
Update 2017-09-28: Handles comments (/*...*/).
Quirks:
If the last CSS declaration in the rule ends with a semicolon,
returned style will include a property with an empty name "" and
an undefined value reflecting the null string following the
semicolon. I consider it a correct behaviour.
The function will return a faulty result if property value (string literal)
includes colon or semicolon or CSS comments, for example
div::before {content: 'test:test2;/*test3*/';}. I don’t know
how to avoid this.
At the moment, it converts property names with prefixes
such as -somebrowser-someproperty incorrectly to SomebrowserSomeproperty
instead of somebrowserSomeproperty. I want a remedy that won’t ruin
the brevity of code, therefore I’ll take time to find one.
Live example
function parseCSSText(cssText) {
var cssTxt = cssText.replace(/\/\*(.|\s)*?\*\//g, " ").replace(/\s+/g, " ");
var style = {}, [,ruleName,rule] = cssTxt.match(/ ?(.*?) ?{([^}]*)}/)||[,,cssTxt];
var cssToJs = s => s.replace(/\W+\w/g, match => match.slice(-1).toUpperCase());
var properties = rule.split(";").map(o => o.split(":").map(x => x && x.trim()));
for (var [property, value] of properties) style[cssToJs(property)] = value;
return {cssText, ruleName, style};
} /* updated 2017-09-28 */
Example:
var sty = document.getElementById("mystyle");
var out = document.getElementById("outcome");
var styRule = parseCSSText(sty.innerHTML);
var outRule = parseCSSText(out.style.cssText);
out.innerHTML =
"<b>⦁ CSS in #mystyle</b>: " + JSON.stringify(styRule) + "<br>" +
"<b>⦁ CSS of #outcome</b>: " + JSON.stringify(outRule);
console.log(styRule, outRule); /* Inspect result in the console. */
<style id="mystyle">
.mybox1, /* a comment
and new lines
to step up the game */
.mybox
{
display: block;
width: 20px; height: 20px;
background-color: /* a comment
and a new line */
rgb(204, 204, 204);
-somebrowser-someproperty: somevalue;
}
</style>
<div id="outcome" style="
display: block; padding: 0.5em;
background-color: rgb(144, 224, 224);
">...</div>
<b style="color: red;">Also inspect the browser console.</b>
This is the beginning of a parser that may do what you want. Of course it needs work, especially if you want to handle any generic css that may be provided. This assumes that input css is written as you provided, with the first row being the name of the property, the last row being a '}' and so on.
If you don't want to handle only basic properties, writing a complex parser is not an easy task. For example, what if you declare something like:
input[type="text"],
table > tr:nth-child(2),
#link a:hover {
-webkit-transition: width 2s; /* Safari and Chrome */
}
This is valid css, but how would you extract a valid javascript variable name from it? How to convert -webkit-transition into a meaningful property name? The whole task smells like you're doing it all wrong. Instead of working on a parser, I'd work on a more stable solution at all.
By the way, here is the code you may start from:
var s = '.mybox {\n';
s += 'display: block;\n';
s += 'width: 20px;\n';
s += 'height: 20px;\n';
s += 'background-color: rgb(204, 204, 204);\n';
s += '}\n';
// split css by line
var css_rows = s.split('\n');
// filter out empty elements and strip ';'
css_rows = css_rows.filter(function(x){ return x != '' }).map(function(x){ return x.trim().replace(';', '') });
// create object
var json_name = css_rows[0].trim().replace(/[\.\{\ \#]/g, '');
eval('var ' + json_name + ' = {};');
// remove first and last element
css_rows = css_rows.splice(1, css_rows.length-2)
for (elem in css_rows)
{
var elem_parts = css_rows[elem].split(':');
var property_name = elem_parts[0].trim().replace('-', '');
var property_value = elem_parts[1].trim();
eval(json_name + '.' + property_name + ' = "' + property_value + '";');
}
If the CSS document is included in the html document, so that the style declarations are actually loaded, you can step through all styles in Javascript like this:
// Get all style sheet documents in this html document
var allSheets = document.styleSheets;
for (var i = 0; i < allSheets.length; ++i) {
var sheet = allSheets[i];
// Get all CSS rules in the current style sheet document
var rules = sheet.cssRules || sheet.rules;
for (var j = 0; j < rules.length; ++j) {
var rule = rules[j];
// Get the selector definition ("div > p:first-child" for example)
var selector = rule.selectorText;
// Create an empty object to put the style definitions in
var result = {};
var style = rule.style;
for (var key in style) {
if (style.hasOwnProperty(key)) {
result[key] = style.cssText;
}
}
// At this point, you have the selector in the
// selector variable (".mybox" for example)
// You also have a javascript object in the
// result variable, containing what you need.
// If you need to output this as json, there
// are several options for this.
}
}
If this is not what you want, like if you want to parse a CSS document and create a JavaScript source file, you need to look into lexical parsers, CSS document object models, JSON serialization, and stuff like that...
I have the same issue working with LeafLet trying to separate CSS styles from JavaScript code... I end up with this:
var css = {};
for (var i = 0; i < document.styleSheets.length; ++i) {
var sheet = document.styleSheets[i];
for (var j = 0; j < sheet.cssRules.length; ++j) {
var rule = sheet.cssRules[j];
var cssText = rule.cssText.slice(rule.cssText.indexOf('{')+1);
var attrs = cssText.split(';');
var ruleSet = {};
for (var k = 0; k < attrs.length; ++k) {
var keyValue = attrs[k].split(':');
if (keyValue.length == 2) {
var key = keyValue[0].trim();
var value = keyValue[1].trim();
ruleSet[key] = value;
}
}
for (var testRule in ruleSet) { // We are going to add the rule iff it is not an empty object
css[rule.selectorText] = ruleSet;
break;
}
}
}
console.log(css);
This will produce something like this:
I know that $("#divId").html() will give me innerHtml. I also need its styles (which might be defined by the means of classes) either in-line style attribute or all the styles/classes within a separate <style> tag.
Is it possible?
UPDATE
What if html is like <div id="testDiv">cfwcvb</div> and a css class for #testDiv is defined in external stylesheet?
UPDATE 2
Sorry for not clarifying this earlier
If this is my HTML
<div id="divId">
<span class="someClass">Some innerText</span>
</div>
And styles are defined in separate style sheet or in head styles.
#divId {
clear: both;
padding: 3px;
border: 2px dotted #CCC;
font-size: 107%;
line-height: 130%;
width: 660px;
}
.someClass {
color: blue;
}
Then when I try to get inner html of $("#divId").html() or call any other custom function, I need something like below
<style>
#divId {
clear: both;
padding: 3px;
border: 2px dotted #CCC;
font-size: 107%;
line-height: 130%;
width: 660px;
}
.someClass {
color: blue;
}
</style>
<div id="divId">
<span class="someClass">Some innerText</span>
</div>
UPDATE 3 for Answer by kirilloid
I ran below code in Command Window of Chrome Debugger tools for this page itself and this is what I see TypeError: Cannot read property 'rules' of undefined
function getElementChildrenAndStyles(selector) {
var html = $(selector).get(0).outerHTML;
selector = selector.split(",").map(function(subselector){
return subselector + "," + subselector + " *";
}).join(",");
elts = $(selector);
var rulesUsed = [];
// main part: walking through all declared style rules
// and checking, whether it is applied to some element
sheets = document.styleSheets;
for(var c = 0; c < sheets.length; c++) {
var rules = sheets[i].rules || sheets[i].cssRules;
for(var r = 0; r < rules.length; r++) {
var selectorText = rules[r].selectorText;
var matchedElts = $(selectorText);
for (var i = 0; i < elts.length; i++) {
if (matchedElts.index(elts[i]) != -1) {
rulesUsed.push(CSSrule); break;
}
}
}
}
var style = rulesUsed.map(function(cssRule){
if ($.browser.msie) {
var cssText = cssRule.style.cssText.toLowerCase();
} else {
var cssText = cssRule.cssText;
}
// some beautifying of css
return cssText.replace(/(\{|;)\s+/g, "\$1\n ").replace(/\A\s+}/, "}");
// set indent for css here ^
}).join("\n");
return "<style>\n" + style + "\n</style>\n\n" + html;
};
getElementChildrenAndStyles(".post-text:first");
outerHTML (not sure, you need it — just in case)
Limitations: CSSOM is used and stylesheets should be from the same origin.
function getElementChildrenAndStyles(selector) {
var html = $(selector).outerHTML();
selector = selector.split(",").map(function(subselector){
return subselector + "," + subselector + " *";
}).join(",");
elts = $(selector);
var rulesUsed = [];
// main part: walking through all declared style rules
// and checking, whether it is applied to some element
sheets = document.styleSheets;
for(var c = 0; c < sheets.length; c++) {
var rules = sheets[c].rules || sheets[c].cssRules;
for(var r = 0; r < rules.length; r++) {
var selectorText = rules[r].selectorText;
var matchedElts = $(selectorText);
for (var i = 0; i < elts.length; i++) {
if (matchedElts.index(elts[i]) != -1) {
rulesUsed.push(rules[r]); break;
}
}
}
}
var style = rulesUsed.map(function(cssRule){
if (cssRule.style) {
var cssText = cssRule.style.cssText.toLowerCase();
} else {
var cssText = cssRule.cssText;
}
// some beautifying of css
return cssText.replace(/(\{|;)\s+/g, "\$1\n ").replace(/\A\s+}/, "}");
// set indent for css here ^
}).join("\n");
return "<style>\n" + style + "\n</style>\n\n" + html;
}
usage:
getElementChildrenAndStyles("#divId");
No jQuery and no IE support, that's all I can do:
<!doctype html>
<html>
<head>
<meta charset = "utf-8">
<script type = "text/javascript">
Element.prototype.getStyles = function () {
var array = {};
var styles = window.getComputedStyle (this, null);
for (var i = 0; i < styles.length; i ++) {
var style = styles[i];
array[style] = styles[style];
}
return array; // return new Array (array, this.innerHTML); You can also return the HTMl content. I don't think its necessary
}
window.addEventListener ("load", function () {
var divId = document.getElementById ("divId");
var someClass = document.getElementsByClassName ("someClass");
var string = "";
var styles = divId.getStyles ();
for (var i in styles) {
string += i + ": " + styles[i] + "\n";
}
alert (string);
alert ("In-line style: Height ->" + styles["height"] + "\n" + "Out-line style: Width ->" + styles["width"])
alert ("HTML: " + divId.innerHTML);
// Same thing with the span element
}, false);
</script>
<style>
#divId {
clear: both;
padding: 3px;
border: 2px dotted #CCC;
font-size: 107%;
line-height: 130%;
width: 660px;
}
.someClass {
color: blue;
}
</style>
<title>Test</title>
</head>
<body>
<div id = "divId" style = "height: 100px">
<span class = "someClass">Some innerText</span>
</div>
</body>
</html>
You can get hold of a style object representing the computed style for an element using window.getComputedStyle() in most browsers and the element's currentStyle property in IE. There are several browser differences, however, with values returned for shortcut properties (such as background), color RGB values, lengths and even font-weight (see this useful test page). Test carefully.
function computedStyle(el) {
return el.currentStyle || window.getComputedStyle(el, null);
}
alert(computedStyle(document.body).color);
You can use something like this for script:-
<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.js"></script>
<script type="text/javascript">
$(function(){
var styleVal = $('#testDiv').attr('style');
console.warn("styleVal >>> " + styleVal);
})
</script>
and simple html would be like this
<div style="border:1px solid red;" id="testDiv">cfwcvb</div>
if you want to save all of the style of an element i think this will be more complicated as you think
first of all my first ide was the firebug css console. this shows all fo the style of an element and i thought how?
so i searched for the source code of the firebug and i found this:
http://fbug.googlecode.com/svn/branches/firebug1.7/content/firebug/css.js
this code working only on the css part.
const styleGroups =
{
text: [
"font-family",
"font-size",
"font-weight",
"font-style",
"color",
"text-transform",
"text-decoration",
"letter-spacing",
"word-spacing",
"line-height",
"text-align",
"vertical-align",
"direction",
"column-count",
"column-gap",
"column-width"
],
background: [
"background-color",
"background-image",
"background-repeat",
"background-position",
"background-attachment",
"opacity"
],
box: [
"width",
"height",
"top",
"right",
"bottom",
"left",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"border-top-width",
"border-right-width",
"border-bottom-width",
"border-left-width",
"border-top-color",
"border-right-color",
"border-bottom-color",
"border-left-color",
"border-top-style",
"border-right-style",
"border-bottom-style",
"border-left-style",
"-moz-border-top-radius",
"-moz-border-right-radius",
"-moz-border-bottom-radius",
"-moz-border-left-radius",
"outline-top-width",
"outline-right-width",
"outline-bottom-width",
"outline-left-width",
"outline-top-color",
"outline-right-color",
"outline-bottom-color",
"outline-left-color",
"outline-top-style",
"outline-right-style",
"outline-bottom-style",
"outline-left-style"
],
layout: [
"position",
"display",
"visibility",
"z-index",
"overflow-x", // http://www.w3.org/TR/2002/WD-css3-box-20021024/#overflow
"overflow-y",
"overflow-clip",
"white-space",
"clip",
"float",
"clear",
"-moz-box-sizing"
],
other: [
"cursor",
"list-style-image",
"list-style-position",
"list-style-type",
"marker-offset",
"user-focus",
"user-select",
"user-modify",
"user-input"
]
};
the function which gets all of the styles.
updateComputedView: function(element)
{
var win = element.ownerDocument.defaultView;
var style = win.getComputedStyle(element, "");
var groups = [];
for (var groupName in styleGroups)
{
var title = $STR("StyleGroup-" + groupName);
var group = {title: title, props: []};
groups.push(group);
var props = styleGroups[groupName];
for (var i = 0; i < props.length; ++i)
{
var propName = props[i];
var propValue = stripUnits(rgbToHex(style.getPropertyValue(propName)));
if (propValue)
group.props.push({name: propName, value: propValue});
}
}
var result = this.template.computedTag.replace({groups: groups}, this.panelNode);
dispatch(this.fbListeners, 'onCSSRulesAdded', [this, result]);
}
function stripUnits(value)
{
// remove units from '0px', '0em' etc. leave non-zero units in-tact.
return value.replace(/(url\(.*?\)|[^0]\S*\s*)|0(%|em|ex|px|in|cm|mm|pt|pc)(\s|$)/gi, function(_, skip, remove, whitespace) {
return skip || ('0' + whitespace);
});
}
in this code i figured out that the
win.getComputedStyle(element, "")
to get all of the styles of an element, and then with a for loop gets all of the style and prints out. so i think the getComputedSTyle is the main function to use, and after this you can get the props one by one with:
style.getPropertyValue(propName)
Based on kirilloid's answer, I've created a developer tools extension for Chrome that incorporates that code for capturing styles and markup for a page fragment. The extension is in the Chrome Web Store and is on Github. All of the "Author Styles" output options use that method for iterating over the stylesheets.
The .css() method gets a particular style of the element... I don't know if you can retrieve all styles:
http://api.jquery.com/css/
Generally you can access style parameter using .attr('style'). If you want to access computed style you can use window.getComputedStyle(element) in Opera, Firefox, Chrome and other sane browsers. For IE you'd do the same with element.currentStyle.
Also if you wish to access individual CSS style you can do so with jQuery .css method. Like so $("#divId").css('font-size').
You can get the stylesheet defined inside style tags under document.styleSheets. You can read the rules into a map, and find them by selectorText. So by id: "#id", by classes: ".className". By safari or chrome you can use getMatchedCSSRules.