Given
<div></div>
<style>
body {
--var1: 3;
--var2: var(--var1);
}
div {
--var3: var(--var2);
width: var(--var3);
}
</style>
How can I obtain the reference list of css variables?
getReferenceList(divElement, 'width')
Would result in
['--var3', '--var2', '--var1', 3]
If you already know the property name, you can use getComputedStyle. For instance,
window.getComputedStyle(document.querySelector('body')).getPropertyValue('--var1');
This will return 3.
If you would like to get a list of all stylesheets, you can use CSSStyleSheet.cssRules.
The following example will give you the stylesheet of body:
document.styleSheets[0].cssRules[0].cssText
So the result of it will be "body { --var1: 3; --var2: var(--var1); }".
In your case, cssRules[0] = body and cssRules[1] = div.
You can also use selectorText to find out what tag it is. In this case document.styleSheets[0].cssRules[0].selectorText will return the string body.
If you prefer an array of properties and values, instead of a string, you can use this regex:
const regex = /(?<={)[ -a-zA-Z0-9\S][^}]+/gm
const str = document.styleSheets[0].cssRules[0].cssText.match(regex);
console.log(str[0].split(';'));
Related
Endless Variables
Often I find myself assigning numerous DOM elements to a list variables, and naming them gets increasingly difficult as things grow more complex.
const header = document.querySelector('header')
const headerWrapper = header.querySelector('.header-wrapper')
const headerH1 = headerWrapper.querySelector('h1')
const headerH2 = headerWrapper.querySelector('h2')
// etc...
Object Notation
It occurred to me try referencing the DOM hierarchy using Objects instead and came up with something like this:
// assign DOM elements to object properties
const homePage = {
headerContainer: document.querySelector('header'),
headerWrapper: document.querySelector('header>.header-wrapper'),
header: {
h1: document.querySelector('header>.header-wrapper>h1'),
h2: document.querySelector('header>.header-wrapper>h2'),
}
}
// update element content
homePage.header.h1.innerText = "New Header Text"
This method is flawed however:
Parent nodes must be given unique keys to reference directly
Objects can't refer to themselves until they have been initialized, so I can't have h1: this.headerContainer.querySelector('h1')
Instead of relying on headerContainer it would be better to have homePage.header, as well as the h1 tag within that container with homePage.header.h1 but I'm not sure if that's possible with JavaScript/JSON.... to my mind it would be something like this:
// pseudocode
const homePage = {
header:
document.querySelector('header'),
{
h1: this.header.querySelector('h1'),
h2: this.header.querySelector('h2'),
}
}
// update element content
homePage.header.h1.innerText = "Cleaner Syntax"
Is there some other means of doing this? Using Classes or Object Constructors perhaps?
Edit 1:
Solution: Using Classes
I think what I want to work out is how to store elements as an attribute of another element or object property, and I think I have worked out how to do that using Classes:
class Page {
constructor(body = document.querySelector('body')) {
this.header = body.querySelector('header')
this.header.h1 = this.header.querySelector('h1')
}
}
const homePage = new Page()
homePage.header.style.backgroundColor='yellow'
homePage.header.h1.textContent = "Hello World"
Consider that I create some custom elements with HTML5
<orange-juice>...</orange-juice>
<apple-juice>...</apple-juice>
<banana-juice>...</banana-juice>
There are many type of juice elements. And I want to select them with a single instruction with jQuery using their suffix.
I try that but it does not work :
$('$=juice').html('juice'); //the .html instruction is not important
If i take them one by one this work.
$('orange-juice').html('juice'); //this work
$('apple-juice').html('juice'); //this work
$('banana-juice').html('juice'); //this work
But there are many of these custom element suffixed by juice. How can I select them in one instruction.
EDIT 1
It's sure that a common class will work but, it's not my code and there are too many of these elements to take theme one by one.
But if no solution then, I will make this (during a month).
You can try .filter(fn) function, Here is an example of prefix
$('body *').filter(function() {
return this.tagName.toLowerCase().indexOf('juice') == 0;
}).html('juice');
However I would recommend, you to assign a common class then Class Selector (“.class”) can be easily used.
Example of Suffix, Here I have used endsWith() method
jQuery(function($) {
$('body *').filter(function() {
return this.tagName.toLowerCase().endsWith('juice');
}).html('juice');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<orange-juice>...</orange-juice>
<apple-juice>...</apple-juice>
<banana-juice>...</banana-juice>
While you've already accepted a jQuery solution to the problem, which is what you asked for, it's worth also adding – if only for the sake of completion – a plain JavaScript approach for the same.
// declaring an object to contain the two functions:
let findElementsBy = {
// retrieving all elements held within the <body> element,
// we could instead use:
// document.getElementsByTagName('*')
// but this is just personal preference:
'allElems': document.querySelectorAll('body *'),
// declaring the 'suffix' function:
// ending: String, a required argument which is 'ending'
// by which we're filtering the retrieved elements:
'suffix': function(ending) {
// here we use Array.from() to convert the Array-like
// NodeList into an Array:
return Array.from(this.allElems)
// we filter that Array using Array.prototype.filter():
.filter(
// here we use an Arrow function to keep only those
// elements ('el', the current Array-element of the
// Array over which we're iterating) whose lower-case
// tagName ends with the supplied 'ending' String,
// determined using String.prototype.endsWith(),
// which returns a Boolean:
el => el.tagName.toLowerCase().endsWith(ending)
// this filtered Array is then passed back to the
// calling context as an Array, which allows that
// context to iterate through the returned elements
// using Array methods.
);
},
'prefix': function(beginning) {
return Array.from(this.allElems)
.filter(
// this function is exactly the same as the above,
// but here we use String.prototype.startsWith()
// to find those elements whose lower-cased tagName
// begins with the supplied String:
el => el.tagName.toLowerCase().startsWith(beginning)
);
}
}
findElementsBy.suffix('juice').forEach(e => e.style.borderColor = 'limegreen');
findElementsBy.prefix('banana').forEach(e => e.style.backgroundColor = '#ffa');
let findElementsBy = {
'allElems': document.querySelectorAll('body *'),
'suffix': function(ending) {
return Array.from(this.allElems)
.filter(
el => el.tagName.toLowerCase().endsWith(ending)
);
},
'prefix': function(beginning) {
return Array.from(this.allElems)
.filter(
el => el.tagName.toLowerCase().startsWith(beginning)
);
}
}
findElementsBy.suffix('juice').forEach(e => e.style.borderColor = 'limegreen');
findElementsBy.prefix('banana').forEach(e => e.style.backgroundColor = '#ffa');
orange-juice,
apple-juice,
banana-juice {
display: block;
border: 1px solid transparent;
margin: 1em auto 0 auto;
width: 80%;
}
<orange-juice>...</orange-juice>
<apple-juice>...</apple-juice>
<banana-juice>...</banana-juice>
JS Fiddle demo.
References:
Array.from().
Array.prototype.filter().
Array.prototype.forEach().
Arrow functions.
document.getElementsByTagName()
document.querySelectorAll().
String.prototype.endsWith().
String.prototype.toLowerCase().
String.prototype.startsWith().
I'm thinking for develop a web component form, so... I was wondering my self if it's possible read attributes from a html elements. For example:
<my-component input="3" data-input='"name", "address", "email"' textarea="1" data-textarea='"mensaje"'></my-component>
<script>
while i <= elementattributes.length{
elementattributes[i]
}
</script>
I know that I can access through .getAttribute, but this way will take more code... I just want to be something smart :P
Sorry, my english is poor... I hope you can understand what I want to do :P
:)
If you want to get all of the attributes and their values, you can do something like this:
function getElementAttrs(el) {
var attributes = [].slice.call(el.attributes);
return attributes.map(function(attr) {
return {
name: attr.name,
value: attr.value
}
});
}
var allAttrs = getElementAttrs(document.querySelector('my-component'));
console.log(allAttrs);
<my-component input="3" data-input='name,address,email' textarea="1" data-textarea='"mensaje"'></my-component>
The function getElementAttrs returns an array of objects to you with attribute name-value pairs as keys on the object so that you can loop over it, or just pull by key.
You can use dataset to get specific data attribute
var data = document.querySelector('my-component').dataset.input.split(',');
data.forEach(function(e) {
console.log(e)
})
<my-component input="3" data-input='name,address,email' textarea="1" data-textarea='"mensaje"'></my-component>
If you want to return all attributes from element you can use attributes which return array-like object, but you can create object with name-value from that
var data = document.querySelector('my-component').attributes, attr = {};
Array.from(data).forEach(function(e) {
attr[e.name] = e.value;
});
console.log(attr['data-input']);
<my-component input="3" data-input='name,address,email' textarea="1" data-textarea='mensaje'></my-component>
You can just use .attributes:
document.getElementById("ELEMENT_ID").attributes
This will return an object containing each attribute with its associated value. You can then index the attributes array for specific attributes and use .value for it's corresponding value.
In an application where certain elements have custom CSS properties, is there any way to retrieve such a value via JavaScript?
e.g.
<div id="myDiv" style="color:#f00;-my-custom-property:upsidedown;" />
I can access the color attribute via these two methods:
document.getElementById('myDiv').style.getPropertyValue("color")
document.getElementById('myDiv').style.color
But these do not work for custom properties. Is this supported at all?
CSS values not understood by the browser are discarded, which explains why -my-custom-property was unavailable via .style.
In the past, you would have had to rely on storing the data with data attributes and dealing with inheritance yourself via JavaScript.
However, "custom properties", aka "CSS variables", have since been introduced into the standard and implemented by browsers, with ~92% support globally as of 2019-05-09. At a quick glance, Edge seems to have been the last major browser to implement, with version 16 on October 16, 2017.
Essentially, you need to set a custom property (eg, --my-custom-property: 'foobar';) on an element, and it can be accessed with something like getComputedStyle(your_el).getPropertyValue("--my-custom-property") which would return 'foobar' (with a leading space). Note the leading space and quotation marks. It will return the value exactly as it was provided.
Example:
console.log(getComputedStyle(document.getElementById("a")).getPropertyValue("--my-custom-property-1"))
console.log(getComputedStyle(document.getElementById("b")).getPropertyValue("--my-custom-property-2"))
#b-div { --my-custom-property-2: 'world' }
<div style="--my-custom-property-1: 'hello'"><h1 id="a">#a 'hello'</h1></div>
<div id="b-div"><h1 id="b">#b 'world'</h1></div>
Here's some testing using one and two leading hyphens, inheritance, and different methods of retrieving the value:
function log(computed, selector, prop, value) {
let method = computed ? "getComputedStyle(el)" : "el.style"
let method_id = computed ? "computed" : "raw"
// Build first level of list (tag name)
let first = document.querySelector("#" + selector)
if (!first) {
first = document.createElement("li")
first.appendChild(document.createTextNode(selector))
first.setAttribute("id", selector)
first.appendChild(document.createElement("ul"))
document.querySelector("ul").appendChild(first)
}
// Build second level of list (method of style retrieval)
let second = document.querySelector("#" + selector + "-" + method_id)
if (!second) {
second = document.createElement("li")
second.appendChild(document.createTextNode(method))
second.setAttribute("id", selector + "-" + method_id)
second.appendChild(document.createElement("ul"))
first.querySelector("ul").appendChild(second)
}
// Build third level of list (property accessed)
let third = document.querySelector("#" + selector + "-prop" + prop)
if (!third) {
third = document.createElement("li")
third.appendChild(document.createTextNode(prop + ": `" + value + "`"))
third.setAttribute("id", "prop" + prop)
second.querySelector("ul").appendChild(third)
if (value === "") {
third.classList.add("bad")
} else {
third.classList.add("good")
}
}
}
// Uses .style
function getStyleAttr(selector, prop) {
let value = document.querySelector(selector).style.getPropertyValue(prop)
log(false, selector, prop, value)
}
// Uses getComputedStyle()
function getStyleComputed(selector, prop) {
let value = getComputedStyle(document.querySelector(selector)).getPropertyValue(prop)
log(true, selector, prop, value)
}
// Loop through each property for each element and output the value
let selectors = ["article", "h1", "p"]
let props = ["--my-custom-property", "-my-custom-property"]
selectors.forEach(function(selector) {
props.forEach(function(prop) {
getStyleAttr(selector, prop)
getStyleComputed(selector, prop)
})
})
code {
background: #eee;
padding: .2em;
}
.bad {
color: #800;
}
.good {
color: #080;
}
<article class="custom-prop-inheritance" style="--my-custom-property: 'foobar'; -my-custom-property: 'foobar'">
<h1>Title</h1>
<p>Custom properties require two leading hyphens (<code>-my-custom-property</code> <em>never</em> works). Using <code>el.style</code> does not support inheritance. To support both inheritance and custom properties, you must use <code>getComputedStyle(<b>el</b>)</code> along with two leading hyphens on the custom property (eg, <code>--my-custom-property</code>).</p>
</article>
<ul></ul>
CSS:
:root {
--custom-property: #000000;
}
Javascript:
var custom_property = window.getComputedStyle(document.body).getPropertyValue('--custom-property').trim()
Non-recognised CSS properties will be ignored when put within the style attribute, or in the style.cssText property.
If you want to define a property at a specific element, I recommend data-attributes:
HTML:
<div id="myDiv" style="color:#f00;" data-custom-property="upsidedown" />
JavaScript:
//jQuery's method to retrieve value:
$("#myDiv").data("custom-property");
//jQuery, without parsing:
$("#myDiv").attr("data-custom-property");
// Modern browsers, native JS:
document.getElementById("myDiv").dataset["custom-property"];
// Older browsers, native JS:
document.getElementById("myDiv").getAttribute("data-custom-property");
This is actually now possible for all browsers using a specialized CSS hack via the CSS content tag. This article explains how to do it:
http://www.yearofmoo.com/2015/04/cross-browser-custom-css-properties.html
function getCustomCssProperty(elementID, propertyName){
var style = document.getElementById(elementID).getAttribute("style");
var entries = style.split(";");
for (var i=0; i<entries.length; i++){
var entry = entries[i].split(":");
if(entry[0] == propertyName){
return entry[1];
}
}
return null;
}
You can't use data-* attributes (html5)?
That would at least be valid and not a strange hack.
I am looking for a way to retrieve the style from an element that has a style set upon it by the style tag.
<style>
#box {width: 100px;}
</style>
In the body
<div id="box"></div>
I'm looking for straight javascript without the use of libraries.
I tried the following, but keep receiving blanks:
alert (document.getElementById("box").style.width);
alert (document.getElementById("box").style.getPropertyValue("width"));
I noticed that I'm only able to use the above if I have set the style using javascript, but unable to with the style tags.
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 CCS 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).
Also, the IE element.currentStyle will return all the sizes in the unit that they were specified, (e.g. 12pt, 50%, 5em), the standard way will compute the actual size in pixels always.
I made some time ago a cross-browser function that allows you to get the computed styles in a cross-browser way:
function getStyle(el, styleProp) {
var value, defaultView = (el.ownerDocument || document).defaultView;
// W3C standard way:
if (defaultView && defaultView.getComputedStyle) {
// sanitize property name to css notation
// (hypen 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;
}
}
The above function is not perfect for some cases, for example for colors, the standard method will return colors in the rgb(...) notation, on IE they will return them as they were defined.
I'm currently working on an article in the subject, you can follow the changes I make to this function here.
I believe you are now able to use Window.getComputedStyle()
Documentation MDN
var style = window.getComputedStyle(element[, pseudoElt]);
Example to get width of an element:
window.getComputedStyle(document.querySelector('#mainbar')).width
In jQuery, you can do alert($("#theid").css("width")).
-- if you haven't taken a look at jQuery, I highly recommend it; it makes many simple javascript tasks effortless.
Update
for the record, this post is 5 years old. The web has developed, moved on, etc. There are ways to do this with Plain Old Javascript, which is better.
Use getComputedStyle function, Computed style contains all the CSS properties set to an element. Even if do not set a property to an element. You will still find that property in the computed styles.
Example:
<style>
#Body_element {
color: green;
}
</style>
<body id="Body_element">
<script>
alert(getComputedStyle(Body_element).color)
</script>
</body>
This is a helper function if you want to get multiple style rules from the same element.
You pass it the element and the styles you want as arguments, and it will return their values
const convertRestArgsIntoStylesArr = ([...args]) => {
return args.slice(1);
}
const getStyles = function () {
const args = [...arguments];
const [element] = args;
let stylesProps = [...args][1] instanceof Array ? args[1] : convertRestArgsIntoStylesArr(args);
const styles = window.getComputedStyle(element);
const stylesObj = stylesProps.reduce((acc, v) => {
acc[v] = styles.getPropertyValue(v);
return acc;
}, {});
return stylesObj;
};
Now, you can use this function like this:
const styles = getStyles(document.body, "height", "width");
OR
const styles = getStyles(document.body, ["height", "width"]);