us

Linking local microstructure to fracture location in a two-dimensional amorphous solid under isotropic strain

Soft Matter, 2024, Advance Article
DOI: 10.1039/D4SM00486H, Paper
Open Access
  This article is licensed under a Creative Commons Attribution 3.0 Unported Licence.
Max Huisman, Axel Huerre, Saikat Saha, John C. Crocker, Valeria Garbin
Machine learning predicts possible crack location in experiments of a fracturing colloid monolayer, shown along with the actual crack path.
To cite this article before page numbers are assigned, use the DOI form of citation above.
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Enhanced gravitational trapping of bottom-heavy Janus particles over parallel microgrooves

Soft Matter, 2024, Accepted Manuscript
DOI: 10.1039/D4SM00989D, Paper
Yan Wen, Jiayu Liu, Wei Wang, Pik-Yin Lai, Penger Tong
We report a systematic study of barrier-crossing dynamics of bottom-heavy self-propelled particles (SPPs) over a one-dimensional periodic potential landscape $U_0(x)$, which is fabricated on a microgroove-patterned polydimethylsiloxane (PDMS) substrate. From...
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Revealing microscale bulk structures in polymer–carbon nanocomposites using spin-echo SANS

Soft Matter, 2024, 20,8663-8674
DOI: 10.1039/D4SM00578C, Paper
Open Access
  This article is licensed under a Creative Commons Attribution 3.0 Unported Licence.
L. V. Tiihonen, M. P. Weir, A. J. Parnell, S. C. Boothroyd, D. W. Johnson, R. M. Dalgliesh, M. Bleuel, C. P. Duif, W. G. Bouwman, R. L. Thompson, K. S. Coleman, N. Clarke, W. A. Hamilton, A. L. Washington, S. R. Parnell
Spin-echo small-angle neutron scattering (SESANS) revealed structure in polymer–carbon nanocomposites over multiple length scales with unprecedented range (10 nm–16 μm). Data from two SESANS instruments and other methods were analysed with SasView.
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Confined bicontinuous microemulsions: nanoscale dynamics of the surfactant film

Soft Matter, 2024, 20,8692-8701
DOI: 10.1039/D4SM00925H, Paper
Open Access
  This article is licensed under a Creative Commons Attribution 3.0 Unported Licence.
Margarethe Dahl, Olaf Holderer, René Haverkamp, Ingo Hoffmann, Kathleen Wood, Jessica Hübner, Thomas Hellweg, Stefan Wellert
A confined bicontinuous C10E4–D2O–n-octane microemulsion is studied using neutron spin echo spectroscopy (NSE). The pore size of the confining matrices determines the dynamics of the confined bicontinuous microemulsion.
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Bending of polymer films: a method for obtaining a compressive modulus of thin films

Soft Matter, 2024, 20,8589-8600
DOI: 10.1039/D4SM00084F, Paper
Akihiro Ohara, Ko Okumura
We constructed a theory and method for measuring the compressive modulus by combining tensile and bending tests. Elastic asymmetry was confirmed in an industrial PET film.
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Understanding Polymer-Colloid Gels: A Solvent Perspective Using Low-Field NMR

Soft Matter, 2024, Accepted Manuscript
DOI: 10.1039/D4SM01098A, Communication
Léo Hervéou, Gauthier Legrand, Thibaut Divoux, Guilhem Pablo Baeza
The present work emphasizes the relevance of low-field NMR relaxometry to investigate colloid-polymer hydrogels by probing water dynamics across a wide range of formulations between 10°C and 80°C. By examining...
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Interfacial viscoelasticity in oscillating drops of cyclodextrin-surfactant aqueous solution: experiments and theory

Soft Matter, 2024, Accepted Manuscript
DOI: 10.1039/D4SM01007H, Paper
José Roberto Romero-Arias, Alberto S. Luviano, Miguel Costas, Aurora Hernandez-Machado, Rafael A Barrio
We present experiments involving oscillating droplets in aqueous cyclodextrin-surfactant solutions. In these experiments, α-cyclodextrin (αCD) and anionic surfactants exhibit remarkable viscoelasticity at the liquid/air interface, with dilatational modulus varying across...
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Fibrotaxis: gradient-free, spontaneous and controllable droplet motion on soft solids

Soft Matter, 2024, Accepted Manuscript
DOI: 10.1039/D4SM01022A, Paper
Open Access
Sthavishtha Bhopalam, Jesus Bueno, Hector Gomez
Most passive droplet transport strategies rely on spatial variations of material properties to drive droplet motion, leading to gradient-based mechanisms with intrinsic length scales that limit the droplet velocity or...
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Obstacle-enhanced spontaneous oscillation of confined active granules

Soft Matter, 2024, Advance Article
DOI: 10.1039/D4SM01027B, Paper
Xue Zhang, Yuxin Tian, Ran Ni, Yong Zhu, Luhui Ning, Peng Liu, Mingcheng Yang, Ning Zheng
Obstacle-enhanced spontaneous oscillation of confined active granules. (a) Without obstacles. (b) With obstacles.
To cite this article before page numbers are assigned, use the DOI form of citation above.
The content of this RSS Feed (c) The Royal Society of Chemistry




us

A generalized model for predicting different morphologies of bacterial swarming on a porous solid surface

Soft Matter, 2024, Accepted Manuscript
DOI: 10.1039/D4SM01072H, Paper
Uttam Kumar, Pushpavanam Subramaniam
In this study, we develop a comprehensive two-phase model to analyze the dynamics of bacterial swarming on porous substrates. The two distinct phases under consideration are the cell and aqueous...
The content of this RSS Feed (c) The Royal Society of Chemistry




us

Create Neon Style Buttons Using CSS

CSS truly is a remarkable tool in a web designer’s toolkit, capable of bringing even the most vibrant creative visions to life. Today, we’re immersing ourselves in the radiant world of neon style buttons, showcasing the impressive spectrum of CSS …




us

How to Animate Gradient Text Using CSS

Web design takes a captivating turn when CSS comes into play. It enables a world of transformations, such as taking static text elements and infusing them with life. Our focus today is one such engaging transformation – animate gradient text …




us

Ripple Button Effect Using Pure CSS

Google’s Material Design guidelines introduced the ripple effect, a subtle animation that indicates user action. The ripple effect rapidly gained popularity in web design as a sophisticated visual feedback form that refines user interaction, particularly on buttons. Today, we’ll show …




us

Making a Better Custom Select Element

Julie Grundy kicks off this, our fifteenth year, by diving headlong into the snowy issue of customising form inputs. Nothing makes a more special gift at Christmas that something you’ve designed and customised yourself. But can it be done while staying accessible to every user?


In my work as an accessibility consultant, there are some frequent problems I find on people’s websites. One that’s come up a lot recently is that people are making custom select inputs for their forms. I can tell that people are trying to make them accessible, because they’ve added ARIA attributes or visually-hidden instructions for screen reader users. Sometimes they use a plugin which claims to be accessible. And this is great, I love that folks want to do the right thing! But so far I’ve never come across a custom select input which actually meets all of the WCAG AA criteria.

Often I recommend to people that they use the native HTML select element instead. Yes, they’re super ugly, but as Scott Jehl shows us in his article Styling a Select Like It’s 2019 they are a lot easier to style than they used to be. They come with a lot of accessibility for free – they’re recognised and announced clearly by all screen reader software, they work reliably and predictably with keyboards and touch, and they look good in high contrast themes.

But sometimes, I can’t recommend the select input as a replacement. We want a way for someone to choose an item from a list of options, but it’s more complicated than just that. We want autocomplete options. We want to put images in there, not just text. The optgroup element is ugly, hard to style, and not announced by screen readers. The focus styles are low contrast. I had high hopes for the datalist element, but although it works well with screen readers, it’s no good for people with low vision who zoom or use high contrast themes.

Figure 1: a datalist zoomed in by 300%

Select inputs are limited in a lot of ways. They’re frustrating to work with when you have something which looks almost like what you want, but is too restricted to be useful. We know we can do better, so we make our own.

Let’s work out how to do that while keeping all the accessibility features of the original.

Semantic HTML

We’ll start with a solid, semantic HTML base. A select input is essentially a text input which restricts the possible answers, so let’s make a standard input.

<label for="custom-select">User Type</label>
<input type="text" id="custom-select">

Then we need to show everyone who can see that there are options available, so let’s add an image with an arrow, like the native element.

<label for="custom-select">User Type</label>
<input type="text" id="custom-select">
<img src="arrow-down.svg" alt="">

For this input, we’re going to use ARIA attributes to represent the information in the icon, so we’ll give it an empty alt attribute so screen readers don’t announce its filename.

Finally, we want a list of options. An unordered list element is a sensible choice here. It also lets screen reader software understand that these bits of text are related to each other as part of a group.

<ul class="custom-select-options">
  <li>User</li>
  <li>Author</li>
  <li>Editor</li>
  <li>Manager</li>
  <li>Administrator</li>
</ul>

You can dynamically add or remove options from this list whenever you need to. And, unlike our <option> element inside a <select>, we can add whatever we like inside the list item. So if you need images to distinguish between lots of very similar-named objects, or to add supplementary details, you can go right ahead. I’m going to add some extra text to mine, to help explain the differences between the choices.

This is a good base to begin with. But it looks nothing like a select input! We want to make sure our sighted users get something they’re familiar with and know how to use already.

Styling with CSS

I’ll add some basic styles similar to what’s in Scott Jehl’s article above.

We also need to make sure that people who customise their colours in high contrast modes can still tell what they’re looking at. After checking it in the default Windows high contrast theme, I’ve decided to add a left-hand border to the focus and hover styles, to make sure it’s clear which item is about to be chosen.

This would be a good time to add any dark-mode styles if that’s your jam. People who get migraines from bright screens will thank you!

JavaScript for behaviour

Of course, our custom select doesn’t actually do anything yet. We have a few tasks for it: to toggle the options list open and closed when we click the input, to filter the options when people type in the input, and for selecting an option to add it to the input and close the list. I’m going to tackle toggling first because it’s the easiest.

Toggling

Sometimes folks use opacity or height to hide content on screen, but that’s like using Harry Potter’s invisibility cloak. No-one can see what’s under there, but Harry doesn’t cease to exist and you can still poke him with a wand. In our case, screen reader and keyboard users can still reach an invisible list.

Instead of making the content see-through or smaller, I’m going to use display: none to hide the list. display: none removes the content from the accessibility tree, so it can’t be accessed by any user, not just people who can see. I always have a pair of utility classes for hiding things, as follows:

.hidden-all {
  display: none;
}

.hidden-visually {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0,0,0,0);
    white-space: nowrap;
    -webkit-clip-path: inset(50%);
    clip-path: inset(50%);
    border: 0;
} 

So now I can just toggle the CSS class .hidden-all on my list whenever I like.

Browsing the options

Opening up our list works well for our mouse and touch-screen users. Our styles give a nice big tap target for touch, and mouse users can click wherever they like.

We need to make sure our keyboard users are taken care of though. Some of our sighted users will be relying on the keyboard if they have mobility or dexterity issues. Usually our screen reader users are in Browse mode, which lets them click the arrow keys to navigate through content. However, custom selects are usually inside form elements. which pushes screen reader software to Forms Mode. In Forms mode, the screen reader software can only reach focusable items when the user clicks the Tab key, unless we provide an alternative. Our list items are not focusable by default, so let’s work on that alternative.

To do this, I’m adding a tabindex of -1 to each list item. This way I can send focus to them with JavaScript, but they won’t be part of the normal keyboard focus path of the page.

csOptions.forEach(function(option) {
    option.setAttribute('tabindex, '-1')
}) 

Now I can move the focus using the Up and Down arrow keys, as well as with a mouse or tapping the screen. The activeElement property of the document is a way of finding where the keyboard focus is at the moment. I can use that to loop through the elements in the list and move the focus point forward or back, depending on which key is pressed.

function doKeyAction(whichKey) {
  const focusPoint = document.activeElement
  switch(whichKey) {
    case: 'ArrowDown':
      toggleList('Open')
      moveFocus(focusPoint, 'forward')
      break
    case: 'ArrowUp':
      toggleList('Open')
      moveFocus(focusPoint, 'back')
      break
  }
}

Selecting

The Enter key is traditional for activating an element, and we want to match the original select input.

We add another case to the keypress detector…

case 'Enter':
  makeChoice(focusPoint)
  toggleList('Shut')
  setState('closed')
  break 

… then make a function which grabs the currently focused item and puts it in our text input. Then we can close the list and move focus up to the input as well.

function makeChoice(whichOption) {
    const optionText = whichOption.documentQuerySelector('strong')
    csInput.value = optionText
}

Filtering

Standard select inputs have keyboard shortcuts – typing a letter will send focus to the first item in the option which begins with that letter. If you type the letter again, focus will move to the next option beginning with that letter.

This is useful, but there’s no clue to tell users how many options might be in this category, so they have to experiment to find out. We can make an improvement for our users by filtering to just the set of options which matches that letter or sequence of letters. Then sighted users can see exactly how many options they’ve got, and continue filtering by typing more if they like. (Our screen reader users can’t see the remaining options while they’re typing, but don’t worry – we’ll have a solution for them in the next section).

I’m going to use the .filter method to make a new array which only has the items which match the text value of the input. There are different ways you could do this part – my goal was to avoid having to use regex, but you should choose whatever method works best for your content.

function doFilter() {
  const terms = csInput.value
  const aFilteredOptions = aOptions.filter(option => {
    if (option.innerText.toUpperCase().startsWith(terms.toUpperCase())) {
    return true
    }
  })
  // hide all options
  csOptions.forEach(option => option.style.display = "none")
  // re-show the options which match our terms
  aFilteredOptions.forEach(function(option) {
    option.style.display = ""
  })
} 

Nice! This is now looking and behaving really well. We’ve got one more problem though – for a screen reader user, this is a jumble of information. What’s being reported to the browser’s accessibility API is that there’s an input followed by some clickable text. Are they related? Who knows! What happens if we start typing, or click one of the clicky text things? It’s a mystery when you can’t see what’s happening. But we can fix that.

ARIA

ARIA attributes don’t provide much in the way of additional features. Adding an aria-expanded='true' attribute doesn’t actually make anything expand. What ARIA does is provide information about what’s happening to the accessibility API, which can then pass it on to any assistive technology which asks for it.

The WCAG requirements tell us that when we’re making custom elements, we need to make sure that as a whole, the widget tells us its name, its role, and its current value. Both Chrome and Firefox reveal the accessibility tree in their dev tools, so you can check how any of your widgets will be reported.

We already have a name for our input – it comes from the label we associated to the text input right at the start. We don’t need to name every other part of the field, as that makes it seem like more than one input is present. We also don’t need to add a value, because when we select an item from the list, it’s added to the text input and therefore is exposed to the API.

Figure 2: How Firefox reports our custom select to assistive technology.

But our screen readers are going to announce this custom select widget as a text entry field, with some images and a list nearby.

The ARIA Authoring Practices site has a pattern for comboboxes with listboxes attached. It tells you all the ARIA you need to make screen reader software give a useful description of our custom widget.

I’m going to add all this ARIA via JavaScript, instead of putting it in the HTML. If my JavaScript doesn’t work for any reason, the input can still be a plain text field, and we don’t want screen readers to announce it as anything fancier than that.

csSelector.setAttribute('role', 'combobox') 
csSelector.setAttribute('aria-haspopup', 'listbox')
csSelector.setAttribute('aria-owns', '#list') 
csInput.setAttribute('aria-autocomplete', 'both')
csInput.setAttribute('aria-controls', 'list')

The next thing to do is let blind users know if the list is opened or closed. For that task I’m going to add an aria-expanded attribute to the group, and update it from false to true whenever the list changes state in our toggling function.

The final touch is to add a secret status message to the widget. We can use it to update the number of options available after we’ve filtered them by typing into the input. When there are a lot of options to choose from, this helps people who can’t see the list reducing know if they’re on the right track or not.

To do that we first have to give the status message a home in our HTML.

<div id='custom-select-status' class='hidden-visually' aria-live='polite'></div>

I’m using our visually-hidden style so that only screen readers will find it. I’m using aria-live so that it will be announced as often as it updates, not just when a screen reader user navigates past it. Live regions need to be present at page load, but we won’t have anything to say about the custom select then so we can leave it empty for now.

Next we add one line to our filtering function, to find the length of our current list.

updateStatus(aFilteredOptions.length)

Then we send that to a function which will update our live region.

function updateStatus(howMany) {
    console.log('updating status')
    csStatus.textContent = howMany + " options available."
}

Conclusion

Let’s review what we’ve done to make an awesome custom select input:

  • Used semantic HTML so that it’s easily interpreted by assistive technology while expanding the types of content we can include in it
  • Added CSS styles which are robust enough to survive different visual environments while also fitting into our branding needs
  • Used JavaScript to provide the basic functionality that the native element has
  • Added more JavaScript to get useful functionality that the native element lacks
  • Carefully added ARIA attributes to make sure that the purpose and results of using the element are available to assistive technology and are updated as the user interacts with it.

You can check out my custom select pattern on GitHub – I’ll be making additions as I test it on more assistive technology, and I welcome suggestions for improvements.

The ARIA pattern linked above has a variety of examples and customisations. I hope stepping through this example shows you why each of the requirements exists, and how you can make them fit your own needs.

I think the volume of custom select inputs out there shows the ways in which the native select input is insufficient for modern websites. You’ll be pleased to know that Greg Whitworth and Simon Pieters are working on improving several input types! You can let them know what features you’d like selects to have. But until that work pays off, let’s make our custom selects as accessible and robust as they can possibly be.


About the author

Julie Grundy is an accessibility expert who works for Intopia, a digital accessibility consultancy. She has over 15 years experience as a front-end web developer in the health and education sectors. She believes in the democratic web and aims to unlock digital worlds for as many people as possible. In her spare time, she knits very slowly and chases very quickly after her two whippets.

More articles by Julie




us

Building a Dictaphone Using Media Recorder and getUserMedia

Chris Mills brushes up his shorthand and shows how the MediaStream Recording API in modern browsers can be used to capture audio directly from the user’s device. Inching ever closer to the capabilities of native software, it truly is an exciting time to be a web developer.


The MediaStream Recording API makes it easy to record audio and/or video streams. When used with MediaDevices.getUserMedia(), it provides an easy way to record media from the user’s input devices and instantly use the result in web apps. This article shows how to use these technologies to create a fun dictaphone app.

A sample application: Web Dictaphone

To demonstrate basic usage of the MediaRecorder API, we have built a web-based dictaphone. It allows you to record snippets of audio and then play them back. It even gives you a visualisation of your device’s sound input, using the Web Audio API. We’ll just concentrate on the recording and playback functionality in this article, for brevity’s sake.

You can see this demo running live, or grab the source code on GitHub. This has pretty good support on modern desktop browsers, but pretty patchy support on mobile browsers currently.

Basic app setup

To grab the media stream we want to capture, we use getUserMedia(). We then use the MediaRecorder API to record the stream, and output each recorded snippet into the source of a generated <audio> element so it can be played back.

We’ll first declare some variables for the record and stop buttons, and the <article> that will contain the generated audio players:

const record = document.querySelector('.record');
const stop = document.querySelector('.stop');
const soundClips = document.querySelector('.sound-clips');

Next, we set up the basic getUserMedia structure:

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
   console.log('getUserMedia supported.');
   navigator.mediaDevices.getUserMedia (
      // constraints - only audio needed for this app
      {
         audio: true
      })

      // Success callback
      .then(function(stream) {

      })

      // Error callback
      .catch(function(err) {
         console.log('The following `getUserMedia` error occured: ' + err);
      }
   );
} else {
   console.log('getUserMedia not supported on your browser!');
}

The whole thing is wrapped in a test that checks whether getUserMedia is supported before running anything else. Next, we call getUserMedia() and inside it define:

  • The constraints: Only audio is to be captured for our dictaphone.
  • The success callback: This code is run once the getUserMedia call has been completed successfully.
  • The error/failure callback: The code is run if the getUserMedia call fails for whatever reason.

Note: All of the code below is found inside the getUserMedia success callback in the finished version.

Capturing the media stream

Once getUserMedia has created a media stream successfully, you create a new Media Recorder instance with the MediaRecorder() constructor and pass it the stream directly. This is your entry point into using the MediaRecorder API — the stream is now ready to be captured into a <Blob>, in the default encoding format of your browser.

const mediaRecorder = new MediaRecorder(stream);

There are a series of methods available in the MediaRecorder interface that allow you to control recording of the media stream; in Web Dictaphone we just make use of two, and listen to some events. First of all, MediaRecorder.start() is used to start recording the stream once the record button is pressed:

record.onclick = function() {
  mediaRecorder.start();
  console.log(mediaRecorder.state);
  console.log("recorder started");
  record.style.background = "red";
  record.style.color = "black";
}

When the MediaRecorder is recording, the MediaRecorder.state property will return a value of “recording”.

As recording progresses, we need to collect the audio data. We register an event handler to do this using mediaRecorder.ondataavailable:

let chunks = [];

mediaRecorder.ondataavailable = function(e) {
  chunks.push(e.data);
}

Last, we use the MediaRecorder.stop() method to stop the recording when the stop button is pressed, and finalize the Blob ready for use somewhere else in our application.

stop.onclick = function() {
  mediaRecorder.stop();
  console.log(mediaRecorder.state);
  console.log("recorder stopped");
  record.style.background = "";
  record.style.color = "";
}

Note that the recording may also stop naturally if the media stream ends (e.g. if you were grabbing a song track and the track ended, or the user stopped sharing their microphone).

Grabbing and using the blob

When recording has stopped, the state property returns a value of “inactive”, and a stop event is fired. We register an event handler for this using mediaRecorder.onstop, and construct our blob there from all the chunks we have received:

mediaRecorder.onstop = function(e) {
  console.log("recorder stopped");

  const clipName = prompt('Enter a name for your sound clip');

  const clipContainer = document.createElement('article');
  const clipLabel = document.createElement('p');
  const audio = document.createElement('audio');
  const deleteButton = document.createElement('button');

  clipContainer.classList.add('clip');
  audio.setAttribute('controls', '');
  deleteButton.innerHTML = "Delete";
  clipLabel.innerHTML = clipName;

  clipContainer.appendChild(audio);
  clipContainer.appendChild(clipLabel);
  clipContainer.appendChild(deleteButton);
  soundClips.appendChild(clipContainer);

  const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
  chunks = [];
  const audioURL = window.URL.createObjectURL(blob);
  audio.src = audioURL;

  deleteButton.onclick = function(e) {
    let evtTgt = e.target;
    evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
  }
}

Let’s go through the above code and look at what’s happening.

First, we display a prompt asking the user to name their clip.

Next, we create an HTML structure like the following, inserting it into our clip container, which is an <article> element.

<article class="clip">
  <audio controls></audio>
  <p>_your clip name_</p>
  <button>Delete</button>
</article>

After that, we create a combined Blob out of the recorded audio chunks, and create an object URL pointing to it, using window.URL.createObjectURL(blob). We then set the value of the <audio> element’s src attribute to the object URL, so that when the play button is pressed on the audio player, it will play the Blob.

Finally, we set an onclick handler on the delete button to be a function that deletes the whole clip HTML structure.

So that’s basically it — we have a rough and ready dictaphone. Have fun recording those Christmas jingles! As a reminder, you can find the source code, and see it running live, on the MDN GitHub.


This article is based on Using the MediaStream Recording API by Mozilla Contributors, and is licensed under CC-BY-SA 2.5.


About the author

Chris Mills manages the MDN web docs writers’ team at Mozilla, which involves spreadsheets, meetings, writing docs and demos about open web technologies, and occasional tech talks at conferences and universities. He used to work for Opera and W3C, and enjoys playing heavy metal drums and drinking good beer.

More articles by Chris




us

Five Interesting Ways to Use Array.reduce() (And One Boring Way)

Chris Ferdinandi turns the heat down low and lets the sauce reduce while we take a look at how to add spice to our source with a sprinkling of Array.reduce(). Just a little ingenuity with the humblest of functions.


Of all the modern array methods, the one I had the hardest time wrapping my head around was Array.reduce().

On the surface, it seems like a simple, boring method that doesn’t do much. But below its humble exterior, Array.reduce() is actually a powerful, flexible addition to your developer toolkit.

Today, we’re going to look at some cool things you can do with Array.reduce().

How Array.reduce() works

Most of the modern array methods return a new array. The Array.reduce() method is a bit more flexible. It can return anything. Its purpose is to take an array and condense its content into a single value.

That value can be a number, a string, or even an object or new array. That’s the part that’s always tripped me up – I didn’t realize just how flexible it is!

The syntax

The Array.reduce() accepts two arguments: a callback method to run against each item in the array, and a starting value.

The callback also accepts two arguments: the accumulator, which is the current combined value, and the current item in the loop. Whatever you return is used as the accumulator for the next item in the loop. On the very first loop, that starting value is used instead.

var myNewArray = [].reduce(function (accumulator, current) {
  return accumulator;
}, starting);

Let’s look at some examples to make this all tangible.

1. Adding numbers together

Let’s say you had an array of numbers that you wanted to add together. Using Array.forEach(), you might do something like this:

var total = 0;

[1, 2, 3].forEach(function (num) {
  total += num;
});

This is the cliche example for using Array.reduce(). I find the word accumulator confusing, so in this example, I’m calling it sum, because that’s what it is.

var total = [1, 2, 3].reduce(function (sum, current) {
  return sum + current;
}, 0);

Here, we pass in 0 as our starting value.

In the callback, we add the current value to the sum, which has our starting value of 0 on the first loop, then 1 (the starting value of 0 plus the item value of 1), then 3 (the sum value of 1 plus the item value of 2), and so on.

Here’s a demo.

2. Combining multiple array methods into Array.map() and Array.filter() into a single step

Imagine you had an array of wizards at Hogwarts.

var wizards = [
  {
    name: 'Harry Potter',
    house: 'Gryfindor'
  },
  {
    name: 'Cedric Diggory',
    house: 'Hufflepuff'
  },
  {
    name: 'Tonks',
    house: 'Hufflepuff'
  },
  {
    name: 'Ronald Weasley',
    house: 'Gryfindor'
  },
  {
    name: 'Hermione Granger',
    house: 'Gryfindor'
  }
];

You want to create a new array that contains just the names of wizards who are in Hufflepuff. One way you could do that is by using the Array.filter() method to get back just wizards whose house property is Hufflepuff. Then, you’d use the Array.map() method to create a new array containing just the name property for the remaining wizards.

// Get the names of the wizards in Hufflepuff
var hufflepuff = wizards.filter(function (wizard) {
  return wizard.house === 'Hufflepuff';
}).map(function (wizard) {
  return wizard.name;
});

With the Array.reduce() method, we can get the same array in a single pass, improving our performance. You pass in an empty array ([]) as the starting value. On each pass, you check to see if the wizard.house is Hufflepuff. If it is, you push it to the newArr (our accumulator in this example). If not, you do nothing.

Either way, you return the newArr to become the accumulator on the next pass.

// Get the names of the wizards in Hufflepuff
var hufflepuff = wizards.reduce(function (newArr, wizard) {
  if (wizard.house === 'Hufflepuff') {
    newArr.push(wizard.name);
  }
  return newArr;
}, []);

Here’s another demo.

3. Creating markup from an array

What if, instead of creating an array of names, we wanted to create an unordered list of wizards in Hufflepuff? Instead of passing an empty array into Array.reduce() as our starting value, we’ll pass in an empty string ('') and call it html.

If the wizard.house equals Hufflepuff, we’ll concatenate our html string with the wizard.name wrapped in an opening and closing list item (li). Then, we’ll return the html to become the accumulator on the next loop.

// Create a list of wizards in Hufflepuff
var hufflepuffList = wizards.reduce(function (html, wizard) {
  if (wizard.house === 'Hufflepuff') {
    html += '<li>' + wizard.name + '</li>';
  }
  return html;
}, '');

Add an opening and closing unordered list element before and after Array.reduce(), and you’re ready to inject your markup string into the DOM.

// Create a list of wizards in Hufflepuff
var hufflepuffList = '<ul>' + wizards.reduce(function (html, wizard) {
  if (wizard.house === 'Hufflepuff') {
    html += '<li>' + wizard.name + '</li>';
  }
  return html;
}, '') + '</ul>';

See it in action here.

4. Grouping similar items in an array together

The lodash library has a groupBy() method takes a collection of items as an array and groups them together into an object based on some criteria.

Let’s say you want an array of numbers.

If you wanted to group all of the items in numbers together based on their integer value, you would do this with lodash.

var numbers = [6.1, 4.2, 6.3];

// returns {'4': [4.2], '6': [6.1, 6.3]}
_.groupBy(numbers, Math.floor);

If you had an array of words, and you wanted to group the items in words by their length, you would do this.

var words = ['one', 'two', 'three'];

// returns {'3': ['one', 'two'], '5': ['three']}
_.groupBy(words, 'length');

Creating a groupBy() function with Array.reduce()

You can recreate that same functionality using the Array.reduce() method.

We’ll create a helper function, groupBy(), that accepts the array and criteria to sort by as arguments. Inside groupBy(), we’ll run Array.reduce() on our array, passing in an empty object ({}) as our starting point, and return the result.

var groupBy = function (arr, criteria) {
  return arr.reduce(function (obj, item) {
    // Some code will go here...
  }, {});
};

Inside the Array.reduce() callback function, we’ll check to see if the criteria is a function, or a property of the item. Then we’ll get its value from the current item.

If there’s no property in the obj with that value yet, we’ll create it and assign an empty array as its value. Finally, we’ll push the item to that key, and return the object as the accumulator for the next loop.

var groupBy = function (arr, criteria) {
  return arr.reduce(function (obj, item) {

    // Check if the criteria is a function to run on the item or a property of it
    var key = typeof criteria === 'function' ? criteria(item) : item[criteria];

    // If the key doesn't exist yet, create it
    if (!obj.hasOwnProperty(key)) {
      obj[key] = [];
    }

    // Push the value to the object
    obj[key].push(item);

    // Return the object to the next item in the loop
    return obj;

  }, {});
};

Here’s a demo of the completed helper function.

Special thanks to Tom Bremer for helping me make some improvements to this one. You can find this helper function and more like it on the Vanilla JS Toolkit.

5. Combining data from two sources into an array

Remember our array of wizards?

var wizards = [
  {
    name: 'Harry Potter',
    house: 'Gryfindor'
  },
  {
    name: 'Cedric Diggory',
    house: 'Hufflepuff'
  },
  {
    name: 'Tonks',
    house: 'Hufflepuff'
  },
  {
    name: 'Ronald Weasley',
    house: 'Gryfindor'
  },
  {
    name: 'Hermione Granger',
    house: 'Gryfindor'
  }
];

What if you had another data set, an object of house points each wizard has earned.

var points = {
  HarryPotter: 500,
  CedricDiggory: 750,
  RonaldWeasley: 100,
  HermioneGranger: 1270
};

Imagine you wanted to combine both sets of data into a single array, with the number of points added to each wizard’s data in the wizards array. How would you do it?

The Array.reduce() method is perfect for this!

var wizardsWithPoints = wizards.reduce(function (arr, wizard) {

  // Get the key for the points object by removing spaces from the wizard's name
  var key = wizard.name.replace(' ', '');

  // If the wizard has points, add them
  // Otherwise, set them to 0
  if (points[key]) {
    wizard.points = points[key];
  } else {
    wizard.points = 0;
  }

  // Push the wizard object to the new array
  arr.push(wizard);

  // Return the array
  return arr;

}, []);

Here’s a demo combining data from two sources into an array.

6. Combining data from two sources into an object

What if you instead wanted to combine the two data sources into an object, where each wizard’s name was the key, and their house and points were properties? Again, the Array.reduce() method is perfect for this.

var wizardsAsAnObject = wizards.reduce(function (obj, wizard) {

  // Get the key for the points object by removing spaces from the wizard's name
  var key = wizard.name.replace(' ', '');

  // If the wizard has points, add them
  // Otherwise, set them to 0
  if (points[key]) {
    wizard.points = points[key];
  } else {
    wizard.points = 0;
  }

  // Remove the name property
  delete wizard.name;

  // Add wizard data to the new object
  obj[key] = wizard;

  // Return the array
  return obj;

}, {});

Here’s a demo combining two data sets into an object.

Should you use Array.reduce() more?

The Array.reduce() method has gone from being something I thought was pointless to my favorite JavaScript method. So, should you use it? And when?

The Array.reduce() method has fantastic browser support. It works in all modern browsers, and IE9 and above. It’s been supported in mobile browsers for a long time, too. If you need to go back even further than that, you can add a polyfill to push support back to IE6.

The biggest complaint you can make about Array.reduce() is that it’s confusing for people who have never encountered it before. Combining Array.filter() with Array.map() is slower to run and involves extra steps, but it’s easier to read. It’s obvious from the names of the methods what they’re supposed to be doing.

That said, there are times where Array.reduce() makes things that would be complicated more simple rather than more complicated. The groupBy() helper function is a good example.

Ultimately, this is another tool to add to your toolkit. A tool that, if used right, can give you super powers.


About the author

Chris Ferdinandi helps people learn vanilla JavaScript. He believes there’s a simpler, more resilient way to make things for the web.

Chris is the author of the Vanilla JS Pocket Guide series, creator of the Vanilla JS Academy training program, and host of the Vanilla JS Podcast. His developer tips newsletter is read by thousands of developers each weekday.

He’s taught developers at organizations like Chobani and the Boston Globe, and his JavaScript plugins have been used used by Apple and Harvard Business School. Chris Coyier, the founder of CSS-Tricks and CodePen, has described his writing as “infinitely quote-worthy.”

Chris loves pirates, puppies, and Pixar movies, and lives near horse farms in rural Massachusetts. He runs Go Make Things with Bailey Puppy, a lab-mix from Tennessee.

More articles by Chris




us

Usability and Security; Better Together

Divya Sasidharan calls into question the trade-offs often made between security and usability. Does a secure interface by necessity need to be hard to use? Or is it the choice we make based on years of habit? Snow has fallen, snow on snow.


Security is often synonymous with poor usability. We assume that in order for something to be secure, it needs to by default appear impenetrable to disincentivize potential bad actors. While this premise is true in many instances like in the security of a bank, it relies on a fundamental assumption: that there is no room for choice.

With the option to choose, a user almost inevitably picks a more usable system or adapts how they interact with it regardless of how insecure it may be. In the context of the web, passwords are a prime example of such behavior. Though passwords were implemented as a way to drastically reduce the risk of attack, they proved to be marginally effective. In the name of convenience, complex, more secure passwords were shirked in favor of easy to remember ones, and passwords were liberally reused across accounts. This example clearly illustrates that usability and security are not mutually exclusive. Rather, security depends on usability, and it is imperative to get user buy-in in order to properly secure our applications.

Security and Usability; a tale of broken trust

At its core, security is about fostering trust. In addition to protecting user accounts from malicious attacks, security protocols provide users with the peace of mind that their accounts and personal information is safe. Ironically, that peace of mind is incumbent on users using the security protocols in the first place, which further relies on them accepting that security is needed. With the increased frequency of cyber security threats and data breaches over the last couple of years, users have grown to be less trusting of security experts and their measures. Security experts have equally become less trusting of users, and see them as the “the weakest link in the chain”. This has led to more cumbersome security practices such as mandatory 2FA and constant re-login flows which bottlenecks users from accomplishing essential tasks. Because of this break down in trust, there is a natural inclination to shortcut security altogether.

Build a culture of trust not fear

Building trust among users requires empowering them to believe that their individual actions have a larger impact on the security of the overall organization. If a user understands that their behavior can put critical resources of an organization at risk, they will more likely behave with security in mind. For this to work, nuance is key. Deeming that every resource needs a similarly high number of checks and balances diminishes how users perceive security and adds unnecessary bottlenecks to user workflows.

In order to lay the foundation for good security, it’s worth noting that risk analysis is the bedrock of security design. Instead of blindly implementing standard security measures recommended by the experts, a better approach is to tailor security protocols to meet specific use cases and adapt as much as possible to user workflows. Here are some examples of how to do just that:

Risk based authentication

Risk based authentication is a powerful way to perform a holistic assessment of the threats facing an organization. Risks occur at the intersection of vulnerability and threat. A high risk account is vulnerable and faces the very real threat of a potential breach. Generally, risk based authentication is about calculating a risk score associated with accounts and determining the proper approach to securing it. It takes into account a combination of the likelihood that that risk will materialize and the impact on the organization should the risk come to pass. With this system, an organization can easily adapt access to resources depending on how critical they are to the business; for instance, internal documentation may not warrant 2FA, while accessing business and financial records may.

Dynamically adaptive auth

Similar to risk based auth, dynamically adaptive auth adjusts to the current situation. Security can be strengthened and slackened as warranted, depending on how risky the access point is. A user accessing an account from a trusted device in a known location may be deemed low risk and therefore not in need of extra security layers. Likewise, a user exhibiting predictive patterns of use should be granted quick and easy access to resources. The ability to adapt authentication based on the most recent security profile of a user significantly improves the experience by reducing unnecessary friction.

Conclusion

Historically, security failed to take the user experience into account, putting the onus of securing accounts solely on users. Considering the fate of password security, we can neither rely on users nor stringent security mechanisms to keep our accounts safe. Instead, we should aim for security measures that give users the freedom to bypass them as needed while still protecting our accounts from attack. The fate of secure systems lies in the understanding that security is a process that must constantly adapt to face the shifting landscape of user behavior and potential threats.


About the author

Divya is a web developer who is passionate about open source and the web. She is currently a developer experience engineer at Netlify, and believes that there is a better workflow for building and deploying sites that doesn’t require a server—ask her about the JAMstack. You will most likely find her in the sunniest spot in the room with a cup of tea in hand.

More articles by Divya




us

Max factor, minimum fuss

SRIYA NARAYANAN embraces the minimalism trend and tells you how to master it




us

She made us believe

Ritu Kumar, who made us trust our traditional textile heritage, talks about her delicious journey




us

Uniqlo turns three in the India market; launches second edition of its collaboration with Italian luxury fashion house Marni

Headquartered in Tokyo, the Japanese apparel brand focusses on technology and sustainability, and its latest collection of fleece jackets is made of recycled PET bottles



  • Life &amp; Style

us

This bus in Chennai brings fashion to your doorstep with its boutique on wheels

Started by Karthick Gunabalan, Pikbig.com showcases a collection of ethnic and Indo-western clothes for women



  • Life &amp; Style

us

The style file from the Lakme Fashion Week that got us talking

Lakmé Fashion Week x FDCI 2023 showcased gender-agnostic styles, recycled accessories and modern Indian draping techniques – we explore what will influence your wardrobe this year



  • Life &amp; Style

us

This Chennai-based startup customises your sneakers so you can put your personality on them

Pastels, bling, or bold colours, whatever be your taste, K-kix, a sneaker customisation platform from Chennai can do it for you



  • Life &amp; Style

us

‘Your life is more than just a number on a weighing scale’

At an event to launch her book Gain to Lose, Dr. Sheela Nambiar said weight training helped women lose weight and cope better with their myriad tasks




us

Let us go then you and I

Walking your dog is good for your body, mind and soul as Mini Anthikad Chhibber discovers




us

Running is just symbolic: Milind Soman

Exhorting women to take charge of their health, Milind Soman says they should not feel guilty of stealing time away from their family responsibilities




us

Consultation, just a flick away

Here is a look at how technology is getting people connected to healthcare, thanks to your Smartphone




us

The pedal pushers

Members of Vibrant Velachery commute around the city by cycle in a bit to lead a sustainable life




us

‘Never trust the front of a food box’




us

Sculpting muscles and dreams

Mr.Madurai contest has been shaping the ideals and aspirations of city body builders. A.SHRIKUMAR takes a peek into what goes on behind the stage




us

Just what your trainer ordered

Healthy meal delivery services now have trainers and nutritionists on board to give clients the best of pre and post-workout food, writes SUSANNA MYRTLE LAZARUS




us

Editorial. Gold users must get a transparent Re-based price

There is a need to provide consumers with a transparently derived domestic reference rate which is under regulatory supervision




us

Editorial. Cautious and conservative

Budget is conservative but not averse to experimenting




us

Editorial. Overall rain surplus but spatial, temporal spread concerning

While the IMD has strived to improve its predictive models, global warming is the wild card factor




us

Editorial. Skilling sops useful, but education is key

There can be no getting away from the fact that the skills crisis in India’s young (15-29 years) reflects a failure of the education system — and the best skilling efforts cannot make up for this lack




us

Not exactly a status quo policy

The RBI has explained its food inflation concerns for those who discount them in the context of monetary policy — while flagging financial stability issues




us

Editorial. Industrial township plan should avoid earlier errors

The institutional arrangements and market linkages must be worked out beforehand. The biggest hurdle pertains to land acquisition. The implementing agency is expected to provide “developed land parcels” for industries to operate in ‘plug and play’ mode. But this is easier said than done




us

Editorial. Robust outlook

Underlying positives in sub-7 pc Q1 growth




us

Editorial. Expanding controls on sugar industry will stifle output

Trying to substitute the operation of market forces in an industry with micro-management of demand, supply and prices creates uncertainty for entrepreneurs




us

Editorial. Gig workers’ e-shram enrolment must be stepped up

Their dual identity as entrepreneurs and workers gives rise to the apprehension that they may miss out on social security benefits




us

Editorial. SEBI’s take on household financial savings clears the air

SEBI has calculated net primary and secondary market investment in equity and debt based on actual data at its disposal




us

Editorial. Vacuous Trump-Harris debate, bad news for world

The debate was meant to be about what should really matter to Americans. And it revealed in stark relief the paucity of political talent and ideas in the US




us

Editorial. Edible oil tariffs must balance farmer, consumer needs

Curbs on edible oil exports must be relaxed so that farmers have the freedom to capitalise on global market opportunities, just as consumers benefit from cheap imports




us

Editorial. More action needed against substandard, spurious drugs

Industry bodies should take the lead to protect the image of a sector that has at least 10,000 units and a market size of about $50 billion, growing in double digits




us

Editorial. More disclosures needed in SEBI ‘direct plan’ push

The dashboards on AMCs’ websites should feature data on direct plans, not just regular ones




us

Editorial. China market stimulus may not impact India’s FPI flows

While foreign funds may be buying Chinese equity for the short-term, the Indian market offers better returns for long-term investors




us

Editorial. RBI right in reining in usurious micro lending

But sudden ‘cease and desist’ orders can also affect the bond markets from where NBFCs raise funds




us

IndusInd Bank Q2 PAT tanks 40 per cent on higher provisions, bad loans

NIM moderates as deposits outpace credit growth



  • Money &amp; Banking

us

PNB Housing Finance Q2 net up 22.7 per cent at ₹470 crore 

The portfolio asset quality continues to improve with Gross NPA at 1.24 percent as on end September 2024



  • Money &amp; Banking

us

Govt can waive off GST on health cover, step up focus on insurance penetration: FGII CEO Anup Rau

Removing GST on health insurance would make it more affordable, driving higher penetration and reducing the government’s burden



  • Money &amp; Banking