I want to dynamically style all elements of a given selector in my DOM. I see more or less two ways about it. For the example below I'll use a p
element and it's text-align
attribute but I'm more interested in the pros and cons of the two possible ways of doing this than I am in specifically text-aligning paragraphs.
var nodes = document.getElementsByTagName('p');
Array.prototype.forEach.call (nodes, function (node) {
node.style.textAlign = "center";
});
var sheet = (function() {
// Create the <style> tag
var style = document.createElement("style");
// WebKit hack :(
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
return style.sheet;
})();
sheet.insertRule("p { text-align: center; }");
Typically I would go the route of inline styles, as it seems simpler and it ensures the style change would override the existing style sheets. But it occurs to me that for one: sometimes not overriding the style sheets might be preferable, and for two: it might be more performant to modify one style
element than an unknown quantity of p
elements. But that's just my assumption.
Performance wise, would there ever be a situation where applying inline styles to each individual element would be better than creating a style sheet? Assuming the answer might be dependent on how many elements I am styling, at one point does creating a style sheet become more efficient?
EDIT: To clarify why I'm asking the question, I'll explain a little about why I'm asking: I've recently turned a handful of JS hacks I've often copy-pasted and adapted between projects into a set of reusable CommonJS modules. They do things like setting all elements of a given selector the same height or width as the tallest or widest of the set in situations where the measure of the tallest or widest might be subject to change on a window resize or other triggers.
Here is a blog post about it: http://davejtoews.com/blog/post/javascript-layout-hacks
Here are the GitHub repos for the modules:
At this point, all these modules use inline styles, but I am thinking of switching them to stylesheets. I couldn't find a good answer about the pros and cons of either approach so I posted the question here.
I don't have a good general answer to your question (and I'm not sure there is one), but I've run some experiments with the example you posted on Chrome 57.0.2987.98 beta, using the dev tools timeline.
Here is a breakdown of the work done in the single frame which updates 10,000 <p>
elements with inline styles (left) and a dynamic stylesheet (right):
For comparison, here are the results for the same test with 100 <p>
elements:
In summary, for a small number of elements, the difference is negligible. For a large number of elements, when using inline styles, the browser spends more time scripting (which is to be expected, since there is a heavy loop) and recalculating styles (I think this can be explained by the browser having to parse one style rule per-element rather than a single common style rule).
All other steps take approximately the same amount of time. In particular, the paint time does not increase when using inline styles.
For reference, I had used the following test page.
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="add-stylesheet">Stylesheet</button>
<button id="add-inline-styles">Inline</button>
<script type="text/javascript">
let stylesheet = document.getElementById('add-stylesheet');
let inline = document.getElementById('add-inline-styles');
stylesheet.addEventListener('click', addStylesheet);
inline.addEventListener('click', addInlineStyles);
function addStylesheet() {
let style = document.createElement("style");
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
style.sheet.insertRule('p { text-align: center }', 0);
}
function addInlineStyles() {
let nodes = document.getElementsByTagName('p');
for (let node of nodes) {
node.style.textAlign = 'center';
}
}
// initialize <p> elements
init(10000);
function init(numElements) {
for (let i = 0; i < numElements; ++i) {
let p = document.createElement('p');
p.innerText = 'testing'
document.body.appendChild(p);
}
}
</script>
</html>
There has been a lot of debate on this subject in the past couple years since React and JSX have gained enormous popularity.
I've tried a few solutions so I'll list them out here. First a general discussion..
CSS is basically the only language that advocates using a global namespace, and this is the number 1 reason people are moving away from straight CSS and heavy overarching frameworks. With flexbox, responsive layouts can be done in several lines of codes rather than an entire grid system such as you'd get with something like bootstrap.
CSS solved the issue of providing styles to documents in a reusable way, but as applications got more huge and more complex, and more 3rd party libraries with their own CSS was included, the chance for global namespace collisions became almost unavoidable. So much so that a few different patterns were authored and advocated for, such as BEM and SMACSS.
The react came along which made managing and creating reusable inline styles relatively straightforward. You could use all of javascript, which meant you could override styles using things like _.extend
or Object.assign
. This made for easy to share modules and packages which included components and their styles, and had the benefit of not requiring any sort of style loader, such as what is required when using webpack
. It wasn't all roses, though. Things like :hover
, other psuedo-selectors, and media queries are not supported in plain inline styles.
To get around these limitations, developers implemented events to trigger style changes, such as onmouseover
for hover, and hooking into the window resize events for media queries. Soon thereafter, a library to standardize these js events, and define a CSS like API was created and gained popularity, called Radium
. In the wild, Radium did not fair as well (believe me I tried). In large apps, none of the media queries could execute until all of the JS had been downloaded, which I don't recommend!
That led to the creation of a few new tools that take a different approach. These new generation of tools use styles defined in JS, but generate CSS. This gives you the full power of inline styles and the full power of CSS. Best of both worlds. Which library is the best probably depends on your use case, but those consist of Fela.js, Aphrodite, and JSS.
My favorite solution is Fela.js. Check it out at fela.js.org. Fela is probably the best performance you are going to get, and it is not specific to any particular framework. That being said it works well with React and React Native. It has a couple of neat features such as allowing you to access props from your styles. You set up a renderer
in the head of the page. Fela works best with SSR, but also works purely client side depending on your needs. When using with SSR you can get blazing fast page loads because Fela optimizes the styles you've written into atomic css classes, and they are sent on back to the client on the initial request.
These tools are amazing if you are concerned about getting the fastest page possible. You can easily accomplish difficult patterns such as critical path css optimization, where the necessary styles are returned as part of the initial HTTP request, rather than a link to an external sheet that also has to be downloaded.
Lastly, I have to mention CSS modules. These do create an external stylesheet, but allow you to still have namespaced CSS per module. This allows you to write real CSS or SASS or etc, but comes with additional setup overhead. In webpack for example, you'd need to use a combination of fake style loaders and text extraction tools to create the .css file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With