[QUESTION] Get all related CSS Rules from mulit-level CSS Selectors
Question
Hi @artf
Hope you are doing well. I've been cracking on this matter for the past few days now, I've been searching whether I got the same scenario with the others, but unfortunately, I can't find any solutions that can solve my problem, so I decided to raise an issue ticket. I hope I will make sense.
So I have a 'Save Block/Component' feature that I need to grab the HTML and CSS of the selected component. I can easily grab the HTML of the selected component with its child elements. by doing const blockHTML = block.toHTML(); that's working fine. However, my problem lies on the CSS rules of the elements from the parent element going to the child elements.
- So my first try is to iterate to each child component of the selected component so that I can use the
getStyle()function to extract the CSS of the child components, unfortunately, it's returning an empty object. Please refer to screenshot below for the code block that does this.

Here are the logs from the browser for item 1.

It seems that it works with the parent component which is the #banner, it can grab the CSS rules, however when I go deeper with the child components it's returning an empty object. Please refer below for screenshot of the HTML markup for the component with it's CSS rules.
#banner

#banner header

- Then my second try was to go with
editor.CodeManager.getCode(childBlock, 'css', {cssc: editor.CssComposer});That resulted with the following logs.

It gives all the CSS rules in the CSS code editor, wasn't able to pull out the related class rules for the specific component.
- Tried the
CssComposerAPI with the following code block, please refer below, but I ended up getting all results asnull

I don't know how can I solve this problem by now, so I'm seeking some help that you may guide me to a proper solution. Looking forward from hearing from you. Thank you.
Regards, Tyrone
Answers (2)
This is the command I use in Grapedrop to get all the CSS from a component
import each from 'lodash/each';
export default (config = {}) => ({
run(editor, snd, opts = {}) {
const component = opts.target || editor.getWrapper();
const cssc = editor.CssComposer;
const rules = cssc.getAll();
let result = '';
const { atRules, notAtRules } = this.splitRules(this.matchedRules(component, rules));
notAtRules.forEach(rule => result += rule.toCSS());
this.sortMediaObject(atRules).forEach(item => {
let rulesStr = '';
const atRule = item.key;
const mRules = item.value;
mRules.forEach(rule => {
const ruleStr = rule.getDeclaration();
if (rule.get('singleAtRule')) {
result += `${atRule}{${ruleStr}}`;
} else {
rulesStr += ruleStr;
}
});
if (rulesStr) result += `${atRule}{${rulesStr}}`;
});
return result;
},
/**
* Get matched rules of a component
* @param {Component} component
* @param {Array<CSSRule>} rules
* @returns {Array<CSSRule>}
*/
matchedRules(component, rules) {
const el = component.getEl();
let result = [];
rules.forEach(rule => {
try {
if (rule.selectorsToString().split(',').some(
selector => el.matches(this.cleanSelector(selector))
)) {
result.push(rule);
}
} catch (err) {}
});
component.components().forEach(component => {
result = result.concat(this.matchedRules(component, rules))
});
// Remove duplicates
result = result.filter((rule, i) => result.indexOf(rule) == i);
return result;
},
/**
* Return passed selector without states
* @param {String} selector
* @returns {String}
*/
cleanSelector(selector) {
return selector.split(' ').map(item => item.split(':')[0]).join(' ');
},
/**
* Split an array of rules in atRules and not
* @param {Array<CSSRule>} rules
* @returns {Object}
*/
splitRules(rules) {
const atRules = {};
const notAtRules = [];
rules.forEach(rule => {
const atRule = rule.getAtRule();
if (atRule) {
const mRules = atRules[atRule];
if (mRules) {
mRules.push(rule);
} else {
atRules[atRule] = [rule];
}
} else {
notAtRules.push(rule);
}
});
return {
atRules,
notAtRules,
};
},
/**
* Get the numeric length of the media query string
* @param {String} mediaQuery Media query string
* @return {Number}
*/
getQueryLength(mediaQuery) {
const length = /(-?\d*\.?\d+)\w{0,}/.exec(mediaQuery);
if (!length) return Number.MAX_VALUE;
return parseFloat(length[1]);
},
/**
* Return a sorted array from media query object
* @param {Object} items
* @return {Array}
*/
sortMediaObject(items = {}) {
const itemsArr = [];
each(items, (value, key) => itemsArr.push({ key, value }));
return itemsArr.sort(
(a, b) => this.getQueryLength(b.key) - this.getQueryLength(a.key)
);
}
})
Hi @artf
Thank you so much for your response! I will try this out, and let you know if I run into any problems. I really appreciate this.
Related Questions and Answers
Continue research with similar issue discussions.
Issue #1659
[Question] How to upload PDF and others files - GrapesJS
Hi @artf, I started using your web builder a few days ago and I found it very well done ! I was able to customize it in my own way but I ha...
Issue #1509
How to disable undo button
Hi @artf, Hope you are doing well with grapesjs. Can you please let us know how to disable the undo button when no data present in undo man...
Issue #1438
Ways to load Html content through reactjs wrapper
@artf Bro, I want to load the html page which contains css and js for effects (parallax). I worked around 10 days but still I cant able to...
Issue #1250
Countdown type "datetime-local" not working in Mozilla Browser
Hi @artf, Hope you are doing fine. Few days back i have asked question from you in reference #750 regarding how to set timezone in countdow...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.