Removing empty form fields from FormData

Browsers by default submit all the fields in a HTML form even when these fields are empty and not set by user. This could be undesirable if you validate your forms using JSON Schema, for example if you have required fields in your form your JSON Schema validator won't throw required error for such empty fields. Not only this, if you don't want to write such empty values to your database, you need to sanitize data posted by such forms. Instead you can remove such fields before validation and submission to backend. Here is one way to do this in your form's submit event listener:

// removing empty fields before validation/submission
const myFormData = new FormData(myFormElement);
for (const [key, value] of Array.from(myFormData.entries())) {
    if (value.length === 0) {
        myFormData.delete(key)
    }
}

Note that we use Array.from() to avoid changing FormData during iteration which can result in keys not being deleted properly as explained here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#concurrent_modifications_when_iterating

Error handling for fetch requests

try {
  const response = await fetch('https://restcountries.com/v4.1/all');
  if (response.ok) {
    console.log('Promise resolved and HTTP status is successful');
    // ...do something with the response
  } else {
    // Custom message for failed HTTP codes
    if (response.status === 404) throw new Error('404, Not found');
    if (response.status === 500) throw new Error('500, internal server error');
    // For any other server error
    throw new Error(response.status);
  }
} catch (error) {
  console.error('Fetch', error);
  // Output e.g.: "Fetch Error: 404, Not found"
}

Reference: https://dionarodrigues.dev/blog/fetch-api-do-you-really-know-how-to-handle-errors

Converting Multi-Page apps to SPA

If you want to build a SPA (single page application) from a MPA (multi page application) you don't always need a giant framework. What you need mainly is to set an event handler on your links to disable their default behaviour...

Using Location API on anchor elements

Did you know that a HTML anchor element can be treated as a Location object? This means similar to how you can use window.location to read or write different parts of a URL, you can do the same with an anchor element. For example if you have a link like this:

<a id="myLink" href="https://mydomain/products.html?category=clothing#productName">test</a>

You can get the value of hash in this way:

const hash = document.getElementById('myLink').hash.substring(1); // returns productName

You can use different properties of Location object on an anchor element and same as with window.location you can both read and write these properties. For example to get the value of category from previous link you can use this:

const params = document.getElementById('myLink').search;
const category = new URLSearchParams(params).get('category'); // clothing

For more info see: https://developer.mozilla.org/en-US/docs/Web/API/Location

Sorting items in an array by an arbitrary order

Sometimes you need to display data that you fetch from backend in a custom order in frontend. The following snippet shows how to order objects in our array using a custom order defined in another array. Note that to avoid mutation we need to use WEB API's structuredClone() method instead of spread syntax (...) as spread syntax only does a shallow copy of array items and hence won't be able to clone objects inside an array.

const myArray = [
  {name: 'firstname', value: 'Tom'},
  {name: 'age', value: 61},
  {name: 'lastname', value: 'Cruise'}
];
const mySortOrder = ['firstname', 'lastname', 'age'];
const sortArray = (myArray, mySortOrder) => {
  myArray.sort((a, b) => {
    const indexA = mySortOrder.indexOf(a.name);
    const indexB = mySortOrder.indexOf(b.name);
    return indexA - indexB;
  });
  return myArray;
}
const sortedArray = sortArray(structuredClone(myArray), mySortOrder);
console.log(sortedArray);