Convert a CSSStyleDeclaration object to a Style Sheet - javascript

How can you create a CSS Style Sheet Rule from a CSSStyleDeclaration object?
The docs for inserting a new Style Sheet rule require the selector and declaration to be declared as a string: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule
myStyle.insertRule('p { font-weight: bold, color:red }');
But what if you already have it as a CSSStyleDeclaration like object?
{
fontWeight:'bold',
color:red
}
You could loop the object, set each prop on a tmp element.style and then grab its css text...
const myStyle = {
fontWeight:'bolder',
color:'red',
};
const tmpStyleEl = document.createElement('style');
for( const prop in myStyle ) {
tmpStyleEl.style[prop] = myStyle[prop];
};
// this converts it to a valid css string format
const ruleStr = tmpStyleEl.style.cssText;
const style = document.createElement('style');
style.sheet.insertRule(`p { ${ruleStr} }`);
document.body.append(style);
But that feels overkill!
Some Background
We have user defined styles/themes (saved as CSSStyleDeclarations at the moment), and then we have user generated HTML (simple collection of <p> and <h1> tags generated by Quill).
We then want to generate a Stylesheet so all the nested <p> and <h1> tags can pick up on the style....
Maybe we store the style declarations as an actual CSS Style Sheet string? We could then create the sheet as an external .css file - but then to populate the model data for the UI controls to edit the theme, we would have to do some reverse process to the above anyway....

This is probably unsafe and you should stick to using elements, but I suppose you could use Object.entries and create a string from that:
const myStyle = {
fontWeight:'bolder',
color:'red',
};
// Taken from https://stackoverflow.com/questions/63116039/camelcase-to-kebab-case
const kebabify = (str) => str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? "-" : "") + $.toLowerCase())
const style = document.createElement('style');
// get entries as key-value pairs
// format with a colon
// join rules with semicolons
style.sheet.insertRule(`p { ${
Object.entries(myStyle)
.map(([key, value]) => `${kebabify(key)}: ${value}`)
.join("; ")
} }`);
document.body.append(style);

Related

Get the referenced value of a CSS variable

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(';'));

using replaceWith to replace an element with a DOMstring or multiple elements in Vanilla JS

I can't find examples of vanilla javascript replaceWith using multiple elements/nodes.
Given HTML with numerous children:
<span id="parent"><span>Hardware:</span> <br>
the <span id="oldChild">physical</span> components of a <span>computer</span>.</span>
Can I use replaceWith to swap any one of the child spans, say #oldChild, with multiple elements AND text nodes (the commas and spaces following these spans):
const newSpans =
"<span id="newChild1">kickable</span>,
<span id="newChild2">throwable</span>,
<span id="newChild3">punchable</span>"
What is wrong with the syntax of the following? And how can I convert this dynamically generated code (above) into an acceptable argument for replaceWith?
oldChild.replaceWith( newSpans );
Many thanks to Phil, below:
const temp = document.createElement("div")
temp.innerHTML = newSpans
const oldChild = document.getElementById("oldChild")
oldChild.replaceWith(...temp.childNodes)
Note: Phil advises wisely it would be preferable to avoid HTML strings (ie it would be preferable to use other data structures like objects and arrays).
Can I use replaceWith to swap any one of the child spans with multiple elements AND text nodes
The signature for Element.replaceWith() accepts a variable number of Node or DOMString arguments...
Syntax
replaceWith(...nodes)
...so, yes
// helper / utility function
const createSpan = (id, textContent) => Object.assign(document.createElement("span"), { id, textContent })
document.getElementById("oldChild").replaceWith(
createSpan("newChild1", "kickable"), // Node
", ", // DOMString
createSpan("newChild2", "throwable"), // Node
", ", // DOMString
createSpan("newChild3", "punchable") // Node
)
#newChild1 { color: green; }
#newChild2 { color: orange; }
#newChild3 { color: red; }
<span id="parent"><span>Hardware:</span> <br> the <span id="oldChild">physical</span> components of a <span>computer</span>.</span>
You could also build up an array of nodes to pass to replaceWith and use spread syntax
const newSpans = [
createSpan("newChild1", "kickable"),
createSpan("newChild2", "throwable"),
createSpan("newChild3", "punchable")
]
// Add in separators
const newNodes = newSpans.flatMap(s => [s, ", "]).slice(0, -1)
document.getElementById("oldChild").replaceWith(...newNodes) // spread
If all you've got is a string containing HTML, you can...
Create a temporary element
Set the innerHTML
Pass that element's child nodes to replaceWith
let newSpans =
`<span id="newChild1">kickable</span>,
<span id="newChild2">throwable</span>,
<span id="newChild3">punchable</span>`
const tmp = document.createElement("div")
tmp.innerHTML = newSpans
document.getElementById("oldChild").replaceWith(...tmp.childNodes)
#newChild1 { color: green; }
#newChild2 { color: orange; }
#newChild3 { color: red; }
/* just showing that #oldChild and the <div> aren't included */
#oldChild, div { background: red; }
<span id="parent"><span>Hardware:</span> <br> the <span id="oldChild">physical</span> components of a <span>computer</span>.</span>

Convert style attribute value to css object

I want to convert my node style attribute value to a js object which contains all the CSS properties and its values.
Style attribute value
background-image:url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLEhYLDhAVDhkVDhEQFhUYFxMZGBYVFhUdKysjHR0oHR0WJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHDsoIh0vLy87Ozs7Oy87LzsvLy8vNS8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBQEEBwD/xAAeEAABBAIDAQAAAAAAAAAAAAAAAQIDIQQSExQxEf/EABYBAQEBAAAAAAAAAAAAAAAAAAMEAv/EABoRAAICAwAAAAAAAAAAAAAAAAECACEDETH/2gAMAwEAAhEDEQA/ANTbnMkpAvZaxLE2DGv30uzsXQlU7E0UyrTdhJc1rqOFnGuxIqioZGaf/9k=);background-color:#00cc7e;background-size:cover;opacity:1;transition:opacity 500ms 500ms;position:absolute;left:0;top:0;width:100%;height:100%
Convert it to JS Object
{
backgroundImage: url(data;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLEhYLDhAVDhkVDhEQFhUYFxMZGBYVFhUdKysjHR0oHR0WJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHDsoIh0vLy87Ozs7Oy87LzsvLy8vNS8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBQEEBwD/xAAeEAABBAIDAQAAAAAAAAAAAAAAAQIDIQQSExQxEf/EABYBAQEBAAAAAAAAAAAAAAAAAAMEAv/EABoRAAICAwAAAAAAAAAAAAAAAAECACEDETH/2gAMAwEAAhEDEQA/ANTbnMkpAvZaxLE2DGv30uzsXQlU7E0UyrTdhJc1rqOFnGuxIqioZGaf/9k=): undefined;
backgroundColor: #00cc7e;
backgroundSize: cover;
opacity: 1;
transition: opacity 500ms 500ms;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
Any helpful answer will be appreciated.
You can try methods of CSSStyleDeclaration.
Here getStyles is of type CSSStyleDeclaration which will give list of inline style properties.
const getStyles = document.getElementById('img').style;
const properties = Array.from(getStyles).reduce((acc, curr) => {
acc[curr] = getStyles.getPropertyValue(curr)
return acc;
}, {})
console.log(properties)
<img id='img' style="background-image:url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLEhYLDhAVDhkVDhEQFhUYFxMZGBYVFhUdKysjHR0oHR0WJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHDsoIh0vLy87Ozs7Oy87LzsvLy8vNS8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBQEEBwD/xAAeEAABBAIDAQAAAAAAAAAAAAAAAQIDIQQSExQxEf/EABYBAQEBAAAAAAAAAAAAAAAAAAMEAv/EABoRAAICAwAAAAAAAAAAAAAAAAECACEDETH/2gAMAwEAAhEDEQA/ANTbnMkpAvZaxLE2DGv30uzsXQlU7E0UyrTdhJc1rqOFnGuxIqioZGaf/9k=);background-color:#00cc7e;background-size:cover;opacity:1;transition:opacity 500ms 500ms;position:absolute;left:0;top:0;width:100%;height:100%">
Well you can definitely do that by:
let element = document.querySelector("your-selector-here")
console.log(element.style)
But let me warn you that you'll get a very big object of css properties because your element has more css properties than what you define in you style-sheet.
Why you wanna do so ?
If its style of an element you can grab it by first selecting the element in your js and then getting style using element.style ,you can slect element by various methods such as by its ID, class etc. Tho this will give you all the element's style including the default one's.
console.log(document.querySelector('#test').style)
<div id="test"></div>
This shall be used in worst case scenario until you can filter the default one's.
If you have the style as a sting with you, and you want to convert it to object, you can use JSON.parse ,but before that you will have to convert you styles into object format, replace ';' with ',' and convert each individual key and value to string. This might not be easy.
const test = 'background-color:#00cc7e;background-size:cover;opacity:1;transition:opacity 500ms 500ms;position:absolute;left:0;top:0;width:100%;height:100%';
var afterReplace = test.replaceAll(";", '","').replaceAll(":", '":"');
objectForm = JSON.parse('{"' + afterReplace + '"}')
console.log(objectForm)
Here add string value of the style in test from which we replace all ';' to '";"' and ':' to '":"' then we add {" and "} to both start and end, and finally parse it into a json object.
Though I would suggest you not to use it.
This answer is incorrect but I thought it was important to leave here. While it works fine in Chrome, it doesn't work in Firefox, where the CSS2Properties object they return don't have enumerable property.
This method goes through the computed style of the element, so it has some browser-specific values. element.style has all possible style attributes, so this filters out the empty ones and returns the balance as a javascript notation style object
let element = document.querySelector('#element');
let styleobj = Object.fromEntries(Object
.entries(element.style)
.filter(e => isNaN(e[0]) && e[1].trim())
)
let element = document.querySelector('#element');
let styleobj = Object.fromEntries(Object.entries(element.style).filter(e => isNaN(e[0]) && e[1].trim()))
console.log(styleobj)
<div id='element' style='background-image:url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLEhYLDhAVDhkVDhEQFhUYFxMZGBYVFhUdKysjHR0oHR0WJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHDsoIh0vLy87Ozs7Oy87LzsvLy8vNS8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBQEEBwD/xAAeEAABBAIDAQAAAAAAAAAAAAAAAQIDIQQSExQxEf/EABYBAQEBAAAAAAAAAAAAAAAAAAMEAv/EABoRAAICAwAAAAAAAAAAAAAAAAECACEDETH/2gAMAwEAAhEDEQA/ANTbnMkpAvZaxLE2DGv30uzsXQlU7E0UyrTdhJc1rqOFnGuxIqioZGaf/9k=);background-color:#00cc7e;background-size:cover;opacity:1;transition:opacity 500ms 500ms;position:absolute;left:0;top:0;width:100%;height:100%'></div>

CKEditor JSON Output [duplicate]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
I'm attempting map HTML into JSON with structure intact. Are there any libraries out there that do this or will I need to write my own? I suppose if there are no html2json libraries out there I could take an xml2json library as a start. After all, html is only a variant of xml anyway right?
UPDATE: Okay, I should probably give an example. What I'm trying to do is the following. Parse a string of html:
<div>
<span>text</span>Text2
</div>
into a json object like so:
{
"type" : "div",
"content" : [
{
"type" : "span",
"content" : [
"Text2"
]
},
"Text2"
]
}
NOTE: In case you didn't notice the tag, I'm looking for a solution in Javascript
I just wrote this function that does what you want; try it out let me know if it doesn't work correctly for you:
// Test with an element.
var initElement = document.getElementsByTagName("html")[0];
var json = mapDOM(initElement, true);
console.log(json);
// Test with a string.
initElement = "<div><span>text</span>Text2</div>";
json = mapDOM(initElement, true);
console.log(json);
function mapDOM(element, json) {
var treeObject = {};
// If string convert to document Node
if (typeof element === "string") {
if (window.DOMParser) {
parser = new DOMParser();
docNode = parser.parseFromString(element,"text/xml");
} else { // Microsoft strikes again
docNode = new ActiveXObject("Microsoft.XMLDOM");
docNode.async = false;
docNode.loadXML(element);
}
element = docNode.firstChild;
}
//Recursively loop through DOM elements and assign properties to object
function treeHTML(element, object) {
object["type"] = element.nodeName;
var nodeList = element.childNodes;
if (nodeList != null) {
if (nodeList.length) {
object["content"] = [];
for (var i = 0; i < nodeList.length; i++) {
if (nodeList[i].nodeType == 3) {
object["content"].push(nodeList[i].nodeValue);
} else {
object["content"].push({});
treeHTML(nodeList[i], object["content"][object["content"].length -1]);
}
}
}
}
if (element.attributes != null) {
if (element.attributes.length) {
object["attributes"] = {};
for (var i = 0; i < element.attributes.length; i++) {
object["attributes"][element.attributes[i].nodeName] = element.attributes[i].nodeValue;
}
}
}
}
treeHTML(element, treeObject);
return (json) ? JSON.stringify(treeObject) : treeObject;
}
Working example: http://jsfiddle.net/JUSsf/ (Tested in Chrome, I can't guarantee full browser support - you will have to test this).
​It creates an object that contains the tree structure of the HTML page in the format you requested and then uses JSON.stringify() which is included in most modern browsers (IE8+, Firefox 3+ .etc); If you need to support older browsers you can include json2.js.
It can take either a DOM element or a string containing valid XHTML as an argument (I believe, I'm not sure whether the DOMParser() will choke in certain situations as it is set to "text/xml" or whether it just doesn't provide error handling. Unfortunately "text/html" has poor browser support).
You can easily change the range of this function by passing a different value as element. Whatever value you pass will be the root of your JSON map.
htlm2json
Representing complex HTML documents will be difficult and full of corner cases, but I just wanted to share a couple techniques to show how to get this kind of program started. This answer differs in that it uses data abstraction and the toJSON method to recursively build the result
Below, html2json is a tiny function which takes an HTML node as input and it returns a JSON string as the result. Pay particular attention to how the code is quite flat but it's still plenty capable of building a deeply nested tree structure – all possible with virtually zero complexity
const Elem = e => ({
tagName:
e.tagName,
textContent:
e.textContent,
attributes:
Array.from(e.attributes, ({name, value}) => [name, value]),
children:
Array.from(e.children, Elem)
})
const html2json = e =>
JSON.stringify(Elem(e), null, ' ')
console.log(html2json(document.querySelector('main')))
<main>
<h1 class="mainHeading">Some heading</h1>
<ul id="menu">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<p>some text</p>
</main>
In the previous example, the textContent gets a little butchered. To remedy this, we introduce another data constructor, TextElem. We'll have to map over the childNodes (instead of children) and choose to return the correct data type based on e.nodeType – this gets us a littler closer to what we might need
const TextElem = e => ({
type:
'TextElem',
textContent:
e.textContent
})
const Elem = e => ({
type:
'Elem',
tagName:
e.tagName,
attributes:
Array.from(e.attributes, ({name, value}) => [name, value]),
children:
Array.from(e.childNodes, fromNode)
})
const fromNode = e => {
switch (e?.nodeType) {
case 1: return Elem(e)
case 3: return TextElem(e)
default: throw Error(`unsupported nodeType: ${e.nodeType}`)
}
}
const html2json = e =>
JSON.stringify(Elem(e), null, ' ')
console.log(html2json(document.querySelector('main')))
<main>
<h1 class="mainHeading">Some heading</h1>
<ul id="menu">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<p>some text</p>
</main>
Anyway, that's just two iterations on the problem. Of course you'll have to address corner cases where they come up, but what's nice about this approach is that it gives you a lot of flexibility to encode the HTML however you wish in JSON – and without introducing too much complexity
In my experience, you could keep iterating with this technique and achieve really good results. If this answer is interesting to anyone and would like me to expand upon anything, let me know ^_^
Related: Recursive methods using JavaScript: building your own version of JSON.stringify
json2html
Above we go from HTML to JSON and now we can go from JSON to HTML. When we can convert between two data types without losing data, this is called an isomorphism. All we are essentially doing here is writing the inverses of each function above -
const HtmlNode = (tagName, attributes = [], children = []) => {
const e = document.createElement(tagName)
for (const [k, v] of attributes) e.setAttribute(k, v)
for (const child of children) e.appendChild(toNode(child))
return e
}
const TextNode = (text) => {
return document.createTextNode(text)
}
const toNode = t => {
switch (t?.type) {
case "Elem": return HtmlNode(t.tagName, t.attributes, t.children)
case "TextElem": return TextNode(t.textContent)
default: throw Error("unsupported type: " + t.type)
}
}
const json2html = json =>
toNode(JSON.parse(json))
const parsedJson =
{"type":"Elem","tagName":"MAIN","attributes":[],"children":[{"type":"TextElem","textContent":"\n "},{"type":"Elem","tagName":"H1","attributes":[["class","mainHeading"]],"children":[{"type":"TextElem","textContent":"Some heading"}]},{"type":"TextElem","textContent":"\n "},{"type":"Elem","tagName":"UL","attributes":[["id","menu"]],"children":[{"type":"TextElem","textContent":"\n "},{"type":"Elem","tagName":"LI","attributes":[],"children":[{"type":"Elem","tagName":"A","attributes":[["href","/a"]],"children":[{"type":"TextElem","textContent":"a"}]}]},{"type":"TextElem","textContent":"\n "},{"type":"Elem","tagName":"LI","attributes":[],"children":[{"type":"Elem","tagName":"A","attributes":[["href","/b"]],"children":[{"type":"TextElem","textContent":"b"}]}]},{"type":"TextElem","textContent":"\n "},{"type":"Elem","tagName":"LI","attributes":[],"children":[{"type":"Elem","tagName":"A","attributes":[["href","/c"]],"children":[{"type":"TextElem","textContent":"c"}]}]},{"type":"TextElem","textContent":"\n "}]},{"type":"TextElem","textContent":"\n "},{"type":"Elem","tagName":"P","attributes":[],"children":[{"type":"TextElem","textContent":"some text"}]},{"type":"TextElem","textContent":"\n"}]}
document.body.appendChild(toNode(parsedJson))
I got few links sometime back while reading on ExtJS full framework in itself is JSON.
http://www.thomasfrank.se/xml_to_json.html
http://camel.apache.org/xmljson.html
online XML to JSON converter : http://jsontoxml.utilities-online.info/
UPDATE
BTW, To get JSON as added in question, HTML need to have type & content tags in it too like this or you need to use some xslt transformation to add these elements while doing JSON conversion
<?xml version="1.0" encoding="UTF-8" ?>
<type>div</type>
<content>
<type>span</type>
<content>Text2</content>
</content>
<content>Text2</content>
Thank you #Gorge Reith. Working off the solution provided by #George Reith, here is a function that furthers (1) separates out the individual 'hrefs' links (because they might be useful), (2) uses attributes as keys (since attributes are more descriptive), and (3) it's usable within Node.js without needing Chrome by using the 'jsdom' package:
const jsdom = require('jsdom') // npm install jsdom provides in-built Window.js without needing Chrome
// Function to map HTML DOM attributes to inner text and hrefs
function mapDOM(html_string, json) {
treeObject = {}
// IMPT: use jsdom because of in-built Window.js
// DOMParser() does not provide client-side window for element access if coding in Nodejs
dom = new jsdom.JSDOM(html_string)
document = dom.window.document
element = document.firstChild
// Recursively loop through DOM elements and assign attributes to inner text object
// Why attributes instead of elements? 1. attributes more descriptive, 2. usually important and lesser
function treeHTML(element, object) {
var nodeList = element.childNodes;
if (nodeList != null) {
if (nodeList.length) {
object[element.nodeName] = [] // IMPT: empty [] array for non-text recursivable elements (see below)
for (var i = 0; i < nodeList.length; i++) {
// if final text
if (nodeList[i].nodeType == 3) {
if (element.attributes != null) {
for (var j = 0; j < element.attributes.length; j++) {
if (element.attributes[j].nodeValue !== '' &&
nodeList[i].nodeValue !== '') {
if (element.attributes[j].name === 'href') { // separate href
object[element.attributes[j].name] = element.attributes[j].nodeValue;
} else {
object[element.attributes[j].nodeValue] = nodeList[i].nodeValue;
}
}
}
}
// else if non-text then recurse on recursivable elements
} else {
object[element.nodeName].push({}); // if non-text push {} into empty [] array
treeHTML(nodeList[i], object[element.nodeName][object[element.nodeName].length -1]);
}
}
}
}
}
treeHTML(element, treeObject);
return (json) ? JSON.stringify(treeObject) : treeObject;
}
I had a similar issue where I wanted to represent HTML as JSON in the following way:
For HTML text nodes, use a string
For HTML elements, use an array with:
The (tag) name of the element
An object, mapping attribute keys to attribute values
The (inlined) list of children nodes
Example:
<div>
<span>text</span>Text2
</div>
becomes
[
'div',
{},
['span', {}, 'text'],
'Text2'
]
I wrote a function which handles transforming a DOM Element into this kind of JS structure. You can find this function at the end of this answer. The function is written in Typescript. You can use the Typescript playground to convert it to clean JavaScript.
Furthermore, if you need to parse an html string into DOM, assign to .innerHtml:
let element = document.createElement('div')
element.innerHtml = htmlString
Also, this one is common knowledge but if you need a JSON string output, use JSON.stringify.
/**
* A NodeDescriptor stands for either an (HTML) Element, or for a text node
*/
export type NodeDescriptor = ElementDescriptor | string
/**
* Array representing an HTML Element. It consists of:
*
* - The (tag) name of the element
* - An object, mapping attribute keys to attribute values
* - The (inlined) list of children nodes
*/
export type ElementDescriptor = [
string,
Record<string, string>,
...NodeDescriptor[]
]
export let htmlToJs = (element: Element, trim = true): ElementDescriptor => {
let convertElement = (element: Element): ElementDescriptor => {
let attributeObject: Record<string, string> = {}
for (let { name, value } of element.attributes) {
attributeObject[name] = value
}
let childArray: NodeDescriptor[] = []
for (let node of element.childNodes) {
let converter = htmlToJsDispatch[node.nodeType]
if (converter) {
let descriptor = converter(node as any)
let skip = false
if (trim && typeof descriptor === 'string') {
descriptor = descriptor.trim()
if (descriptor === '') skip = true
}
if (!skip) childArray.push(descriptor)
}
}
return [element.tagName.toLowerCase(), attributeObject, ...childArray]
}
let htmlToJsDispatch = {
[element.ELEMENT_NODE]: convertElement,
[element.TEXT_NODE]: (node: Text): string => node.data,
}
return convertElement(element)
}

How to get an HTML element's style values in JavaScript?

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"]);

Categories

Resources