jsdom icon indicating copy to clipboard operation
jsdom copied to clipboard

JSDom can't set and retrieve CSS styles

Open OlofT opened this issue 7 years ago • 3 comments

Basic info:

  • Node.js version: v10
  • jsdom version: v11.10

Minimal reproduction case

As the title says, I can't set css styles, and get them later. It's sad since it means you can't use JSDom to inline your CSS.

At least its easy to reproduce:

const { JSDOM } = require("jsdom");

const options = {
url: "http://localhost/",
		referrer: "http://aggressive.se",
		contentType: "text/html",
		userAgent: "AggressiveDev",
		includeNodeLocations: false,
		resources: "usable",		
		pretendToBeVisual: true,
		runScripts: "dangerously",
};
const dom = new JSDOM(`<body></body>`, options);

window = dom.window
document = window.document;
var styleSheet = document.createElement("style");
styleSheet.setAttribute('type', 'text/css');
var head = document.getElementsByTagName("head")[0];
head.appendChild(styleSheet);
styleSheet = document.styleSheets[document.styleSheets.length - 1];

styleSheet.insertRule(".workingClass {  dontAddThis }", 0);
var rule = styleSheet.cssRules[0];
var style = rule.style;
style.borderColor = "green";

console.log("rule.cssText gets selector but no css");
console.log(rule.cssText);
console.log("should be:\n.workingClass {border-color: green;}");

console.log("but using setProperty works (almost)");
style.setProperty("borderColor", "green")
console.log(rule.cssText);
console.log("cssText is wrong (still js keys)");

How does similar code behave in browsers?

(can be copy/pasted into browser after removing "window = dom.window") or jsbin: https://jsbin.com/sumadul/edit?js,console

OlofT avatar May 09 '18 05:05 OlofT

I have a similar problem, insertRule can't be modified:

const $style = document.createElement('style');
document.head.appendChild($style);
const sheet = $style.sheet!;

const style = {
   color: 'red'
};

const index = sheet.cssRules.length;
sheet.insertRule(`body { }`, index);
const rule = sheet.cssRules[index];
if (rule instanceof CSSStyleRule) {
    Object.assign(rule.style, style);
}
console.log(rule.cssText);
// prints body {}
// should be body { color: red; }

This is open for almost 6 years. Will you accept a PR if I can implement the missing feature of updating rule.cssText?

jcubic avatar Dec 22 '23 19:12 jcubic

I was investigating and it seems that the problem is that when the CSS property is not present in the rule when inserting you can't change it afterwards:

    it('should not introduce CSS and className without debug option', () => {
        const $style = document.createElement('style');
        document.head.appendChild($style);
        const sheet = $style.sheet!;
        const index = sheet.cssRules.length;
        sheet.insertRule(`body { color: blue }`, index);
        const style = {
            color: 'red',
            background: 'green'
        };
        const rule = sheet.cssRules[index];
        console.log({XXXX: rule.cssText});
        console.log(rule);
        if (rule instanceof CSSStyleRule) {
            Object.assign(rule.style, style);
        }
        console.log({YYY: rule.cssText});
        // body { color: red; }
    });

I'm using version 20.0.3 with Jest 29.7.0

jcubic avatar Dec 22 '23 20:12 jcubic

I've found a workaround that works and the good thing is that it will also work with CSS variables as a bonus:

    const style = {
       color: 'red',
       background: 'black'
    };
    if (rule instanceof CSSStyleRule) {
        const _style = rule.style; // this is needed to fix TypeScript bug
        Object.entries(style).forEach(([prop, value]) => {
            _style.setProperty(prop, value as any);
        });
    }

style.setProperty is a method that adds the style properly according to an implementation.

jcubic avatar Dec 23 '23 11:12 jcubic