The Dangers of Logic in CSS

We can do a lot of logic in CSS. I’m thinking specifically of scenarios which take the form “if this, then that”.

There are media queries which will let us control the appearance of elements based on a variety of factors. Probably the most common is screen width, so we get things like “if the width is 1024px or higher, then show an extra element”.

There are pseudo selectors like :empty, :checked, :valid, :disabled which let us control appearance based on state, giving us, for example, “if the parent element has no children (:empty), then change its opacity”.

We can also do logic based on combinations of elements or selectors, like li + li, for example, “if the list item follows another list item, then give it a top border.”

I’m sure there are plenty more ways of applying logic but you get the idea. Each of these is equivalent to writing a conditional statement in a programming language – if this, then that.

The big question is, should we be doing this in CSS, and is there a point at which it becomes too difficult to manage in CSS and we need to defer to another technology, like JavaScript?

My Experience

I started thinking about this when I was designing a UI with a lot of logic going on. We had a busy table where we show some additional columns at wider screen resolutions. At smaller widths the user would be able to toggle between different groups of columns and save a preference. But then these additional columns are only offered if they contain data. So, we had logic around the screen width, user preference and presence of data, which, multiplied out, meant 8 permutations.

I initially tried to do it all with CSS. I set up media queries for the narrower and wider screen widths. Within each of these I then read a class for the user preference and then I used :empty to see if table cells contained any data. I managed it and it worked. Just.

However, I realised that it was really not scalable. Every time I added a new condition it doubled the amount of CSS I was having to write. If we wanted to add more logic in future it would create more and more work and be harder and harder to maintain.

By moving all the logic into JavaScript we can set simpler classes, one for each type of display we need rather than every permutation of logic. So now we just have 3 classes: 1) show the first set of columns, 2) show the second set or 3) show all columns.

We still have lots of branches in our JavaScript but the big difference is that it’s easy to read and follow. It’s also easy to edit or extend if we ever need to.

I think doing some logic with CSS is fine but as soon as you’re starting to layer it on and find yourself multiplying out the selectors you’re writing it’s probably time to move it over to JavaScript.

Clicking Off Things to Close Them

There’s a common design pattern these days where a bit of content is shown over the main screen. It might be a modal window or maybe a fly-in menu or sidebar. It’s also common, especially in native apps, that we can just click or tap in the space not used by these elements to close them. So, how can we do this?

I think there are 2 approaches, one which is pure JavaScript and another which involves an overlay element. Let’s look at both.

JavaScript Document Click Event Method

We can detect that a click is not on the content element using JavaScript. As all click events bubble up or propagate through the DOM tree they eventually reach the top document level. We can listen for a click on the document and then use the event target to check what was clicked. For this example let’s assume our content element has a class of ‘modal’, like <div class=”modal”></div>.

// JavaScript

function handleClick(event) {
  if (!event.target.closest('.modal')) {
    console.log('close');
  }
}

document.addEventListener('click', handleClick);

event.target gets the element that was clicked. This could be the modal element itself, another element inside it or another element outside of it. event.target.closest(‘.modal’) checks if the element clicked is the modal or is an antecedent of the modal element, an element within it. If the element clicked is not (!) in the modal we can close it.

It’s maybe worth noting that .closest() doesn’t work in IE11 if you need to support this but there is a polyfill available.

Using an Overlay Element

The other approach, and the one I tend to use, is to use an overlay element. This means adding an element that covers the whole screen area, slipped in between the modal and the main screen content. The idea is that this overlay will appear and disappear along with the modal and will pick up any click events which are not on the modal. It’s a bit like a safety net that will catch any stray clicks.

The CSS for the overlay would typically look something like this:

/* CSS */

.overlay {
  background-color:#000;
  bottom:0;
  left:0;
  opacity:.5;
  position:fixed;
  right:0;
  top:0;
  z-index:1;
}

.modal {
  /* styling */
  z-index:2;
}

The overlay has fixed display and goes to each edge so it fills the screen.

The overlay has z-index set to 1 so it’s over the main screen content, and the modal has it set to 2 so it’s over the overlay. These can increase as needed but the modal always needs to be higher.

I’ve given the overlay a background colour and 50% (.5) opacity so that it veils the main screen content underneath giving the modal a “lightbox” effect but these are not needed and our overlay can be effectively invisible.

You then just add a click event to the overlay which will close the modal and hide the overlay.

The reason I like this approach is that by covering the main screen we prevent any interactions with it. If the user tries to click on a button under the overlay their first click will just close the modal. We don’t need to worry about any other interactions – while the modal is showing it becomes the sole focus.

Extra tip. If you use a visible overlay it’s nicer not to show and hide it immediately but fade it in and out with a transition on the opacity, taking it between 0 and .5. This feels much smoother and less jerky.

Why Are We Making Web Development So Hard?

I don’t think I’m the first person to think this by any stretch. There are lots of infographics around showing the difference between web tooling now and 10 years ago and it’s clearly ballooned at an extraordinary rate.

In the last year or so I’ve started working with Angular (as opposed to Angular.js a.k.a. Angular 1) and have also dabbled in Vue and React. All of these frameworks seem to share the same basic ideas – build everything as a component and push and pull them from the DOM as needed. Each component is made up of a HTML template, embedded CSS styles and a JavaScript controller.

This feels like an efficient way of working, very DRY. But is it? You create a component, let’s say a time picker. One it’s developed you can put it into multiple views or uses relatively quickly. However, if you later modify it to work slightly differently, using adding complexity, you have to test it in each use case and make sure it still works with no detrimental effects.

All UIs are basically built up of HTML, CSS and JavaScript. Traditionally HTML is for content, CSS for styling and JavaScript for reacting to user behaviour. With components we’re adding a whole load of JavaScript just to make the basic elements appear before we even get into any behavioural usage. This adds to page weight (size of files being transferred to the browser) and render times. There’s the library itself, often split into many smaller modules, plus a controller for the UI to assemble it and then the components. It could just be one HTML file, one CSS file and one JavaScript file.

Increasingly, what the developer writes in their editor is becoming further and further from what actually appears in the DOM. The frameworks insert a whole host of IDs and classes to elements, bloating the HTML and making it harder to read. Styles are inserted as inline <style> blocks rather than being cached in an external stylesheet. Opening the browser’s Dev Tools to track down and fix a problem is becoming harder, not easier.

Also, the more tools you use, the more you’ve got to maintain, keep up to date, upgrade and replace. It’s creating work just to keep up and inviting technical debt.

I think that what we’re gaining in terms of being able to develop new UI more quickly is all rooted in developer efficiency and production cost saving. It’s not thinking about the user. We’re making things slower for users. The mobile experience on most sites is still awful and the overhead of these frameworks is partly to blame.

The latest frameworks also rely on good browsers with all the JavaScript features. They do not use progressive enhancement but just grind to a halt on an older browser. This goes against the universal nature of the web – it should be easy to read and easy to write. The bells and whistles on the top is just that and should be optional, not prevent the basics from working.

I think we need to get back to making things simple. Write HTML, CSS and as little JavaScript as we can get away with. Go static wherever possible. Make it fast and simple for users. User experience should trump developer experience.

Super Fast Tree View in JavaScript

I thought I’d share a way of writing your JavaScript that will make a tree view hierarchy render and respond very quickly.

Firstly, the demo. I’ve got 100,000 items, quite a hefty data set, which is randomly generated in a function. You can change the number of items easily in the JS area. Click on the + or – icons to expand or collapse nodes.

See the Pen Super Fast JavaScript Data Tree by Chris Smith (@chris22smith) on CodePen.dark

It’s still quick, like instant, with 100,000 items. It can start to slow a little at a million but I’d say it’s still acceptable given that kind of data.

One Long List

The trick is in how the data is structured. It’s one big array, not hierarchical data with objects inside other objects. The reason for this is pretty simple, it’s all about iterations or looping. If you have a single array you can run through the whole list of items once. If it’s nested you have to get into loops inside other loops and it gets more complicated and takes longer. In fact, you’d be lucky for it to work at all without getting stack overflow errors.

JavaScript Magic

With a single array you can use the built in JavaScript Array methods, which are very fast – map(), filter() and some(). I’d definitely recommend reading up on these. All three work in IE9+ so hopefully you shouldn’t have to worry too much about browser support.

I use map() to convert each data item into a HTML list item which I can insert into the DOM. I use filter() to quickly find the children items of any item by returning a subset, and I use some() to see if an item has children. The beauty of some() is that once its found it child it stops iterating, saving time.

Easy on the DOM

The other part of this is keeping DOM manipulation to a minimum. Playing around with data in memory trivial for a browser but making changes to the DOM and rendering things on screen takes time. So, only the nodes that are needed right now exist in the DOM – there are not hidden items waiting on the sidelines. So, a new <ul> is added when you expand a node and the <ul> is removed when you collapse. A + icon is shown if the item has children of its own but we don’t know anything about how many it has or what they contain until it’s expanded.

More…

If you found this useful it might be worth looking at my older post Lightning Fast Filtering in JavaScript, which uses a lot of the same principles.

Different Conditional Syntaxes in JavaScript

There are a few ways of using conditionals in JavaScript. I’ve started thinking about it again as I’ve just discovered another new way.

Lets start off with some basic set up stuff for our examples – a variable, which can be true or false and functions to use in either case:

var isAllowed = false;

function admit() {
  console.log("admitted");
}

function reject() {
  console.log("rejected");
}

The first way I learned to write a conditional, which I consider the classic way, is with if and else clauses like this:

if (isAllowed) {
  admit();
} else {
  reject();
}

If you don’t need the else part, you can do it in one line, leaving out the curly braces like this:

if (isAllowed) admit();

Another way of writing it is to use a ternary operator. The first part before the question mark is evaluated. If it’s true the next part between the question mark and colon is used; if false the last part after the colon is used:

isAllowed ? admit() : reject();

If you want this style of expression, without actually writing “if” but you don’t want the else part there is yet another way.

Look at this:

!isAllowed || admit();

Weird, eh? With an OR expression using ||, the first part is evaluated or run if a function. If true it stops there. If false it evaluates or runs the second part. In case you’re new to this, the ! means NOT or inverts the meaning. So, we’re saying if isAllowed is false, stop; otherwise run the admit() function.

It’s the same technique often used to set default values, just turned around:

var carModel = getCarModel() || 'unknown';

If you’re free to use any of these I’d probably advise sticking to the classic if (else) with the braces. It’s so much more readable and can be understood by a developer at any level. I like the last technique but I’d only use it in solo projects or where I can’t use anything else.