Use The Platform

Software Engineer @TapWater

Use the platform means choosing native features/APIs versus 3rd party libraries.

Behind all the APIs we use, are groups of people whose job is to push the Web forward.

TC39

WHATWG

💡

🗣️

slow...🐢

Time
Features
3rd party libraries
The platform
Features that only the platform can implement

Platform Advatages

1. Performance & Bugs

📦 No extra library

🚀 Faster load times

Less JS to download

Less opportunities for bugs

Less code in our app

2. Productivity

📈 Higher chance of devs knowing the standard APIs

Easier to understand, use or refactor the code

1. My Philosoply

1. Start with libraries. Don't wanna get old waiting 👴🏻

2. Continue with native features when they're available

3. Maybe refactor if I have time

Let's look at some examples

1. Image Lazy Loading

Most of the images on the web are downloaded, decoded and rendered only never to be seen, as [...] the user never scrolled that far. - Yoav Weiss

In my first ever paid-project in 2018, I used lazysizes to lazy load images.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Lazy Loading</title>
    <script src="lazysizes.min.js"></script>
</head>
<body>
    <img data-src="image.jpg" class="lazyload" />
</body>
</html>

It is downloaded more than 250k times per week

However, now we have native lazy loading right into the platform 🎉

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Lazy Loading</title>
</head>
<body>
    <img src="image.jpg" loading="lazy" />
</body>
</html>

2. Copy to Clipboard

<body>
  <button
	class="btn"
	data-clipboard-text="Text to copy to clipboard">
    Click me to copy!
  </button>

  <script>
    let clipboard = new ClipboardJS('.btn');

    clipboard.on('success', function (e) {
      e.clearSelection();

      // Now I have it copied to the clipboard
    });

    clipboard.on('error', function (e) {
      // Oops. It failed...
    });
  </script>
</body>

For years I've been using Clipboard JS for this.

function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
  } catch (err) {
    console.log("Oops, something went wrong.", err);
  }
}

Now we can just use the Native Clipboard API!

3. AJAX Requests

const req = new XMLHttpRequest();
req.addEventListener("load", onLoad);
req.addEventListener("error", onError);
req.open("GET", "http://www.example.org/example.txt");
req.setRequestHeader("content-type", "application/json");
req.send();

function onLoad() {
  console.log(req.responseText);
}

function onError(err) {
  console.log("Something failed", err);
}

When I started programming we had to use the XMLHttpRequest to make AJAX calls...

😫

try {
  const response = await fetch('https://example.com/movies');

  const jsonResponse = await response.json();
} catch (err) {
  // Handle error
}

Luckily I haven't written that in maaaany years, because we have the native fetch API.

try {
  const response = await fetch('https://example.com/movies', {
    headers: new Headers({
      'content-type': 'application/json',
    }),
    method: 'POST',
    body: JSON.stringify(body),
  });

  const jsonResponse = await response.json();
} catch (err) {
  // Handle error
}

This is a POST Request with custom Headers:

Thus, we don't necessarily need Axios or JQuery to do this.

pune exemplu cu Axios, cand ar trebui folosit

import axios from 'axios';

try {
  const jsonResponse = await axios.post(
    'https://example.com/movies',
    body,
    {
      'content-type': 'application/json'
    }
  );
} catch (err) {
  // Handle error
}

4. Date/Number formatting

Date
📅 30.10.2022, 09:12
number
💰 2.000,00 RON
let today = new Date();

let roDateFormatter = Intl.DateTimeFormat('ro-RO');
let enDateFormatter = Intl.DateTimeFormat('en-US');

console.log(roDateFormatter.format(today));
  // 12.11.2022

console.log(enDateFormatter.format(today));
  // 11/12/2022

The Intl API is great for serializing dates...

let salary = 5000;

let roRONFormatter = Intl.NumberFormat('ro-RO', {
  style: 'currency',
  currency: 'RON'
});
let roUSDFormatter = Intl.NumberFormat('ro-RO', {
  style: 'currency',
  currency: 'USD'
});

let usRONFormatter = Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'RON'
});

let usUSDFormatter = Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

console.log(roRONFormatter.format(salary));
// 5.000,00 RON
console.log(roUSDFormatter.format(salary));
// 5.000,00 USD  


console.log(usRONFormatter.format(salary));
// RON 5,000.00
console.log(usUSDFormatter.format(salary));
// $5,000.00

... and Currency.

import { startOfWeek, endOfWeek } from 'date-fns';

getWeekInterval(new Date());
// 7 - 13 November

function getWeekInterval(date) {
  let month = Intl.DateTimeFormat('en-US', {
    month: 'long'
  }).format(new Date());
  
  let startDay = startOfWeek(dateToShow);
  let endDay = endOfWeek(dateToShow);
  
  return `${startDay.getDate()} ${month}` +
    `- ${endDay.getDate()} ${month}`;
}

However, for more advanced stuff, we may still need to use libraries like date-fns.

5. Accordions

What if we could built this with native HTML elements, and NO JavaScript?? 🤯🤯🤯

Meet the <details> element

<details>
  <summary> Title </summary>
  <div> Content </div>
</details>

And here's the HTML code:

<details>
  <summary>
    <span class="number">01</span>
    <span class="question">Where are the stores located?</span>
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
      class="icon">
      <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
    </svg>
  </summary>
  <div class="accordion-content">
    <p>
      Lorem, ipsum dolor sit amet consectetur adipisicing elit. Dolorem
      fuga praesentium nisi molestiae illo dolorum unde aliquid tenetur,
      repellendus ut sit tempora necessitatibus, odit, dolor sequi
      laudantium soluta nesciunt atque!
    </p>
    <ul>
      <li>Lorem ipsum dolor sit amet consectetur adipisicing.</li>
      <li>Lorem ipsum dolor sit amet consectetur adipisicing.</li>
      <li>Lorem ipsum dolor sit amet consectetur adipisicing.</li>
      <li>Lorem ipsum dolor sit amet consectetur adipisicing.</li>
      <li>Lorem ipsum dolor sit amet consectetur adipisicing.</li>
    </ul>
  </div>
</details>
    

6. Form Handling

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Form parsing</title>
</head>

<body>
  <form>
    <input type="text" name="username" />
    <input type="number" name="age" />
    <button type="submit"> Submit </button>
  </form>
  <script>
    const form = document.querySelector("form");
    form.addEventListener("submit", (event) => {
      event.preventDefault();

      const formData = new FormData(event.currentTarget);
      console.log(Object.fromEntries(formData));
      // { username: "bob", age: "27" }
    });

  </script>
</body>
</html>

For very basic forms we can use the native APIs to read and process the data.

Even when using libraries/frameworks like React, I sometime prefer the native approach for simplicity.

7. Modals

{ the }

One

of

hardest

things

to

implement

Not Any More! 🎉

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Form parsing</title>
</head>

<body>
  <dialog open>
    <p> Finally, a native dialog! </p>
    <button> Great! </button>
  </dialog>  
</body>
</html>

Open it with show() or showModal()

let dialog = document.querySelector('dialog');

// We can still interact with the rest of the page 
dialog.show();
	
// We cannot interact with the rest of the page
dialog.showModal();

Style the backdrop:

dialog::backdrop {
  background: linear-gradient(#e6646580, #9198e580);
}

Close when clicking the backdrop:

const dialogEl = document.querySelector("dialog");

dialogEl.addEventListener('click', maybeCloseDialog);

function maybeCloseDialog(e) {
  if (e.target.tagName === 'DIALOG') {
    dialogEl.close();
  }
}
<dialog>
  <div class="dialog-content">
    <p> This is a simple dialog </p>
    <button type="button"> Close </button>
  </div>
</dialog>

Not the best but I'll take it...

8. Private Properties

class CreditCard {
  private number;

  constructor() {
    this.number = '...';
  }

  private getNumber() {
    return this.number;
  }

  getInfo() {
    const lastFour = this.getNumber().slice(-4);
    return `Last four numbers: ${lastFour}`;
  }
}

We've been using TypeScript for many years to declare private properties.

Now we finally got native support!

class CreditCard {
  // private variable
  #number;

  constructor() {
    this.#number = '...';
  }

  // private function
  #getNumber() {
    return this.#number;
  }

  getInfo() {
    const lastFour = this.#getNumber().slice(-4);
    return `Last four numbers: ${lastFour}`;
  }
}

We can start writing code using the new syntax!

 

I already started doing this, and slowly migrating old code when I get some extra "free" hours. 🙌

9. Container Queries

Probably the feature I'm most excited about!

And here's how to use them:

.parent {
  container-type: inline-size;
}

@container (max-width: 400px) {
  // ...
}

@container (min-width: 400px) and (max-width: 700px) {
  // ...
}

@container (min-width: 700px) {
  // ...
}

Not full support but getting there. Looking at you Firefox 👀

10. Web Components

<tabs>
    <tab title="Home">
        <!-- ... -->
    </tab>
    <tab title="Settings">
        <!-- ... -->
    </tab>
</tabs>

We were supposed to get an easy way to build custom components, for example tabs:

And replace all the frameworks:

Instead, we got this:

So, whenever you're building something, first search for native APIs.

 

If you don't find any, or they are difficult to use, try 3rd party libraries.

Thanks! 🙏

Use The Platform

By Pava

Use The Platform

The browser is pretty powerful, and we can do a lot of things natively, without using libraries. What are some of these things and why start with the platform first?

  • 273