Progress Indicators

I’ve been working on a questionnaire with a requirement to show progress through the questions. It has thrown up some interesting challenges which I felt were worth sharing.

When I first saw the requirement I thought it would be simple – progress is just number of questions divided by number of answers, easy maths, right? Well, it could be in a very simple questionnaire but ours threw up a few more curved balls. Here are some additional considerations:

Some questions are mandatory, others are optional, so are we counting all questions or only the mandatory ones?

There is some conditional logic so a question may be shown or hidden depending on how you answer another question. How do we handle that?

Some questions can have multiple answers, like a checkbox list, so how many do we count?

Some answers may automatically answer other questions. I know this sounds odd but it’s a big old questionnaire and sometimes it was useful to repeat some questions and answers in a different context in case it changed the answer.

Our architecture has questions and answers as 2 separate API calls, which each bring back object arrays. Questions and answers are linked by a common data point, which is just an identifier so that we can match them up. So, time to work through our extra considerations…

We only want to count mandatory questions. These are denoted by an isRequired property on each question so we can filter to that like this:

const filteredQuestions = questions.filter(q => q.isRequired);

We also only want to count questions that are shown. We can’t reasonably expect a user to answer a question they haven’t seen. This is similar to the isRequired property but using isVisible, so to filter on both of these (instead of just the one property above) we can do this:

const filteredQuestions = questions.filter(q => q.isRequired && q.isVisible);

If questions have multiple answers, it doesn’t matter, we’re just interested in whether or not they have an answer.

Next we need to deal with duplicated questions and/or answers. We can convert our filtered questions and our answers into arrays of unique data points. First we simplify the array to only hold the data point string, then we dedupe it by converting the array to a set and back again.

const questionDataPoints = questions.map(q => q.dataPoint);
const uniqueQuestionDataPoints = [...new Set(questionDataPoints)];

const answerDataPoints = answers.map(a => a.dataPoint);
const uniqueAnswerDataPoints = [...new Set(answerDataPoints)];

Now that we’ve got a list of unique question data points and a list of unique answer data points we only need to see which of our questions have answers, which we can do like this:

const numberOfQuestions = uniqueQuestionDataPoints.length;
const questionsAnswered = uniqueQuestionDataPoints.filter(q => uniqueAnswerDataPoints.includes(q));
const numberOfAnswers = questionsAnswered.length;

These figures can now be used as our value and max attributes or numerator and denominator in a fraction. To give a percentage we can do this:

const percentage = Math.round((numberOfAnswers/numberOfQuestions) * 100);

If we want a normalised figure, a decimal in the range between 0 and 1, we can stick with the simpler fraction calculation like this:

const normalized = numberOfAnswers/numberOfQuestions;

The <progress> HTML element can take max and value attributes or if we don’t specify a max we can simply pass a normalised value.

Hiding UI Elements

At the start of March 2019 I ran a poll on Twitter to see how devs hide UI elements. Here’s the tweet and results.

50% CSS "display:none",5% HTML "hidden" attribute,27% JS framework state,18% Something else

Admittedly, not a very scientific sample size but it was enough to confirm my suspicion that very few people use the “hidden” attribute. I should probably call it a property rather than an attribute as it’s a Boolean and acts in a similar way to “disabled” or “required” which we often use on form elements. And, by contrast, the most common method seems to be to use the CSS “display:none”. Probably no great surprise to anyone.

I think it’s interesting to think about what we’re really doing when we hide an element. Is the visibility of the element just for screen users or for all users? Do we still want the element available for non visual media? Or is the content going to be used by another part of the page in some way? Does it need to be in the DOM at all or are we just keeping it there because it’s easier?

If we use JavaScript, either native or a framework, we can insert and remove elements as needed based on logic. If it’s playing no part in our document why not just leave it out altogether? This keeps the DOM light and performant and easier to work with. If you’re working with something like automated testing you can check for an element’s existence rather than trying to figure out if it’s visible or not.

If we use CSS to hide an element and respect the separation of concerns principle then are we saying that hiding this element is a presentational choice rather than a semantic one? Maybe we are hiding it, maybe temporarily, rather than denying its existence?

In the case where we want an element to be hidden in one medium and shown in another, CSS feels like the right choice. Whilst “display:none” and “visibility:hidden” are valid, other techniques like “height:0”, “opacity:0” or “left:9999px” feel very unclear in their intentions and too focused on the visual user.

The HTML “hidden” property is a more semantic choice. It is stating that this element is not to be considered when reading the document, whether that be on screen or via assistive technologies. It is about more than visibility and is probably badly named. Maybe “ignore” would have been more fitting? It is more about the state of the element and closer in meaning to the input[type=hidden] that we used to see in the days before Ajax.

If you take away the JavaScript and CSS – maybe some network issues, maybe the HTML gets copied elsewhere – the element stays hidden. The point is that it’s meant to be hidden, it’s not styling.

The “hidden” property is also easy to work with and change in JavaScript. In fact, it’s easier than using a CSS class as you don’t have to write any CSS and you know it will work regardless.

<p class="disclaimer" hidden>This is a disclaimer</p>

<script>
  const disclaimer = document.querySelector('.disclaimer');
  function showDisclaimer() {
    // with a CSS class
    disclaimer.classList.add('show');
    // with the hidden property
    disclaimer.hidden = false;
  }
</script>

Think about why your content is hidden and how it may be reused and choose the most appropriate method of hiding it (or removing it).

Making Icons Accessible

I’ve been using the excellent Font Awesome for a few years and it’s become part of almost every new design that I do. For a long time I’ve just slapped my icon down on the page, styled it and added a click event where needed. Job done.

Having spent some time looking a bit more deeply into accessibility, I can now see that this is not a great approach. There’s more to consider. If you can’t see it what is it actually doing?

Here’s how I approach using icons now. It may not be the perfect solution but it’s certainly a lot closer than most use cases I see. The way I use HTML depends on the use case.

If the icon is purely decorative – there in addition to text, e.g. a floppy disk icon next to the word “Save” on a button, then I think we can reasonably just forget about it and hide it for non screen users. A simple aria-hidden=”true” attribute should do.

<button><i class="fa fa-save" aria-hidden="true"></i> Save</button>

If it is there as an indicator, in place of a label, like where a hotel might have icons for TV, bath, etc. then we can use an <span> to wrap it. Inside the span we add our icon and another span with a text alternative. We show the icon and hide the inner span for screens, show the text only for other media.

Finally, if the icon is interactive and acts like a link or button, let’s make it a link or button. Like the previous example, use the <button> or <a> tag to wrap the icon and a span text alternative and show/hide for the appropriate media. This has the added bonus of making it possible to tab to the icon and so use just the keyboard to navigate the page. It also handles the icon’s active state and visited in the case of a link.

Alternatively, if you didn’t want to add a span with text inside your button or link, you could use the aria-label attribute to provide a text alternative.

One little tip. If you use an icon inside a link or button, do not leave whitespace between the outer wrapper element and the inner icon as you may get unwanted effects in the styling, such as underlined spaces either side of your icon. Keep it tight.

To DOM or Not to DOM?

I’m trying to work out when it’s best to put attributes in the DOM and when it’s best to just leave them in JavaScript objects. This is going to be hard to explain so bear with me.

Let’s suppose we have a list of users on a page. Each user has a number of properties, not things we’d display like name, age and gender but hidden things like permissions, e.g. isAdmin, canComment, isManager, etc. These properties are going to be used in logic to determine how things are displayed on screen – maybe an extra bit of detail or an icon. Just as an example, if canComment is true then a speech bubble icon will appear.

The basic HTML might look something like this:

<ul>
  <li id="001" class="user">John</li>
  <li id="002" class="user">Paul</li>
  <li id="003" class="user">George</li>
</ul>

Behind this is a JavaScript object, like this:

var users = [
  {
    id: '001',
    name: 'John',
    canComment: true
  },
  {
    id: '002',
    name: 'Paul',
    canComment: false
  },
  {
    id: '003',
    name: 'George',
    canComment: false
  }
];

So, user John is allowed to comment and we want to show an icon. There are 2 approaches:

  1. Use the DOM

    We could add the icon inside each <li> and then add a class (class=”user can-edit”) or a data-attribute (data-can-edit) to the <li> and use CSS to display it as appropriate, e.g. .user[data-can-edit] .icon { display: inline-block }.

  2. Use JavaScript

    Or, we could write a JavaScript function to filter the objects to just those with canComment set to true, get their ids, and then loop through the list items adding the icon HTML when the ids match the filtered list.

The first approach puts the property into the DOM, puts the icon into the DOM and then uses CSS to show/hide it. It feels easy. The icon is always sat there waiting, like an unchecked checkbox, only shown when needed (checked). There is still logic but it’s done up front. The DOM is actually human readable and it’s easy to see which users can and can’t edit for debugging purposes. I see this approach used a lot, including the hidden DOM elements, usually because of the way templates or components are used. It’s easier to include it and pass a property to toggle whether or not it displays. It also means that if we want to change the property during the lifecycle of the page, e.g. take away John’s commenting permission, it’s minimal change to the DOM.

The second approach only touches the DOM to insert the icon where needed. It does all the logic in JavaScript and the output in the DOM reflects what the user actually sees on screen. There are no hidden elements. This has advantages when it’s scaled up, in that it keeps the DOM lighter and there’s no waste. The JavaScript is incredibly fast in modern browsers so the functions won’t slow anything down, especially if using .filter(), map(), etc. The trick here is not to loop through and add the icons one at a time but to run through building the whole list and just doing the one insert or replace operation. This makes the DOM less readable as all the canComment data is in JavaScript.

So, is it better to use the DOM for data or keep it in JavaScript?

 

Learning the Right Things

With so much happening so fast knowing what new tech to learn next can be difficult. Where should you invest your time and energy? What if you spend ages learning the shiny new framework and it’s gone within 2 years?

There are a lot of trends and a lot of new-fangled things do come and go. How do you spot the ones that are going to stick around. It’s not easy – you kind of have to go with the flow, not swim against the tide (2 water based metaphors in one sentence, tut).

In front end development there are currently a few big JavaScript frameworks – Angular, React and Vue. It seems obvious to me that none of these are going away any time soon. Which do you choose? It really doesn’t matter. Learning one in depth, finding its powers and limitations, will help you learn the others or other future technologies.

If you’re really not sure where to turn I’d focus on core learning. In the front-end world, learn plain JavaScript. With ES2015 and newer versions than that emerging there’s still plenty to take on and perfect. That knowledge will always be useful.

I guess my short answer would be to favour the evergreen HTML, CSS and JavaScript over associated tech like Pug, Sass, TypeScript, etc. As the core technologies get better over time these current convenience technologies may one day not be needed.

One word answer. JavaScript. :)