12 Rarely Used JavaScript Web APIs that Will Boost Your Website to THE MOON 🚀
Tags: javascript, webdev, beginners, productivity
Published: 2022-07-23 07:43:00

> There are over 97 Web APIs, and you’re only using 5% of them. Let’s unlock that other 95 percent!
> 

After taking a long stroll down the dark realms of the spec, I realized that there were so many technologies out there being left-out.

My goal with this article is to bring them to the light. And I’ll prove to you, using practical examples, that some APIs are really worth a shot!

Each section will thoroughly examine each API and provide a fun example that beautifully demonstrates a practical use-case.

___

## Ad: Like freebies? 🤑

I made a pack of 100 free hover animations. [Get it now](https://www.producthunt.com/posts/100-css-buttons?ref=dev), share it, and do whatever you want with it. It's yours forever! ❤️

[![Every style imaginable.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvcivn0t2ae0tzwtwtc1.png)](https://www.producthunt.com/posts/100-css-buttons?ref=dev)

___

## 📑 Table of Contents

- [🤳 Screen Orientation API](#warning-about-portrait-mode)
- [📺️ Fullscreen API](#making-your-website-a-fullscreen-experience)
- [📲 Intersection Observer API](#animating-an-element-as-it-enters-the-viewport)
- [💤 Screen Wake Lock API](#preventing-the-screen-from-going-dark)
- [💻️ Screen Capture API](#recording-your-screen)
- [📔 IndexedDB API](#storing-tabular-data-in-an-ondevice-database)
- [🧠 Local and Session Storage APIs](#storing-text-data-on-the-device-even-when-the-user-leaves)
- [🎨 Houdini API](#creating-a-locationaware-ripple-effect)
- [🕸️ Web Share API](#showing-a-native-sharing-menu)
- [📋️ Clipboard API](#copying-text-to-the-clipboard)
- [✒️ Selection API](#sharing-the-selected-text)
- [👀 Page Visibility API](#changing-the-title-when-the-user-leaves-the-tab)

## Warning about portrait mode

![Screen is too narrow. try in landscape mode.](https://lh3.googleusercontent.com/9TGRzd_Au_EmpwScLyUw-IOWeAweNTM9sJQN5_2Dp8tl8W7Xu6ClOKL1iCr9Pob-NxpP36gj0shbazYWb1oTocZADcQ2av8ZiUzHladIrSYOH3hlIe1GAL6ft4fGUlDPbjrFuEKbLdVVuhIAwM0)

Screen is too narrow. try in landscape mode.

Some apps like non-linear video editors were not meant for vertical devices: they just don’t work well in narrow screens!

Sure, the [web is supposed to be responsive](https://www.smashingmagazine.com/2011/01/guidelines-for-responsive-web-design/), but it’s not always worth it to port a whole wide layout to a narrow display.

Wouldn’t it be nice if we could warn our users when their device is rotated in the wrong direction? Let me introduce to you… the [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation)!

In order to avoid errors, it’s important to check support for the Screen Orientation API. This is as simple as: `if ('orientation' in screen)`. You’ll see this pattern again and again throughout this article.

### The Screen Orientation API

Browsers expose a global variable named `screen`, which we’ll use to access the information we need. The `[ScreenOrientation](https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation)` instance can be accessed with `screen.orientation`. We’ll be working with this object throughout this section.

### Detecting the screen orientation

![Screen Orientation types and angles: https://w3c.github.io/screen-orientation/#dfn-screen-orientation-values-table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nzaxnpvword2zs2oifz9.png)

Screen Orientation types and angles: https://w3c.github.io/screen-orientation/#dfn-screen-orientation-values-table

Contrary to popular belief, there are *four* ways that a screen can be oriented, as can be seen in the picture above. But we’re only interested in knowing whether the screen is in portrait or landscape mode, and it’s easy to write a function that tells us exactly that:

```jsx
function getOrientation() {  
	const isPortrait = screen.orientation.type.startswith('portrait')  
	return isPortrait ? 'portrait' : 'landscape'
}
```

### Locking the screen orientation

Native apps like Instagram lock the screen orientation while it’s in-use. As [the line between PWAs and native apps](https://www.smashingmagazine.com/2018/12/pwa-native-mobile-apps/) is getting blurrier by the day, it’s not a surprise that this feature is also on the web.

While [less supported](https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation#browser_compatibility), it’s also possible to lock and unlock the screen orientation using this code snippet:

```jsx
screen.orientation.lock(orientation)
```

Don’t forget to [handle errors](https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock#exceptions) too, because as I already stated, this feature is not well-supported.

## Making your website a full-screen experience

{% codepen https://codepen.io/ludviglindblom/pen/medXwN %}

Browsers cover our websites with a lot of UI elements, distracting the user from what’s important.

![Screenshot of Chrome mobile, highlighting the browser’s UI elements.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rww2zhgutyijg9hl2r8.png) 

Screenshot of Chrome mobile, highlighting the browser’s UI elements.

This is especially a problem when it comes to immersive content, such as:

- movies,
- games,
- maximizing images.

and the list goes on-and-on.

Thankfully, the [Fullscreen API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API) comes and saves the day!

This feature is very [well supported in all modern browsers](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API#browser_compatibility), so don’t worry about using it.

### Entering fullscreen mode

Surprisingly, any DOM element can enter fullscreen mode:

```jsx
el.requestFullscreen()
```

However, most of the time, we want the entire page to enter fullscreen. The root document element —`<html>`— can be accessed in JavaScript with `document.documentElement`.

So it’s not unusual to see this code snippet floating around the web:

```jsx
document.documentElement.requestFullscreen()
```

### Leaving fullscreen mode

There’s a variety of ways for exiting. Some of them are the browser-default keyboard shortcuts: `ESC` and `F11`.

There’s also the possibility to leave by switching tabs `Ctrl+Tab` or jumping windows `Alt+Tab`.

However, the most important leaving mechanism is the one that *you* — the developer — provide. You can programmatically disembark fullscreen mode with the following:

```jsx
document.exitFullscreen()
```

In a deployment environment, however, it’s important to avoid errors by checking if this function exists before calling it:

```jsx
if (document.exitFullscreen) {    
	document.exitFullscreen()
}
```

### Verifying if the user is in fullscreen mode

If we wanted to implement a fullscreen toggle as seen in [the Codepen at the start](https://codepen.io/ludviglindblom/pen/medXwN), we’d need a way to determine whether or not fullscreen mode is active.

That’s totally possible with the following code snippet:

```jsx
document.fullscreenElement // returns 'null' or the fullscreen-enabled DOM element
```

For better browser compatibility, we’d have to check for multiple attributes:

```jsx
document.fullscreenElement    
	|| document.mozFullscreenElement    
	|| document.msFullscreenElement    
	|| document.webkitFullscreenElement
```

With this in hand, we can implement a fullscreen toggle:

```jsx
function toggleFullScreen() { 
	if (!document.fullscreenElement) {   
		document.documentElement.requestFullscreen(); 
	} else {  
		if (document.exitFullscreen) {   
			document.exitFullscreen();  
		} 
	}
}
```

## Animating an element as it enters the viewport

{% codepen https://codepen.io/eludadev/pen/eYMmvxq %}

Consider all the times you needed to do something when an element enters into view:

- Animating it,
- Loading more content ([Infinite Scrolling](https://www.smashingmagazine.com/2013/05/infinite-scrolling-lets-get-to-the-bottom-of-this/)),
- [Lazy-loading an image](https://www.smashingmagazine.com/2019/05/hybrid-lazy-loading-progressive-migration-native/),
- Registering ad revenue.

One naive solution is calling `getBoundingClientRect` on every scroll event. And I mean… it works!

However, it’s terribly inefficient. It runs on the [main thread](https://developer.mozilla.org/en-US/docs/Glossary/Main_thread), so the more event listeners that we register, the slower our app becomes.

Thankfully, browser engineers have blessed us with the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API): an efficient solution that delegates all optimizations to the browser, so that we — web developers — can focus on what’s important.

We’re gonna make a pretty cool effect where  text elements are highlighted only when they enter into view, creating a sleek and modern animation that our readers will appreciate. See it with your own eyes in [the Codepen above](https://codepen.io/eludadev/pen/eYMmvxq).

### Creating an observer

Before we start listening for intersection events, we must to create an **observer** object that handles all the background tasks:

```jsx
const options = {  
	root: null, // use viewport  
	threshold: 0.75
}

const observer = new IntersectionObserver(callback, options)
```

You may have noticed `threshold`. It’s an option that tells the browser to only trigger intersection events when *N%* of the element is visible.

### Handling intersection events

Let’s define callback, a function that will be called once an intersection event occurs.

We want to handle the event only when the element is shown at least N% in the viewport:

```jsx
function callback(entry) {
  if (entry.intersectionRatio > 0.75) {
    // ...
  }
}
```

It’s time to decide what to do with an element when it enters into view. In this case, we’ll simply assign it the `.active` class name, while delegating the animation responsibilities to CSS.

```jsx
function callback(entry) {
  if (entry.intersectionRatio > 0.75) {
    entry.target.classList.add('active')
  }
}
```

We can also “undo” this effect once it leaves the screen:

```jsx
function callback(entry) {
  if (entry.intersectionRatio > 0.75) {
    entry.target.classList.add('active')
  } else {
    entry.target.classList.remove('active')
  }
}
```

For a deeper introduction to the IntersectionObserver API, read this [amazing article by Denys Mishunov](https://www.smashingmagazine.com/2018/01/deferring-lazy-loading-intersection-observer-api/).

## Preventing the screen from going dark

![Paused space-launch video on Youtube, showing the video player’s controls.](https://lh6.googleusercontent.com/YDGDh4pkxK81gN5swTPFwTdTrtJFDIw4Mg0G-Gcu_7MdsCCHnu31cMZfKPyosmhQsw0VS3ZKsMEi8jOUxIs1ZpI0YKkTDYGWasmsPXOPllebv2i-97yWmWiO3IOeyk3vDxQGbbQDVThCgDT_SA)

Paused space-launch video on Youtube, showing the video player’s controls.

Long-form videos require the screen to stay on even without any interaction. This behavior is usually seen on native applications, but with the [Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API), it’s on the web too!

There are many other use-cases for this API:

- Online games,
- Presentations,
- Drawing on canvas,
- Camera,
- Streaming,
- Timers.

and the list never stops.

Let’s delve deeper into its inner-workings!

> Firefox and Safari have yet to support this feature. So it’s always a good idea to check its availability first, in order to avoid all kinds of errors: if ('wakelock' in navigator)
> 

### Acquiring wakelock

A video player, such as Youtube, might acquire wakelock in its `play` function:

```jsx
let wakelock = null

async function play() {
    // … 
	wakelock = await navigator.wakeLock.request('screen')
}
```

If the user’s battery is too low, [expect it to fail](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API#requesting_a_wake_lock).

### Releasing wakelock

It’s bad practice to keep the wakelock on forever, as that will hurt the user’s battery and might even degrade performance. So make sure to always release it when possible:

```jsx
async function pause() {
  // ...
  await wakelock.release()
}
```

Whenever the user leaves your website’s tab, the wakelock is automatically released.

In this case, you should re-acquirese it by listening to the `visibilitychange` event, which we’ll learn more about [in another section](https://www.notion.so/Rarely-Used-JavaScript-Web-APIs-that-Will-Take-Your-Website-to-The-Next-Level-71849681706142f3bd7681ad2e4a753f).

But in a nutshell, it’s triggered when the user leaves/enters the website’s tab.

```jsx
document.addEventListener('visibilitychange', async () => {  
    if (wadocument.addEventListener('visibilitychange', async () => {  
    if (wakelock !== null && document.visibilityState === 'visible') {    
        wakelock = await navigator.wakeLock.request('screen')  
    }
})
```

## Recording your screen

{% codepen https://codepen.io/eludadev/pen/MWVYoOE %}

There’s an increasing number of web-based screen recording apps. But how exactly do they do it? The answer is surprisingly simple.

The secret to their success is the [Screen Capture API,](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API) an easy-to-use interface that allows users to record their screen in a wide variety of ways:

- Whole screen,
- Specific window,
- Specific tab.

It also comes with extra nifty features, including but not limited to:

- blurring/covering overlapping windows to avoid accidentally sharing sensitive information,
- hiding/showing the cursor,
- recording sound.

### Browser compatibility

I hate to be the bearer of bad news, but [no mobile browsers support this API](https://caniuse.com/mdn-api_mediadevices_getdisplaymedia) as of yet.

On the other hand, it’s very well-supported by modern desktop navigators! ([except Internet Explorer](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/), of course)

### Starting screen capture

With this delight of an API, recording the screen is shockingly simple:

```jsx
const options = {
    video: {
        cursor: 'always' // show the cursor
    },
    audio: false // don't record audio
}
navigator.mediaDevices.getDisplayMedia(options) // returns a promise
```

Would you believe me if I told you that was it? Well, I never lie.

![Screen Capture Prompt showing the 3 types: entire screen, window, tab.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dwbpkbvgj8mw2zgoewr.png) 

Screen Capture Prompt showing the 3 types: entire screen, window, tab.

The above function tells the browser to show a prompt for selecting the desired recording surface, as can be seen on the image above. (try it yourself in [the codepen at the start](https://codepen.io/eludadev/pen/MWVYoOE) of this section)

### Preview the recording

It would be nice if we could see exactly what the website is seeing. Thankfully, that’s terribly easy to do:

```html
<video autoplay id="preview"></video>
```

And that was it for the HTML. Now, let’s move into the JavaScript logic:

```jsx
previewElem = document.getElementById('preview')
previewElem.srcObject = await navigator.mediaDevices.getDisplayMedia(options)
```

That’s it! Now you can see what’s being recorded, in real time.

### Stopping screen capture

With one method, we can achieve everything! Note that it’s important to have a preview element first, as demonstrated in the last subsection.

```jsx
let tracks = previewElem.srcObject.getTracks()

tracks.forEach(() => track.stop())
previewElem.srcObject = null
```

## Storing tabular data in an on-device database

{% codepen https://codepen.io/eludadev/pen/VwXYbPr %}

There’s an entire NoSQL database system hidden right in your browser, and it’s accessed with the [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) API!

Each operation is asynchronous, so it never slows down other operations. It’s also cleared once the user erases the browser’s cache or locally-stored data.

In addition to all of that, it also supports the usual search, get, and put actions, with transactions on the side. It can store almost all kinds of data, including but not limited to `File`, images and videos as `Blob`, and `String` of course.

> Unfortunately, not all browsers agree on which kind of data should be supported. Safari on iOS, for example, cannot store Blob data. However, it’s possible to [convert all other formats to an `ArrayBuffer`](https://web.dev/indexeddb-best-practices/#not-everything-can-be-stored-in-indexeddb-on-all-platforms), which is very well-supported by all platforms.
> 

Storage limit is not an issue, because [most browsers allocate a bunch of space that your websites can consume freely](https://web.dev/storage-for-the-web/#how-much). Also, each database is tied not only to a domain name, but to the very specific subdomain. To add to all of that, [browser compatibility](https://caniuse.com/mdn-api_indexeddb) is not an issue at all, not even on IE11.

There are many things that we can do with this treat of an API, including but not limited to:

- Storing structured data for offline use,
- Speeding up the loading time for repeated visits,
- Caching user-generated data,
- Temporarily saving data before uploading it to a server.

Let’s see how we can store contact data in IndexedDB!

### Using IndexedDB

Before we can do anything, we should use a [wrapper library](https://www.npmjs.com/package/idb) on top of IndexedDB because, by default, it’s too complicated; it uses events instead of promises.

```jsx
import { openDB } from 'idb';

const db = await openDB('contacts', 1, {
    // Creates a new database if it doesn't exist.
    upgrade(db) {
        db.createObjectStore('contacts', {
            // The 'id' property of the object will be the key.
            keyPath: 'id',
            // If it isn't explicitly set, create a value by auto incrementing.
            autoIncrement: true,
        })
        
        // Create an index on the 'name' property of the objects.
      	store.createIndex('name', 'name');
    }
})
```

And with that done, we can start storing structured data like it’s no one’s business!

```jsx
await db.add('contacts', {
    name: 'John Doe',
    age: 25,
    email: 'johndoe@john.doe'
})
```

And we can just as easily retrieve all of it:

```jsx
// Get all contacts in name order:
const contacts = await db.getAllFromIndex('contacts', 'name')
```

That’s all we need to know for our simple use-case. But if you’re still interested, you can delve deeper into [the documentation](https://www.npmjs.com/package/idb#examples).

## Storing text data on the device even when the user leaves

![Newsletter form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iooy4ha5fxbjjw1yipc8.png) 

While we can use IndexedDB to store large and complicated data on the browser, it’s still important to consider those other times when all we need to save is a simple key-value pair:

- Login information,
- Newsletter subscription state,
- Cookies consent,
- Analytics tracking pixels.

> It’s important to note that sensitive information (passwords, credit card information, social security numbers, etc…) should NEVER be saved in the browser, because it’s [vulnerable to XSS attacks](https://auth0.com/blog/secure-browser-storage-the-facts/) in addition to [other exploits](https://fingerprint.com/blog/indexeddb-api-browser-vulnerability-safari-15/).
> 

There’s a special utensil for such simple cases, and it’s called the [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API). And just like IndexedDB, it’s tied to the particular subdomain. It’s also cleared if the user empties the browser’s cache or data.

In this API, you’ll find two types of storage: `localStorage` and `sessionStorage`. They offer different benefits:

- Local storage *persists* data even when the user leaves the website, unlike session storage which clears all data as soon as the tab is closed.
- Local storage can store more data, unlike session storage which is maxed-out at 5MB.

### Using local storage

Say that we’re implementing a newsletter subscription form. We don’t want to keep showing it to the user even after they’ve subscribed, so we’ll use the `localStorage` global variable to conditionally display it:

```jsx
function subscribe() {
    // ...
    localStorage.setItem('is-newsletter-subscribed', true)
}
```

You can now [use DevTools to see the new item](https://developer.chrome.com/docs/devtools/storage/localstorage/), saved right in your computer.

And now, let’s write a function that decides whether or not to show the subscription form:

```jsx
function isSubscribed() {
    return localStorage.hasOwnProperty('is-newsletter-subscribed') ? localStorage.getItem('is-newsletter-subscribed') : false
}
```

As you can see, we’re first checking if the `newsletter-subscribed` item exists. If it does, we simply return its value using `getItem()`, otherwise, we return `false` because the user hasn’t subscribed yet.

## Creating a location-aware ripple effect

{% codepen https://codepen.io/eludadev/pen/abYzWeE %}

With the advance of the web, special effects have grown too. These days, CSS properties are just not enough to achieve our wildest dreams.

Our last resort used to be GIFs and images, but with the [CSS Painting API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Painting_API), that’s not the case anymore!

Now, we can use all the stylish effects that come with the HTML Canvas to draw anything over an element’s background.

Browser compatibility is not that great. [Firefox and Safari on iOS are yet to support it.](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Painting_API#browser_compatibility) Therefore, it’s very important to run the following: `if ('paintWorklet' in CSS)`

Let’s build a ripple effect without any pseudo-elements, inspired by [Google’s own implementation](https://github.com/GoogleChromeLabs/houdini-samples).

### The JavaScript Logic

For this effect to work, we need to use JavaScript events to get the cursor’s `x` and `y` positions:

```jsx
// index.js

button.addEventListener('click', (e) => {
    const {x: offsetX, y: offsetY} = button.getBoundingClientRect()
    const [x, y] = [e.clientX - offsetX, e.clientY - offsetY]
    
    button.style.setProperty('--x', x)
    button.style.setProperty('--y', y)
	// ...
})
```

Since the ripple effect is an animation that evolves over time, we need to keep track of its timeline using a `tick` variable:

```jsx
// index.js

button.addEventListener('click', (e) => {
    // ...
    
    const start = performance.now()
    requestAnimationFrame(function step(now) {
        const tick = Math.floor(now - start)
        button.style.setProperty('--tick', tick)
        
        // Continue animation
        requestAnimationFrame(step)
    })
})
```

The above code uses `[requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)` to create an efficient and optimized animation. During each animation step, we calculate the “tick” and assign it to a CSS property.

If we leave it like this, it will run forever. So let’s add an “end condition” to end the animation. We’re gonna stop it when it reaches 1 second (meaning 1000 milliseconds):

```jsx
// index.js

button.addEventListener('click', (e) => {
    // ...
    
    requestAnimationFrame(function step(now) {
		const tick = Math.floor(now - start)
        button.style.setProperty('--tick', tick)
        
        // Stop the animation after 1 second
        if (tick > 1000) {
            button.style.setProperty('--tick', 0)
            return
        }
        
        // Continue animation
        requestAnimationFrame(step)
    })
})
```

And that’s it for the logic!

### The Paint Worklet

Let’s make the actual ripple effect, using the Paint API.

This effect should go into a separate file, which we’ll call `ripple.js`.

Let’s start by retrieving the CSS properties that we just defined:

```jsx
// ripple.js

registerPaint('ripple', class {
    static get inputProperties() {
        return ['--x', '--y', '--tick']
    }
})
```

Next, we’ll use the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) to draw a circle into the button’s background:

```jsx
// ripple.js

registerPaint('ripple', class {
    //...
    
    paint(ctx, {width}, props) { 
        // Retrieve props
        const x = parseFloat(props.get('--x').toString())
        const y = parseFloat(props.get('--y').toString())
        let tick = parseFloat(props.get('--tick').toString())
        
        // Clamp tick in [0, 1000]
        if (tick < 0) tick = 0
        if (tick > 1000) tick = 1000

        // Draw ripple
        const rippleColor = 'rgba(255,255,255,0.54)'
        ctx.fillStyle = rippleColor
        ctx.globalAlpha = 1 - tick/1000
        ctx.arc(
        	x, y, // center
            width * tick/1000, // radius
            0, 2 * Math.PI // full circle
        )
        
        ctx.fill()
    }
})
```

### Registering the paint worklet

Go back into your `index.js` file, and add the following code:

```jsx
// index.js

if ('paintWorklet' in CSS) {
    CSS.paintWorklet.addModule('ripple.js')
}
```

It will first check if CSS Paint API is supported, and only then will it link the ripple effect.

And we’re done! All that’s left is to use this effect. So add the following code to your CSS:

```css
button {
    background-color: #0d1117;
    background-image: paint(ripple);
}
```

For a deeper introduction to the CSS Paint API, read this [amazing article by Adrian Bece](https://www.smashingmagazine.com/2020/03/practical-overview-css-houdini/).

## Showing a native sharing menu

{% codepen https://codepen.io/eludadev/pen/qBoEjqM %}

There is so much content on the web that we may want to share with others:

- links,
- images,
- paragraphs.

and the list never ends.

Normally, a developer would implement their own sharing system with links to Twitter, Facebook, and other social media sites.

These components, however, always fall short compared to their native counterparts, which come with a gigantic quantity of options:

- sharing with contacts,
- sharing with other apps,
- sharing over bluetooth,
- copying to clipboard.

and the list, again, never ends.

These native sharing menus used to be exclusive to native applications, but with the [Web Share API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API), that fact is no longer true.

[Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API#browser_compatibility) is excellent in mobile browsers, but struggles a bit when it comes to Firefox on desktop.

Try it yourself in the Codepen above, and if it’s not supported in your device, here’s what it *can* to look like:

![Sharing menu with many options including Gmail, Messages, Reddit, and LinkedIn.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y1bv0w4szwdvhnx43h4q.png) 

Sharing menu with many options including Gmail, Messages, Reddit, and LinkedIn.

### Sharing URLs

The method to look for is `navigator.share`. It takes an object containing a title, a string of text, and a URL.

```jsx
const shareData = {
  title: 'Smashing Magazine',
  text: 'Smashing Magazine — For Web Designers And Developers',
  url: 'https://www.smashingmagazine.com/'
}

await navigator.share(shareData)
```

Note that this function is protected by [transient activation](https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation), meaning it requires a UI event (like clicking) before it can be handled.

## Copying text to the clipboard

{% codepen https://codepen.io/eludadev/pen/ZExYyeX %}

The clipboard is one of the most underrated features in today’s computers. Would we developers survive without the constant `Ctrl+C`’ing code from Stackoverflow? Doubt it.

Clipboards are all about *moving digital information* from point A to point B. The only alternative is rewriting content by-hand, which is a huge opportunity for errors. Modern clipboards also allow the copying of images and other forms of media.

With the advent of the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API), developers can shine the beauty of UX on their users by programmatically copying important information to the user’s clipboard. This feature is also seen everywhere, from code in the MDN website to Twitter. It’s only missing in Stackoverflow, and for [good reason](https://stackoverflow.blog/2019/11/26/copying-code-from-stack-overflow-you-might-be-spreading-security-vulnerabilities/).

[Browser compatibility is also great](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#browser_compatibility), except IE of course.

### Using the Clipboard API

Copying text is extremely simple:

```jsx
await navigator.clipboard.writeText('Howdy, partner!')
```

And reading it is just as easy:

```jsx
const text = await navigator.clipboard.readText()
```

## Sharing the selected text

![Selected text on a blog with a sharing tooltip on top of it.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/isn1ni1852olwl5yko84.png) 

Selected text on a blog with a sharing tooltip on top of it.

Multiple blogs such as Medium allow users to effortlessly share selected text with other social platforms.

Being such a useful feature, it encourages the sharing of content, and as a result, it grows the blog to enormous proportions.

We already saw how to invoke a [native sharing menu in a previous section](about:blank#showing-a-native-sharing-menu), so let’s just focus on text selection.

Also, we won’t see [how to add a tooltip on top of selected text](https://www.smashingmagazine.com/2021/02/designing-tooltips-mobile-user-interfaces/), but we‘ll delve into using the [Selection API](https://developer.mozilla.org/en-US/docs/Web/API/Selection_API) to retrieve the selected portion of text, because this whole article is about APIs and their use-cases.

And no need to worry about the [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection#browser_compatibility), because it’s just perfect!

### Getting the selected text

This is a terribly easy thing to do:

```jsx
const text = window.getSelection().toString()
```

That’s it! Now seek the [previous section on the Web Share API](about:blank#showing-a-native-sharing-menu) to pop-up an OS-defined sharing menu, and let your users go wild!

## Changing the title when the user leaves the tab

![page-visibility.gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d4mn66h4c1eb68jievl.gif) 

Website title changing from “Shopping website” to “Please stay” when the user leaves the tab.

It’s possible for a website to tell if it’s being viewed or not with the [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API).

While I don’t advocate using the Page Visibility API to grab the user’s attention with annoying messages, it has many positive use-cases:

- showing new notifications,
- reporting engagement analytics,
- pausing video and audio,
- stopping an image carousel.

[Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API#browser_compatibility) is not an issue.

### Detecting page visibility

We can get the page’s visibility state any time with the following code:

```jsx
document.visibilityState // 'visible' or 'hidden'
```

But real use-cases require listening to events and respectively changing some behavior.

Unfortunately, the event name varies by browser, so we have to do the following:

```jsx
let hidden;
let visibilityChange;
if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
  hidden = "hidden";
  visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
  hidden = "msHidden";
  visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
  hidden = "webkitHidden";
  visibilityChange = "webkitvisibilitychange";
}
```

We then can listen for page visibility events, like this:

```jsx
document.addEventListener(visibilityChange, handleVisibilityChange);

function handleVisibilityChange() {
  if (document[hidden]) {
    // page is hidden
  } else {
    // page is visible
  }
}
```

For the purposes of our demonstration, we’ll just change the document title:

```jsx
function handleVisibilityChange() {
  if (document[hidden]) {
    document.title = 'Please stay!!!'
  } else {
    document.title = 'Shopping website'
  }
}
```

Note that I don’t recommend doing this, as it’s just annoying to the user and a fundamental malpractice in ethical web design.

## Conclusion

Web APIs these days are bridging the gap between the web applications and the native applications.

The web is starting to become a real threat to the monopolies created by the App Store and the Google Play Store, and it’s showing no signs of stopping. Let’s discuss this in the comment section below!

There are many more APIs that we haven’t explored yet, and some can do unbelievable things like [scanning bar-codes](https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API) and even [recognizing speech](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API)! So stay tuned for a part 2! :heart:

## Honorable Mentions

It would be a shame to not mention another group of APIs that are very rarely used, and yet have so many interesting and practical use-cases:

- [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API)
- [Sensor API](https://developer.mozilla.org/en-US/docs/Web/API/Sensor_APIs)
- [Vibration API](https://developer.mozilla.org/en-US/docs/Web/API/Vibration_API)
- [Performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API)
- [Resize Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API)

## Like freebies? 🤑

I made a pack of 100 free hover animations. [Get it now](https://www.producthunt.com/posts/100-css-buttons?ref=dev), share it, and do whatever you want with it. It's yours forever! ❤️

[![Every style imaginable.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvcivn0t2ae0tzwtwtc1.png)](https://www.producthunt.com/posts/100-css-buttons?ref=dev)
 
___

{% user eludadev %}