Superpowers of browser's Web API
Nikola Mitrović
Development Lead & Software Engineer
@ Vega IT
Web API
console.log(1);
setTimeout(() => console.log(2), 0);
console.log(3);
Web API
?
Collection of built-in interfaces that allow developers to interact with and manipulate web pages and web applications within a web browser.
Web API
- Web API docs
Nikola Mitrović
Development Lead & Software Engineer
Vega IT
Novi Sad, Serbia
Intersection Observer API
-
Provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport
Intersection Observer API
import { useState, useEffect, RefObject } from 'react';
export const useVisible = (
ref: RefObject<HTMLDivElement>,
options = {
root: null,
rootMargin: '0px',
threshold: 1.0
}
) => {
const [isVisible, setIsVisible] = useState<boolean>(false);
useEffect(() => {
const { current: elementRef } = ref;
if (elementRef) {
const observer = new IntersectionObserver(
([entry]) => setIsVisible(entry.isIntersecting), options
);
observer.observe(elementRef);
return () => observer.unobserve(elementRef);
}
}, [ref, options]);
return isVisible;
};
Intersection Observer API - Use Cases
Lazy-load images
Infinite scroll lists
Defer animations
Save compute power
03
02
04
01
Intersection Observer API - Browser Support
Page Visibility API
-
Provides events you can watch for to know the current visibility state of the page
-
Triggers when user
switches tab
minimizes window
puts another window as overlay
locks/turns off device
Page Visibility API
import { useEffect, useState } from "react";
export const usePageVisibility = () => {
const [pageVisible, setPageVisible] = useState<boolean>(true);
useEffect(() => {
const handleVisibilityChange = () => {
const isPageVisible = document.visibilityState === "visible";
setPageVisible(isPageVisible);
};
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, []);
return pageVisible;
};
Page Visibility API - Use Cases
Improving performance
01
Pause audio/video
02
Detect page visit count
03
Poll server data
04
Carousel
05
Page Visibility API - Browser Support
Screen Wake Lock API
-
Prevent devices from locking the screen when an application needs to keep running
-
Only visible (active) documents can acquire the screen wake lock
Screen Wake Lock API
import { useEffect, useCallback, useRef } from 'react';
export const useWakeLock = (shouldWakeLock = false) => {
let sentinel = useRef<WakeLockSentinel | null>(null);
const requestWakeLockSentinel = useCallback(async () => {
sentinel.current = await navigator.wakeLock.request('screen');
}, []);
const releaseWakeLockSentinel = useCallback(async () => {
await sentinel.current?.release();
sentinel.current = null;
}, []);
useEffect(() => {
(async () => {
if (shouldWakeLock) {
await requestWakeLockSentinel();
return releaseWakeLockSentinel;
}
await releaseWakeLockSentinel();
})();
}, [shouldWakeLock, requestWakeLockSentinel, releaseWakeLockSentinel]);
return { isWakeLock: !!sentinel.current };
};
Screen Wake Lock API - Use Cases
Reading an ebook
Presenting to an audience
Following a recipe
Map navigation
Scanning a QR / barcode
03
05
02
04
01
Screen Wake Lock API - Browser Support
Screen Wake Lock API - Drawbacks
Network API
-
Provides information about the system's connection, such as the current bandwidth of the user's device or whether the connection is metered
-
Detect connection changes
Network API
import { useState, useEffect } from "react";
enum ENetworkEffectiveType {
SLOW_TWO_G = "slow-2g",
TWO_G = "2g",
THREE_G = "3g",
FOUR_G = "4g",
}
export const useNetwork = () => {
const [isFastNetwork, setIsFastNetwork] = useState<boolean | null>(null);
useEffect(() => {
if ("connection" in navigator) {
const { connection: { effectiveType } } = navigator;
setIsFastNetwork(effectiveType === ENetworkEffectiveType.FOUR_G);
}
}, []);
return isFastNetwork;
};
Network API
import { useState, useEffect } from "react";
enum ENetworkEffectiveType {
SLOW_TWO_G = "slow-2g",
TWO_G = "2g",
THREE_G = "3g",
FOUR_G = "4g",
}
export const useNetwork = () => {
const [isFastNetwork, setIsFastNetwork] = useState<boolean | null>(null);
const updateConnectionStatus = ({ target: { effectiveType } }: Event) => {
setIsFastNetwork(effectiveType === ENetworkEffectiveType.FOUR_G);
};
useEffect(() => {
if ("connection" in navigator) {
const { effectiveType } = navigator.connection;
setIsFastNetwork(effectiveType === ENetworkEffectiveType.FOUR_G);
navigator.connection.addEventListener("change", updateConnectionStatus);
}
}, []);
return isFastNetwork;
};
Network API - Use Cases
Notification to the user
Preload large resources
02
01
Network - Browser Support
Background Sync API
-
Provides a way to defer tasks to be run in a service worker until the user has a stable network connection
Background Sync API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
Background Sync API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
self.addEventListener("sync", (event) => {
if (event.tag === "sync-planets") {
event.waitUntil(fetchPlanets());
}
});
Background Sync API - Use Cases
Offline capabilities
Improving UX
02
01
Background Sync API - Browser Support
Background Sync API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
self.addEventListener("sync", (event) => {
if (event.tag === "sync-planets") {
event.waitUntil(fetchPlanets());
}
});
Background Sync API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
const fetchPlanets = async () => {
const planetsResponse = await fetch(
"https://exoplanetarchive.ipac.caltech.edu"
);
const planets = await planetsResponse.json();
};
self.addEventListener("sync", (event) => {
if (event.tag === "sync-planets") {
event.waitUntil(fetchPlanets());
}
});
Broadcast Channel API
-
Allows basic communication between browsing contexts (that is, windows, tabs, frames, or iframes) and workers on the same origin
-
Bi-directional communication
Broadcast Channel API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
const fetchPlanets = async () => {
const planetsResponse = await fetch(
"https://exoplanetarchive.ipac.caltech.edu"
);
const planets = await planetsResponse.json();
};
self.addEventListener("sync", (event) => {
if (event.tag === "sync-planets") {
event.waitUntil(fetchPlanets());
}
});
Broadcast Channel API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
const broadcast = new BroadcastChannel("planets-channel");
const fetchPlanets = async () => {
const planetsResponse = await fetch(
"https://exoplanetarchive.ipac.caltech.edu"
);
const planets = await planetsResponse.json();
broadcast.postMessage({ planets });
};
self.addEventListener("sync", (event) => {
if (event.tag === "sync-planets") {
event.waitUntil(fetchPlanets());
}
});
Broadcast Channel API
import React from "react";
import { Link } from "react-router-dom";
export const FlightsLink = () => {
useEffect(() => {
const broadcast = new BroadcastChannel("planets-channel");
broadcast.onmessage = ({ data: { planets } }) => setPlanets(planets);
return () => broadcast.close();
}, []);
const registerPlanetsSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
await registration.sync.register("sync-planets");
} catch {
console.log("Background Sync could not be registered!");
}
};
return (
<Link
to="rocket"
onMouseEnter={registerPlanetsSync}
>
EXPLORE
</Link>
)
}
const broadcast = new BroadcastChannel("planets-channel");
const fetchPlanets = async () => {
const planets = await fetch(
"https://exoplanetarchive.ipac.caltech.edu"
).then((response) => response.json());
broadcast.postMessage({ planets });
};
self.addEventListener("sync", (event) => {
if (event.tag === "sync-planets") {
event.waitUntil(fetchPlanets());
}
});
Broadcast Channel API - Use Cases
Detect user action
Send messages to workers
02
01
Broadcast Channel API - Browser Support
Web API - Bonus points
-
Beacon API
-
Media Session API
-
Web Speech API
-
Battery API
-
Geolocation API
-
Background Fetch API
-
Web Storage API
Web API - Takeaways
Standardized code
Standardized code
Standardized code
Smooth learning curve
Increase performance
03
05
02
04
01
With great power comes great responsibility
Green JavaScript
Internet is consuming 21% of the electricity
check out carbon footprint
website traffic
energy intensity
data transfers
Thank you!
n.mitrovic@vegait.rs
You can find me at
Link to the slides
https://github.com/nmitrovic-vega/superpowers-web-api
Grazie
Web Day 2023 - Superpowers of browser's Web API
By nmitrovic
Web Day 2023 - Superpowers of browser's Web API
- 242