Toggle style properties with JavaScript by commenting-out CSS attributes - javascript

I want to send a list of properties to a function that will temporarily disable (via comments) those properties. The purpose is similar to what is accomplished by Chrome Dev Tools when inspecting the style of an element, except that the user will not check/uncheck boxes, instead, the action will be performed by JavaScript.
function disableProperties(el, properties) {
var property; // property value pair
var cssStr = el.style.cssText.replace(/; /g, ";");
for (let i = 0; i < properties.length; i++) {
property = properties[i] + ": " + eval("el.style." + properties[i]) + ";";
cssStr = cssStr.replace(property, "/* " + property + " */");
}
el.setAttribute("style", cssStr);
}
function restoreProperties(el, properties) {
var outHtml = el.outerHTML;
var key = 'style="';
var idx1 = outHtml.indexOf(key) + key.length;
var idx2 = outHtml.indexOf('"', idx1);
var style = outHtml.substring(idx1, idx2);
for (let i = 0; i < properties.length; i++) {
str = "/* " + properties[i];
let a = style.indexOf(str);
let b = style.indexOf(" */", a + a.length) + 3;
let comment = style.substring(a, b);
let property = (comment).replace("/*", "").replace("*/", "");
style = style.replace(comment, property);
}
el.style.cssText = style;
}
When elements are created, their style is embedded in the HTML instead of external CSS files. It is important that the properties are only temporarily disabled; their earlier values must be preserved.
UPDATE:
I thought my comment code was not working because I was trying to retrieve the comment by asking for el.style.cssText; however, the comment was only available through outerHTML.
The code below is functional, but I'm sure that could be cleaned up. If you have suggestions on how I can improve this code, please advise. Here is a fiddle to demonstrate one example of how I am using the code.

Expanding on Volts deleted answer, use data attributes to store the initial state of your element. You can then that information to reset the values. One catch is that the style property is read only, so if we want to reset all style properties, we have to loop all the properties.
This is much cleaner than hacking comments in and out. This works with styles set inline and with classes.
const disableProperties = (el, properties) => {
//store the style so we can restore it
el.dataset.styles = JSON.stringify(el.style);
console.log(JSON.parse(el.dataset.styles));
properties.forEach(property => {
el.style[property] = "unset";
})
}
const enableProperties = (el, properties) => {
//Get our saved initial state
let styles = JSON.parse(el.dataset.styles);
console.log(styles);
properties.forEach(property => {
el.style[property] = styles[property];
})
}
const restoreAll = (el) => {
let styles = JSON.parse(el.dataset.styles);
console.log(styles);
/*You might think the following line should work, but the styles property is read only*/
/*el.style = styles;*/
/*So loop our properties instead*/
for(prop in styles){
if(styles.hasOwnProperty(prop)){
el.style[prop] = styles[prop];
}
}
}
document.querySelector('.child').addEventListener("click", function() {
disableProperties(this.parentNode, ["top", "left"])
})
let enableButtons = document.querySelectorAll(".enable");
for (var i = 0; i < enableButtons.length; i++) {
enableButtons[i].addEventListener("click", function(event) {
let property = this.dataset.restore;
let target = document.querySelector(this.dataset.target);
enableProperties(target, property.split(","));
});
}
document.getElementById("restoreAll").addEventListener("click", function() {
restoreAll(document.querySelector(".parent"));
});
.parent {
/*top: 50px;*/
left: 50px;
position: absolute;
}
.child {
background: red;
width: 50px;
height: 50px;
}
.container {position:relative; height:200px;}
Click the square before restoring
<div class="container">
<div class="parent" style="top:50px;">
<div class="child">
</div>
</div>
</div>
<button id="restoreLeft" class="enable" data-restore="left" data-target=".parent">Restore Left</button>
<button id="restoreTop" class="enable" data-restore="top" data-target=".parent">Restore top</button>
<button id="restoreAll">RestoreAll</button>

Related

Can't Inject Variable into Style Margins with Javascript

Fairly easy solution to this problem, I'm pretty sure, but I'm still currently unable to find where the problem may be (probably some syntax).
I'm trying to create a simple JS exercise to move an object's position to a random place after each hover/mouseover. (DOM manipulations fundamentals).
Here is the code:
let arr = [".top", ".left"];
let logTest = []
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('mouseover', (e) => {
for (let i = 0; i < 2; i++) {
var num = Math.floor((Math.random() * 100) + 1);
e.target.style[arr[i]] = num + "px";
logTest[i] = num + "px";
}
console.log(logTest[0] + ", " + logTest[1]);
});
});
Since the numbers are being generated and printed correctly to the console, I'm fairly sure that the problem has to be in line 9, where e.target.style[.left and .top] is not being able to be assigned to the random numbers.
let arr = [".top", ".left"];
Don't use '.' dots for specifying the style. Directly use the style property name. Use this:
let arr = ["top", "left"];
And make sure to set position as relative, absolute or fixed for the div elements.
Here's a working example made using your script:
<style>
.t {
height: 50px;
width: 50px;
background: green;
margin: 10px;
position: relative;
}
</style>
<h1>Hello</h1>
<div class="t">e</div>
<div class="t">f</div>
<script>
let arr = ["top", "left"];
let logTest = []
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('mouseover', (e) => {
for (let i = 0; i < 2; i++) {
var num = Math.floor((Math.random() * 100) + 1);
e.target.style[arr[i]] = num;
logTest[i] = num;
}
console.log(logTest[0] + ", " + logTest[1]);
});
});
</script>

Get CSS class definition from chrome developer tools console

I would like to programatically retrieve a set of CSS class definitions from chrome developer tools. In effect similar to what is displayed in styles tab in the right hand side. The input needs to be a class name and the output should be all the styles defined in it.
I'm aware of getComputedStyle DOM method, but this doesn't separate into separate classes which I need.
This approach worked for me (stackoverflow.com/a/27527462/1023562):
/**
* Gets styles by a classname
*
* #notice The className must be 1:1 the same as in the CSS
* #param string className_
*/
function getStyle(className_) {
var styleSheets = window.document.styleSheets;
var styleSheetsLength = styleSheets.length;
for(var i = 0; i < styleSheetsLength; i++){
var classes = styleSheets[i].rules || styleSheets[i].cssRules;
var classesLength = classes.length;
for (var x = 0; x < classesLength; x++) {
if (classes[x].selectorText == className_) {
var ret;
if(classes[x].cssText){
ret = classes[x].cssText;
} else {
ret = classes[x].style.cssText;
}
if(ret.indexOf(classes[x].selectorText) == -1){
ret = classes[x].selectorText + "{" + ret + "}";
}
return ret;
}
}
}
}
It lets you invoke the javascript code in Chrome console like this:
console.log(getStyle('#heder_logo a'));
and get results like this:
> #heder_logo a { width: 200px; height: 114px; display: block; }.
I did have issues with some CSS files which were not on the same domain (they were pulled from CDN), but there are variety of proposals in that thread, so some should work for you.
Have adapted Ivan's answer in order to get a more complete result. This method will also return styles where the class is part for the selector
//Get all styles where the provided class is involved
//Input parameters should be css selector such as .myClass or #m
//returned as an array of tuples {selectorText:"", styleDefinition:""}
function getStyleWithCSSSelector(cssSelector) {
var styleSheets = window.document.styleSheets;
var styleSheetsLength = styleSheets.length;
var arStylesWithCSSSelector = [];
//in order to not find class which has the current name as prefix
var arValidCharsAfterCssSelector = [" ", ".", ",", "#",">","+",":","["];
//loop through all the stylessheets in the bor
for(var i = 0; i < styleSheetsLength; i++){
var classes = styleSheets[i].rules || styleSheets[i].cssRules;
var classesLength = classes.length;
for (var x = 0; x < classesLength; x++) {
//check for any reference to the class in the selector string
if(typeof classes[x].selectorText != "undefined"){
var matchClass = false;
if(classes[x].selectorText === cssSelector){//exact match
matchClass=true;
}else {//check for it as part of the selector string
//TODO: Optimize with regexp
for (var j=0;j<arValidCharsAfterCssSelector.length; j++){
var cssSelectorWithNextChar = cssSelector+ arValidCharsAfterCssSelector[j];
if(classes[x].selectorText.indexOf(cssSelectorWithNextChar)!=-1){
matchClass=true;
//break out of for-loop
break;
}
}
}
if(matchClass === true){
//console.log("Found "+ cssSelectorWithNextChar + " in css class definition " + classes[x].selectorText);
var styleDefinition;
if(classes[x].cssText){
styleDefinition = classes[x].cssText;
} else {
styleDefinition = classes[x].style.cssText;
}
if(styleDefinition.indexOf(classes[x].selectorText) == -1){
styleDefinition = classes[x].selectorText + "{" + styleDefinition + "}";
}
arStylesWithCSSSelector.push({"selectorText":classes[x].selectorText, "styleDefinition":styleDefinition});
}
}
}
}
if(arStylesWithCSSSelector.length==0) {
return null;
}else {
return arStylesWithCSSSelector;
}
}
In addition, I've made a function which collects the css style definitions to the sub-tree of a root node your provide (through a jquery selector).
function getAllCSSClassDefinitionsForSubtree(selectorOfRootElement){
//stack in which elements are pushed and poped from
var arStackElements = [];
//dictionary for checking already added css class definitions
var existingClassDefinitions = {}
//use jquery for selecting root element
var rootElement = $(selectorOfRootElement)[0];
//string with the complete CSS output
var cssString = "";
console.log("Fetching all classes used in sub tree of " +selectorOfRootElement);
arStackElements.push(rootElement);
var currentElement;
while(currentElement = arStackElements.pop()){
currentElement = $(currentElement);
console.log("Processing element " + currentElement.attr("id"));
//Look at class attribute of element
var classesString = currentElement.attr("class");
if(typeof classesString != 'undefined'){
var arClasses = classesString.split(" ");
//for each class in the current element
for(var i=0; i< arClasses.length; i++){
//fetch the CSS Styles for a single class. Need to append the . char to indicate its a class
var arStylesWithCSSSelector = getStyleWithCSSSelector("."+arClasses[i]);
console.log("Processing class "+ arClasses[i]);
if(arStylesWithCSSSelector != null){
//console.log("Found "+ arStylesWithCSSSelector.length + " CSS style definitions for class " +arClasses[i]);
//append all found styles to the cssString
for(var j=0; j< arStylesWithCSSSelector.length; j++){
var tupleStyleWithCSSSelector = arStylesWithCSSSelector[j];
//check if it has already been added
if(typeof existingClassDefinitions[tupleStyleWithCSSSelector.selectorText] === "undefined"){
//console.log("Adding " + tupleStyleWithCSSSelector.styleDefinition);
cssString+= tupleStyleWithCSSSelector.styleDefinition;
existingClassDefinitions[tupleStyleWithCSSSelector.selectorText] = true;
}else {
//console.log("Already added " + tupleStyleWithCSSSelector.styleDefinition);
}
}
}
}
}
//push all child elments to stack
if(currentElement.children().length>0){
arStackElements= arStackElements.concat(currentElement.children().toArray());
}
}
console.log("Found " + Object.keys(existingClassDefinitions).length + " CSS class definitions");
return cssString;
}
Note that if a class is defined several times with the same selector, the above function will only pick up the first.
Python script to search css file for a word once found read what is inbetween the curly braces. Quick and dirty way**

Why aren't javascript and absolute positioning working together properly? [duplicate]

I know I can set a CSS value through JavaScript such as:
document.getElementById('image_1').style.top = '100px';
But, can I get a current specific style value? I've read where I can get the entire style for the element, but I don't want to have to parse the whole string if I don't have to.
You can use getComputedStyle().
var element = document.getElementById('image_1'),
style = window.getComputedStyle(element),
top = style.getPropertyValue('top');
console.log(top);
<img id="image_1">
jsFiddle.
The element.style property lets you know only the CSS properties that were defined as inline in that element (programmatically, or defined in the style attribute of the element), you should get the computed style.
Is not so easy to do it in a cross-browser way, IE has its own way, through the element.currentStyle property, and the DOM Level 2 standard way, implemented by other browsers is through the document.defaultView.getComputedStyle method.
The two ways have differences, for example, the IE element.currentStyle property expect that you access the CSS property names composed of two or more words in camelCase (e.g. maxHeight, fontSize, backgroundColor, etc), the standard way expects the properties with the words separated with dashes (e.g. max-height, font-size, background-color, etc).
......
function getStyle(el, styleProp) {
var value, defaultView = (el.ownerDocument || document).defaultView;
// W3C standard way:
if (defaultView && defaultView.getComputedStyle) {
// sanitize property name to css notation
// (hyphen separated words eg. font-Size)
styleProp = styleProp.replace(/([A-Z])/g, "-$1").toLowerCase();
return defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
} else if (el.currentStyle) { // IE
// sanitize property name to camelCase
styleProp = styleProp.replace(/\-(\w)/g, function(str, letter) {
return letter.toUpperCase();
});
value = el.currentStyle[styleProp];
// convert other units to pixels on IE
if (/^\d+(em|pt|%|ex)?$/i.test(value)) {
return (function(value) {
var oldLeft = el.style.left, oldRsLeft = el.runtimeStyle.left;
el.runtimeStyle.left = el.currentStyle.left;
el.style.left = value || 0;
value = el.style.pixelLeft + "px";
el.style.left = oldLeft;
el.runtimeStyle.left = oldRsLeft;
return value;
})(value);
}
return value;
}
}
Main reference stackoverflow
Use the following. It helped me.
document.getElementById('image_1').offsetTop
See also Get Styles.
Cross-browser solution to checking CSS values without DOM manipulation:
function get_style_rule_value(selector, style)
{
for (var i = 0; i < document.styleSheets.length; i++)
{
var mysheet = document.styleSheets[i];
var myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
for (var j = 0; j < myrules.length; j++)
{
if (myrules[j].selectorText && myrules[j].selectorText.toLowerCase() === selector)
{
return myrules[j].style[style];
}
}
}
};
Usage:
get_style_rule_value('.chart-color', 'backgroundColor')
Sanitized version (forces selector input to lowercase, and allows for use case without leading ".")
function get_style_rule_value(selector, style)
{
var selector_compare=selector.toLowerCase();
var selector_compare2= selector_compare.substr(0,1)==='.' ? selector_compare.substr(1) : '.'+selector_compare;
for (var i = 0; i < document.styleSheets.length; i++)
{
var mysheet = document.styleSheets[i];
var myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
for (var j = 0; j < myrules.length; j++)
{
if (myrules[j].selectorText)
{
var check = myrules[j].selectorText.toLowerCase();
switch (check)
{
case selector_compare :
case selector_compare2 : return myrules[j].style[style];
}
}
}
}
}
If you set it programmatically you can just call it like a variable (i.e. document.getElementById('image_1').style.top). Otherwise, you can always use jQuery:
<html>
<body>
<div id="test" style="height: 100px;">Test</div>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
alert($("#test").css("height"));
</script>
</body>
</html>
In 2021
check before use
You can use computedStyleMap()
The answer is valid but sometimes you need to check what unit it returns, you can get that without any slice() or substring() string.
var element = document.querySelector('.js-header-rep');
element.computedStyleMap().get('padding-left');
var element = document.querySelector('.jsCSS');
var con = element.computedStyleMap().get('padding-left');
console.log(con);
.jsCSS {
width: 10rem;
height: 10rem;
background-color: skyblue;
padding-left: 10px;
}
<div class="jsCSS"></div>
As a matter of safety, you may wish to check that the element exists before you attempt to read from it. If it doesn't exist, your code will throw an exception, which will stop execution on the rest of your JavaScript and potentially display an error message to the user -- not good. You want to be able to fail gracefully.
var height, width, top, margin, item;
item = document.getElementById( "image_1" );
if( item ) {
height = item.style.height;
width = item.style.width;
top = item.style.top;
margin = item.style.margin;
} else {
// Fail gracefully here
}
The cross-browser solution without DOM manipulation given above does not work because it gives the first matching rule, not the last. The last matching rule is the one which applies. Here is a working version:
function getStyleRuleValue(style, selector) {
let value = null;
for (let i = 0; i < document.styleSheets.length; i++) {
const mysheet = document.styleSheets[i];
const myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
for (let j = 0; j < myrules.length; j++) {
if (myrules[j].selectorText &&
myrules[j].selectorText.toLowerCase() === selector) {
value = myrules[j].style[style];
}
}
}
return value;
}
However, this simple search will not work in case of complex selectors.

How to get the complete css information if css class is given form the used style sheets files

I want to find the complete css information of applied class in details by clicking on it. Like
<div class="class1 class2">This is my div</div>
Suppose I've written css in xyz.css file
.class1{
background-color:#999999;
width:auto;
overflow:hidden;
}
.class2{
background-color:#856241;
width:500px;
overflow:hidden;
}
After clicking on the div I want to display all the css information which is applied to the div.
jQuery solution will be preferred.
As per first few answers I'm getting all the computed style but I'm interested in my only applied class style details.
You can get the current style of an element with this:
var elem = ...;
var css = document.defaultView ? document.defaultView.getComputedStyle(elem, null) : elem.currentStyle;
if ('length' in css) {
// iterate over all property names
for (var i = 0, l = css.length; i < l; ++i) {
var propertyName = css[i];
// get property value
var value = css[propertyName];
if (value != void 0) {
console.log(propertyName+': '+value);
}
}
} else {
// IE
for (var propertyName in css) {
// get property value
var value = css[propertyName];
if (value != void 0) {
console.log(propertyName+': '+value);
}
}
}
Try this here: http://jsfiddle.net/P5WYC/2/
You may also whant to try with document.styleSheets ( see http://www.quirksmode.org/dom/w3c_css.html )
You could do:
<div class="class1 class2" id='myId'>This is my div</div>
elem = document.getElementById('myId');
var cssRules = (getComputedStyle || currentStyle)(elem);
var properties = {};
for (var i = 0, l = cssRules.length; i < l; ++i) {
var propertyName = cssRules[i];
var value = cssRules.getPropertyValue(propertyName);
properties[propertyName] = value;
}
And properties is an object that has all the css properties of your div

Get a CSS value with JavaScript

I know I can set a CSS value through JavaScript such as:
document.getElementById('image_1').style.top = '100px';
But, can I get a current specific style value? I've read where I can get the entire style for the element, but I don't want to have to parse the whole string if I don't have to.
You can use getComputedStyle().
var element = document.getElementById('image_1'),
style = window.getComputedStyle(element),
top = style.getPropertyValue('top');
console.log(top);
<img id="image_1">
jsFiddle.
The element.style property lets you know only the CSS properties that were defined as inline in that element (programmatically, or defined in the style attribute of the element), you should get the computed style.
Is not so easy to do it in a cross-browser way, IE has its own way, through the element.currentStyle property, and the DOM Level 2 standard way, implemented by other browsers is through the document.defaultView.getComputedStyle method.
The two ways have differences, for example, the IE element.currentStyle property expect that you access the CSS property names composed of two or more words in camelCase (e.g. maxHeight, fontSize, backgroundColor, etc), the standard way expects the properties with the words separated with dashes (e.g. max-height, font-size, background-color, etc).
......
function getStyle(el, styleProp) {
var value, defaultView = (el.ownerDocument || document).defaultView;
// W3C standard way:
if (defaultView && defaultView.getComputedStyle) {
// sanitize property name to css notation
// (hyphen separated words eg. font-Size)
styleProp = styleProp.replace(/([A-Z])/g, "-$1").toLowerCase();
return defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
} else if (el.currentStyle) { // IE
// sanitize property name to camelCase
styleProp = styleProp.replace(/\-(\w)/g, function(str, letter) {
return letter.toUpperCase();
});
value = el.currentStyle[styleProp];
// convert other units to pixels on IE
if (/^\d+(em|pt|%|ex)?$/i.test(value)) {
return (function(value) {
var oldLeft = el.style.left, oldRsLeft = el.runtimeStyle.left;
el.runtimeStyle.left = el.currentStyle.left;
el.style.left = value || 0;
value = el.style.pixelLeft + "px";
el.style.left = oldLeft;
el.runtimeStyle.left = oldRsLeft;
return value;
})(value);
}
return value;
}
}
Main reference stackoverflow
Use the following. It helped me.
document.getElementById('image_1').offsetTop
See also Get Styles.
Cross-browser solution to checking CSS values without DOM manipulation:
function get_style_rule_value(selector, style)
{
for (var i = 0; i < document.styleSheets.length; i++)
{
var mysheet = document.styleSheets[i];
var myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
for (var j = 0; j < myrules.length; j++)
{
if (myrules[j].selectorText && myrules[j].selectorText.toLowerCase() === selector)
{
return myrules[j].style[style];
}
}
}
};
Usage:
get_style_rule_value('.chart-color', 'backgroundColor')
Sanitized version (forces selector input to lowercase, and allows for use case without leading ".")
function get_style_rule_value(selector, style)
{
var selector_compare=selector.toLowerCase();
var selector_compare2= selector_compare.substr(0,1)==='.' ? selector_compare.substr(1) : '.'+selector_compare;
for (var i = 0; i < document.styleSheets.length; i++)
{
var mysheet = document.styleSheets[i];
var myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
for (var j = 0; j < myrules.length; j++)
{
if (myrules[j].selectorText)
{
var check = myrules[j].selectorText.toLowerCase();
switch (check)
{
case selector_compare :
case selector_compare2 : return myrules[j].style[style];
}
}
}
}
}
If you set it programmatically you can just call it like a variable (i.e. document.getElementById('image_1').style.top). Otherwise, you can always use jQuery:
<html>
<body>
<div id="test" style="height: 100px;">Test</div>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
alert($("#test").css("height"));
</script>
</body>
</html>
In 2021
check before use
You can use computedStyleMap()
The answer is valid but sometimes you need to check what unit it returns, you can get that without any slice() or substring() string.
var element = document.querySelector('.js-header-rep');
element.computedStyleMap().get('padding-left');
var element = document.querySelector('.jsCSS');
var con = element.computedStyleMap().get('padding-left');
console.log(con);
.jsCSS {
width: 10rem;
height: 10rem;
background-color: skyblue;
padding-left: 10px;
}
<div class="jsCSS"></div>
As a matter of safety, you may wish to check that the element exists before you attempt to read from it. If it doesn't exist, your code will throw an exception, which will stop execution on the rest of your JavaScript and potentially display an error message to the user -- not good. You want to be able to fail gracefully.
var height, width, top, margin, item;
item = document.getElementById( "image_1" );
if( item ) {
height = item.style.height;
width = item.style.width;
top = item.style.top;
margin = item.style.margin;
} else {
// Fail gracefully here
}
The cross-browser solution without DOM manipulation given above does not work because it gives the first matching rule, not the last. The last matching rule is the one which applies. Here is a working version:
function getStyleRuleValue(style, selector) {
let value = null;
for (let i = 0; i < document.styleSheets.length; i++) {
const mysheet = document.styleSheets[i];
const myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
for (let j = 0; j < myrules.length; j++) {
if (myrules[j].selectorText &&
myrules[j].selectorText.toLowerCase() === selector) {
value = myrules[j].style[style];
}
}
}
return value;
}
However, this simple search will not work in case of complex selectors.

Categories