Playing Around with DOM Styles
Tags: javascript, webdev, tutorial, beginners
Published: 2022-01-28 15:42:17

If you've been at the front-end dev thing for a while, and you've learned all you need to know about manipulating the DOM and clever javascript, this is not for you. If you're brand-new, and you're learning that we *can* manipulate the DOM with javascript, this will *eventually*  be for you, but it may be too much right now.

This is directed to a very particular set of learners: when we start tinkering with the DOM, and we find ourselves doing the same thing over and over again, we should consider how we can automate a solution to that. Any time we are repeating ourselves in code, it's a candidate worth refactoring. This is sort of an exploration of that refactoring thought process.

## The Problem
When going through an online curriculum and learning about "DOM manipulation with javascript", you'll often see styles being set inline. At one point, it was considered bad form to apply styles inline, but that's a different animal: the reference is saying "don't apply styles directly in your HTML." For good reason: inline styles are the highest priority in the CSS realm. When we apply styles in the HTML, and we then move on to writing a stylesheet, we *can't* override those inline styles without resorting to `!important`. And, if we find ourselves using the `!important` style rule? We're doing something wrong.

But there *is* quite a lot of usefulness to setting styles inline, via *javascript*. We can do so in a few ways:
1. We could toggle a css class on a particular DOM node on or off, setting the styles for that element as we like (so long as that CSS selector rule has been defined),
2. We could directly tinker with the `domNode.style` attribute, setting styles inline.

That second one is what this is all about!

## Option 1

There are times we want to be able to style DOM nodes, inline, with javascript. And that is fairly easily done. We can simply set all the styles we like, directly on the node, like this:

```js
document.querySelector("#myNode").style = "background-color:red; color: aliceblue; font-size: 1.75em;";
```

And with that line, we set the styles for the element. Works great. Except that, if we *have* existing styles on the element, we have *replaced* the styles. By setting the style directly like that, we have effectively said "Whatever you had in the `style` before? Toss it. Here's the new style." 

So if there were styles for padding, or border, or line-spacing... they have been completely replaced. Let's try someting similar, but a little different.

## Option 2

Rather than setting the entire `style`, we can set attributes *of* that style. With this, the only thing being *replaced* is the particular attribute we are updating:

```js
const myNode = document.querySelector("#myNode");
myNode.style.backgroundColor = 'red';
myNode.style.color = 'aliceblue';
myNode.style.fontSize = '1.75em';
```

This also works. We are setting each style as we like, we aren't blowing the entire style property away. So this is *better*, but we are far from *best*.

Since we're trying to find a way to optimize or automate that, and the core javascript system might not include the functionality we want built-in, it might be a great place to consider writing one or more functions. Suppose we wanted a function that simply lets us pass in a DOM node and a color, and updates the `style.color` for us:

```js
const setColor = (domNode, color)=> domNode.style.color = color;
```

Seems a little silly like that. We wrote a function in three times the characters, to do what we could simply do inline. Still, it is an automated process. Let's make that a little more functional. What if we add a parameter in there, call it `styleProp`? That might be the style property name. Given the DOM node, the style property, and the CSS value, we can set that directly:

```js
const setCSSProperty = (domNode, styleProp, value) => domNode.style[styleProp] = value;
// and how it might get used:
const myNode = document.querySelector('#myNode');
setCSSProperty(myNode, "backgroundColor", "red");
setCSSProperty(myNode, "color", "aliceblue");
setCCSSProperty(myNode, "fontSize", "1.75em");
```

It's more generic, maybe, but it's still longer than just editing the style properties ourselves. How is this saving us time?

It isn't any shorter to type, maybe, but it is a little more abstract. We can now automate that, if we want. For my own peace of mind, and because I like *currying*, let's rewrite that last one as a curried function:

```js
const setCSSOn = (domNode) =>
  (styleProp) =>
    (value) => domNode.style[styleProp] = value;
// now, we can do this:
const myNode = document.querySelector("#myNode");
const styleMyNode = setCSSOn(myNode);
styleMyNode("backgroundColor")("red");
styleMyNode("color")("aliceblue");
styleMyNode("fontSize")("1.75em")
```

A curried function is useful in a case like this - note how we only pass in *which node* we want to style the first time? We have created a **closure** in that outer function, which stores the value `domNode` locally and reuses that each time we call `styleMyNode`. 

I like this, this is a good direction. But it would be nice to not have to do each line like that, it's kind of ugly and not really *saving* us anything. So far, there is no benefit. But that last function, `setCSSOn()`? We want to keep that in mind. It is going to be in the sequel. 

## Option 3

So we have a way of setting a style on a node, functionally. That's great. But what if, like in that last one, we want to set a bunch of styles? Our function is useful, but not much more than simply setting the styles directly. We still haven't *gained* much.

Ah, Grasshopper, we're nearly there. What if we had a way to do this:

```js
const myNode = document.querySelector("#myNode");
const styleMyNode= setStylesOn(myNode);

styleMyNode({
  backgroundColor: 'red',
  color: 'aliceblue',
  fontSize: '1.75em'
});
```

That starts to look pretty darn juicy, doesn't it? It is clean, it says exactly what we're trying to do, it is very functional, it can be automated... and let's face it: it's darn pretty. So how do we get there?

Let's look at what it is we want to do, given the *function signature*. We show a function, `styleMyNode`, that takes a DOM node, just like the `setCSSOn` function we just wrote. But then, rather than taking a property name and value, we have passed in an object. That object contains the style names in javascript format (so camelCase, rather than CSS's kebab-case), as keys to the data.

So we want to go through that object, pull each "key/value" pair out, and apply it to that DOM node. Seems pretty straightforward when we talk it through like that, huh? Let's try:

```js
const styleMyNode = (domNode) =>
  (styleObject) => {
  Object.entries(styleObject).forEach( ([key, value])=>{
    setCSSOn(domNode)(key)(value) 
  })
}
```

so `Object.entries` takes an object, and returns an array of `[key, value]` pairs. In the case of our passed `styleObject` in the example, it looks like:

```js
[
  ['backgroundColor', 'red'],
  ['color', 'aliceblue'],
  ['fontSize', '1.75em']
]
```

Then we `forEach` over that outer array, giving us each of those pairs in turn. Within that `forEach`, we *destructure* the array pair into a `key` and a `value` variable - which we then pass into our handy-dandy `setCSSOn` function.

A nice way to use something like this might be if we need to apply similar styles to all nodes of a given class:

```js
const myStyles = {
  border: "1px solid silver",
  borderRadius: "10px",
  margin: "5px auto",
  padding: ".25rem .5rem 0 .75rem"
};

const myCells = document.querySelectorAll(".cell");

myCells.forEach((cell)=>styleMyNode(cell)(myStyles));
```

Now, that *is* cleaner - we can see at a glance what we're doing. For each element in `myCells`, we are calling `styleMyNode` and applying that style object we created. Wonderful!

Of course, not entirely what we like. Ideally, we might have been able to just call `myCells.forEach(applyMyStyles)` and not define that function inline. The problem we have is that we arranged the curried parameters in a particular order. Nothing wrong with that, until we see it in use.

As a rule, it's useful to arrange curried parameters from those less likely to change (the "static parameters") to those more likely to change (the "dynamic parameters"). In this case, as each element is getting the same style object, the static parameter is that style object - because each time we call the function, we want the *same* style object, but a *different* DOM node.

In order to fix this, in order to use this as a passable function, we need simply reorder the parameters:

```js
const applyStylePair = ((domNode)=>
						([key, value])=> setCssOn(domNode)(key)(value))

const setStyles = (styleObject) =>
  (domNode) =>Object.entries(styleObject).forEach(applyStylePair(domNode))

// with that, we can now:
const applyMyStyles = setStyles(myStyles);

const myCells = document.querySelectorAll(".cell");

// now, when we pass the applyMyStyles function, it already
//  encloses that `myStyles` reference. We simply tell each
//  member of `myCells` to run that function, on that cell.
myCells.forEach(applyMyStyles)
```

## Conclusion

Yes, it seems like work. Have we *gained* anything for all this? Well, sort of. Hopefully, we gained some understanding of ways to tinker with the DOM, and with objects in general. We have had a bit of a thought experiment, we've seen some impact regarding the order of parameters, and we've had a bit of fun.

Is this something you'll use every day? Not likely. Being able to apply multiple styles to multiple DOM nodes is fun and educational, but practically speaking, there may be other and better ways up this mountain. Tools like React's styled components do this same thing, and we often use them without necessarily thinking about what that *represents*.