All About JavaScript Loops

Category Image 080

Every programming language has loops. Loops perform an operation (i.e., a chunk of work) a number of times, usually once for every item in an array or list, or to simply repeat an operation until a certain condition is met.

JavaScript in particular has quite a few different types of loops. I haven’t even used all of them, so for my own curiosity, I thought I’d do a high-level overview of them. And as it turns out, there are pretty good reasons I haven’t used at least a couple of the different types.

So, for now let’s spend a while exploring the different types of loops, what we can do with each of one, and why you might use one over another. (You’ll think that little play on words is absolutely hilarious by the end.)

The while and do...while loops

First up is the while loop. It’s the most basic type of loop and has the potential to be the easiest to read and the fastest in many cases. It’s usually used for doing something until a certain condition is met. It’s also the easiest way to make an infinite loop or a loop that never stops. There is also the do...while statement. Really, the only difference is that the condition is checked at the end versus the beginning of each iteration.

// remove the first item from an array and log it until the array is empty
let queue1 = ["a", "b", "c"];

while (queue1.length) {
  let item = queue1.shift();

  console.log(item);
}

// same as above but also log when the array is empty
let queue2 = [];

do {
  let item = queue2.shift() ?? "empty";

  console.log(item);
} while (queue2.length);

The for loop

Next is the for loop. It should be the go to way to do something a certain number of times. If you need to repeat an operation, say, 10 times, then use a for loop instead. This particular loop may be intimidating to those new to programming, but rewriting the same loop in the while-style loop can help illustrate the syntax make it easier to stick in your mind.

// log the numbers 1 to 5
for (let i = 1; i <= 5; i++) {
  console.log(i);
}

// same thing but as a while loop
let i = 1; // the first part of a for loop

// the second
while (i <= 5) {
  console.log(i);

  i++; // the third
}

("end");

The for...of and for await...of loops

A for...of loop is the easiest way to loop through an array.

let myList = ["a", "b", "c"];

for (let item of myList) {
  console.log(item);
}

They aren’t limited to arrays though. Technically they can iterate through anything that implements what is called an iterable protocol. There are a few built-in types that implement the protocol: arrays, maps, set, and string, to mention the most common ones, but you can implement the protocol in your own code. What you’d do is add a [Symbol.iterator] method to any object and that method should return an iterator. It’s a bit confusing, but the gist is that iterables are things with a special method that returns iterators; a factory method for iterators if you will. A special type of function called a generator is a function that returns both a iterable and iterator.

let myList = {
  *[Symbol.iterator]() {
    yield "a";
    yield "b";
    yield "c";
  },
};

for (let item of myList) {
  console.log(item);
}

There is the async version of all the things I just mentioned: async iterables, async iterators, and async generators. You’d use an async iterable with for await...of.

async function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

// this time we're not making an iterable, but a generator
async function* aNumberAMinute() {
  let i = 0;

  while (true) {
    // an infinite loop
    yield i++;

    // pause a minute
    await delay(60_000);
  }
}

// it's a generator, so we need to call it ourselves
for await (let i of aNumberAMinute()) {
  console.log(i);

  // stop after one hour
  if (i >= 59) {
    break;
  }
}

One unobvious thing about for await...of statement is that you can use it with non-async iterables and it will work just fine. The reverse, however, is not true; you can’t use async iterables with the for...of statement.

The forEach and map loops

While these are not technically loops per se, you can use them to iterate over a list.

Here is the thing about the forEach method. Historically it was much slower than using a for loop. I think in some cases that may not be true anymore, but if performance is a concern, then I would avoid using it. And now that we have for...of I’m not sure there is much reason to use it. I guess the only reason that it still may come up is if you have a function ready to use as the callback, but you could easily just call that same function from inside the body of for...of.

forEach also receives the index for each item though, so that may be a thing you need too. Ultimately, the decision to use it will probably come down to whether any other code you’re working with uses it, but I personally would avoid using it if I’m writing something new.

let myList = ["a", "b", "c"];

for (let item of myList) {
	console.log(item);
}

// but maybe if I need the index use forEach
["a", "b", "c"].forEach((item, index) => {
  console.log(`${index}: ${item}`);
});

Meanwhile, map essentially converts one array into another. It still has the same performance impact that forEach has, but it is a bit nicer to read than the alternative. It’s certainly subjective though, and just like with forEach you’ll want to do what the rest of your other code is doing. You see it a ton in React and React-inspired libraries as the primary way to loop through an array and output a list of items within JSX.

function MyList({items}) {
  return (
    <ul>
      {items.map((item) => {
        return <li>{item}</li>;
      })}
    </ul>
  );
}

The for...in loop

This list of loops in JavaScript wouldn’t be complete without mentioning the for...in statement because it can loop through the fields of an object. It visits fields that are inherited through the object’s prototype chain too, though, and I’ve honestly always avoided it for that reason.

That said, if you have an object literal, then for...in might be a viable way to iterate through the keys of that object. Also it’s worth noting that if you’ve been programming JavaScript for a long time, you may remember that the order of keys use to be inconsistent between browsers, but now the order is consistent. Any key that could be an array index (i.e., positive integers) will be first in ascending order, and then everything else in the order as authored.

let myObject = {
  a: 1,
  b: 2,
  c: 3,
};

for (let k in myObject) {
  console.log(myObject[k]);
}

Wrapping up

Loops are something that many programmers use every day, though we may take them for granted and not think about them too much.

But when you step back and look at all of the ways we have to loop through things in JavaScript, it turns out there are several ways to do it. Not only that, but there are significant — if not nuanced — differences between them that can and will influence your approach to scripts.


All About JavaScript Loops originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

React Helpful Hints Series: Volume 2

Category Image 080
React is a popular JavaScript framework that was developed by Facebook and has gained a massive following among developers due to its simplicity, modularity, and reusability. In this series of short “bloglets,” our team will cover a wide array of React topics, including developer tips, issues, and experiences. We are hopeful that everyone from the beginner to the seasoned professional will find something useful in each post. 

Using React Context

By Jeff Schuman

Developers new to React have a tendency to over-use property propagation to nested child components. The term for this is prop drilling. Prop drilling is generally frowned upon as it inhibits clean, reusable, and DRY code.

Cypress Feature “Test Replay” Launched: Let’s Play With Test Replay

Category Image 080

Problem Statement

Before Cypress v13, test failures in CI have historically been captured through screenshots, videos, and stack trace outputs, but these artifacts provide limited information.

So Cypress comes with a new feature Test Replay in version 13.0.0. The introduction of features like “Test Replay” in Cypress v13 aims to bridge this gap by providing developers with a way to replay the exact test run and inspect various Aspects of it, such as DOM interactions, network requests, console logs, JavaScript errors, and more

Light DOM and Lightning Web Components in Salesforce

Category Image 080

Lightning Web Components (LWC) from Salesforce are based on standard Web Components built using HTML and JavaScript. They are lightweight, easy to build, and perform well in modern browsers. When building LWCs, you’ll become familiar with the concept of composition: piecing together simple building-block components within the body of a more complex component.

Regarding composition, LWC leverages the Shadow Document Object Model (DOM) web standard, which encapsulates the internal structure of a Web Component and makes it inaccessible to code and components outside of the component. The alternative to this approach is Light DOM, which Salesforce makes available in beta.

Signed Exchanges recommended cache TTL

Category Image 080

According to this page, Google requires "as a rule of thumb" that the SXG expiration date is less than 1 day in the future if the content is JS, or otherwise 7 days in the future.

My external Javascript files are immutable. Why should they have a 1 day cache life? Currently I am doing something similar to max-age=3600, s-maxage=604800 for HTML files, and a flat max-age=2592000 for JS.

Is this not ideal?

Custom SVG Cursors with an Interactive Emitter Effect

Category Image 080

From the custom cursor on my portfolio marvinx.com using blurred SVG circles, I created several variations which I would like to share with you today.

Without going into too much detail, I’d like to explain some points on how I approached the making of this set.

For the demos, the idea is to set a main class grouping all the functions inherent to all cursors.

Then, I separate demos in different classes where each variable is configurable: number of particles, colors, size, gradient, opacity, filters, radius, speed, acceleration, direction, etc.

Everything is coded in native JavaScript and does not use any libraries (only d3.js if we want to sort particles).

This is how particles are drawn in the Cursor class:

  drawParticles() {
    return `<g class="particles" filter=${this.filterParticles || "none"}>
      ${(() => {
        if (this.strokeGradient) {
          return `
          <defs>
            <linearGradient id=${this.strokeGradient.idStrokeGradient} x1="0%" y1="0%" x2="0%" y2="100%">
              <stop offset="0%" stop-color=${this.strokeGradient.color1} />
              <stop offset="100%" stop-color=${this.strokeGradient.color2} />
            </linearGradient>
          </defs>`
        }
      })()}
      ${Array(this.nbrParticles).fill().map((_,i) =>
        `<circle
          r="${this.setRadiusParticles(i)}"
          cx=${this.pos.x} cy=${this.pos.y}
          fill="${this.fillParticles || "none"}"
          fill-opacity="${this.fillOpacityParticles || 1}"
          stroke="${this.strokeGradient ? `url(#${this.strokeGradient.idStrokeGradient})` : this.strokeColorParticles}"
          stroke-width="${this.strokeWidthParticles || 0}"
          stroke-opacity="${this.strokeOpacityParticles || 1}"
          id="${i}">
        </circle>`).join('')}
    </g>`
  }

This is how each parameter is then configured:

export class Cursor1 extends Cursors{

  constructor(index) {
    super(index);
    this.speed = !isTouchDevices ? 0.5 : 1;
    this.init();
    this.loop();
  }

  setParamsCursor() {
    this.radiusCursor = 15;
    this.fillCursor = getComputedStyle(document.body).getPropertyValue('--primary');
    this.maxSqueeze = 0.6;
    this.accelerator = 1000;
  }

  setParamsParticles() {
    this.strokeGradient = {
      idStrokeGradient : "gradient",
      color2 : getComputedStyle(document.body).getPropertyValue('--primary'),
      color1 : getComputedStyle(document.body).getPropertyValue('--secondary'),
    }
    this.strokeWidthParticles = 1.5;
    this.strokeOpacityParticles = .15;
    this.radiusDiff = 7;
    this.radiusStart = this.radiusCursor*3;
    this.nbrParticles = Math.round((this.diagonalWindow() + this.radiusDiff - this.radiusStart) / this.radiusDiff);
    this.transitionParticles = {
      duration: 18,
      delay: !isTouchDevices ? 4 : 14,
      easing : "linear"
    };
  }
}

1. Waves effect

2. Trail effect

3. Tube effect

4.Mask effect

On this last demo, I use twice the same superimposed video (from Mikhail Nilov‘s beautiful royalty free collection).

The first video uses a grayscale filter:

  filterImageBack() {
    return
    `<filter id=${this.filterBackId}>
      <feColorMatrix type="matrix" values=".33 .33 .33 0 0
        .33 .33 .33 0 0
        .33 .33 .33 0 0
        0 0 0 1 0">
      </feColorMatrix>
    </filter>`
  }

And the second one is placed inside a mask where I apply a duotone filter:

  filterImageCursor() {
    return 
     `<filter id=${this.filterCursorId} filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
      <feColorMatrix type="matrix" values=".44 .44 .44 0 0
        .44 .44 .44 0 0
        .44 .44 .44 0 0
        0 0 0 1 0">
      </feColorMatrix>
      <feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
        <feFuncR type="table" tableValues="0.55 0.25"></feFuncR>
        <feFuncG type="table" tableValues="0.06 1"></feFuncG>
        <feFuncB type="table" tableValues="0.93 0.91"></feFuncB>
        <feFuncA type="table" tableValues="0 1"></feFuncA>
      </feComponentTransfer>
    </filter>`
  }

I also thank Ghislain Auzillon, for his help on the design.

Hope you enjoy it!

Can You Avoid the Git ‘Fatal: Refusing to Merge Unrelated Histories’ Error?

Category Image 080

One of the most common Git errors, "fatal: refusing to merge histories" occurs when there is an attempt to merge unrelated projects in one branch. This happens because the pull request or clone is not compatible with the commit histories and tags of a branch. 

Resolving this error is not that difficult, though. The causes that lead to this problem and the solutions for it are presented below. The more intriguing question may be, would it be possible to stop this error from happening in the first place? 

Send Push Notifications to Users of Another App

Category Image 080

As online shopping for products and services becomes more and more popular, new business opportunities have also arisen. To seize such opportunities, I recently developed an online shopping app, which I shall refer to in this article as "app B". Once you have developed an app, the next thing that you need to do is to promote the app and attract more users to use it. Since sending push messages to users is a widely used method for promoting apps and improving user engagement, I decided to do the same for my new app in order to deliver promotional information and various coupons to users, which hopefully should increase their engagement and interest.

However, I discovered a glaring problem straightaway. Since the app has just been released, it has few registered users, making it hard to achieve the desired promotional effect by just sending push messages to these users. What I needed to do was to send push messages to a large pool of existing users in order to get them to try out my new app. It suddenly occurred to me that I once developed a very popular short video app (which I shall refer to as "app A"), which has now accumulated thousands of registered users. Wouldn't it be great if there was a one-stop service that I can use to get app B to send push messages to the wide user base of app A, thus attracting users of app A to use app B?

Exploring Deferred and Promise in JQuery

Category Image 080

The jQuery Deferred object was introduced as a part of the 1.5 release of the framework. The Deferred object in jQuery is based upon the concept of Promises. To understand all about Deferred objects, it would be wise to try to understand what a Promise is all about.

Many times in our daily lives, we cannot rely on the outcomes of certain actions. However, we may still need to make decisions about the future depending on an anticipated outcome. All we know for sure is that we are going to get an outcome. Let’s just say that we wrap the term “outcome” in a fancy-looking gift wrapper and call it a “Promise.”

WordPress 5.8 Media Library Changes You Should Know About

Category Image 080

It is hard not to look through a list of upcoming WordPress 5.8 changes and not find at least a little something to whet your appetite. With so many enhancements headed our way, even we have not been able to keep up with them all here at WP Tavern. The next release will bring a few much-needed media-related upgrades.

Users should enjoy WebP image format support and a copy-to-clipboard button on the media upload screen. Developers have a new hook for filtering the image output format, and the platform is dropping infinite scrolling.

WordPress 5.8 is scheduled to ship on July 20, so these changes will be landing in less than a week. If you have not already done so, give WordPress 5.8 Release Candidate 3 a test run and report any issues.

Infinite Scroll Replaced With Ajax Button

The upcoming core release will drop infinite scrolling for media in favor of an Ajax-powered “Load more” button. The admin screen and editor’s media overlay will cap the initial and subsequent “pages” to 40 media items each.

This change is a part of an effort from the WordPress accessibility team to improve the experience for end-users. Team member and core contributor Andrea Fercia noted two a11y problems with infinite scrolling. The first is that it is impossible or nearly for keyboard users to reach content appended to the screen. Second, there is no audible feedback or instructions about how infinite scrolling works for screen readers.

He also noted usability and performance issues. Infinite scroll can break the browser’s history, and there is no JavaScript fallback. And loading hundreds or more large-sized images increases the memory footprint.

While the media library is getting the Ajax treatment in WordPress 5.8, we should expect similar updates for other areas in the future, including:

  • Add Themes Screen
  • Customizer > Add Menu Items
  • Editor > Link > Search

Copy URL From Add New Media Screen

Screenshot of the Add New Media screen after an image has been uploaded.  It now shows a "Copy URL to clipboard" button.
Copy URL to clipboard button on the Add New Media screen.

This change is an enhancement that rids the platform of a small but noticeable nuisance that has plagued it for years. When uploading an image from the Media > Add New screen in the WordPress admin, there was no way to grab its URL without clicking over to the edit screen.

WordPress 5.8 introduces a “Copy URL to clipboard” button that appears after the image has been uploaded. No need to leave the page and track down the URL. The change also makes the user experience consistent with the Media Library screen and overlay in the post editor.

More often than not, browsing Trac means seeing many of the same names. This time around, it seems that a regular user wanted a feature. They created an account — perhaps for this purpose alone –, wrote a support forum post, was directed to Trac, and created their first ticket. It took eight months to work its way into WordPress, but it is one of those success stories of an average user making things happen by just providing feedback. Thanks for the contribution, @anotia.

WebP Image Format Support

WordPress is allowing a new image format. And, no, it is not SVG (technically not an image). There are still security hurdles to jump for that to ever happen. However, it now supports WebP, which carries with it the promise of better performance for those who use it.

As Sarah Gooding reported for WP Tavern last month:

This modern image file format was created by Google in September 2010, and is now supported by 95% of the web browsers in use worldwide. It has distinct advantages over more commonly used formats, providing both lossless and lossy compression that is 26% smaller in size compared to PNGs and 25-34% smaller than comparable JPEG images.

In the report, she noted that only 1.6% (currently at 1.8%) of the top 10 million websites used the WebP format. With WordPress now adding support, that percentage is likely to rise in the coming years.

Developers: Image Editor Output Format Hook

For developers who want to transform images with one mime type to another, 5.8 introduces the image_editor_output_format filter hook. Plugin authors can convert all newly uploaded images or only overwrite specific formats.

The following example converts JPG images to the new WebP format:

add_filter( 'image_editor_output_format', function( $formats ) {
        $formats['image/jpeg'] = 'image/webp';

        return $formats;
} );

The output format will be applied to all image sub-sizes as they are created. However, this will only work for WebP images if the webserver supports it.