typescript 45 lines · 7 steps

Clipboard copy with a legacy fallback

Prefer the modern async Clipboard API, but fall back to a hidden textarea when it isn't available.

Explained by highlit
1export async function copyToClipboard(text: string): Promise<boolean> {
2 if (navigator.clipboard && window.isSecureContext) {
3 try {
4 await navigator.clipboard.writeText(text);
5 return true;
6 } catch {
7 return legacyCopy(text);
8 }
9 }
10 
11 return legacyCopy(text);
12}
13 
14function legacyCopy(text: string): boolean {
15 const textarea = document.createElement("textarea");
16 textarea.value = text;
17 textarea.setAttribute("readonly", "");
18 textarea.style.position = "fixed";
19 textarea.style.top = "-9999px";
20 textarea.style.opacity = "0";
21 
22 document.body.appendChild(textarea);
23 
24 const selection = document.getSelection();
25 const previousRange = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
26 
27 textarea.select();
28 textarea.setSelectionRange(0, text.length);
29 
30 let succeeded = false;
31 try {
32 succeeded = document.execCommand("copy");
33 } catch {
34 succeeded = false;
35 }
36 
37 document.body.removeChild(textarea);
38 
39 if (previousRange && selection) {
40 selection.removeAllRanges();
41 selection.addRange(previousRange);
42 }
43 
44 return succeeded;
45}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Feature-detect capable APIs and degrade gracefully instead of assuming support.
  2. 2Offscreen DOM elements let you drive legacy browser commands without visible flicker.
  3. 3Restoring prior state after a side effect keeps the operation invisible to the user.

Related explainers

typescript
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';

Wiring validation and Swagger docs in NestJS

validation openapi decorators
Intermediate 8 steps
rust
use axum::{extract::{Path, State}, http::StatusCode, Json};
use dashmap::DashMap;
use serde::Serialize;
use std::sync::Arc;

Request coalescing in an Axum handler

caching concurrency request-coalescing
Advanced 8 steps
typescript
import { Component, Input } from '@angular/core';
 
interface Order {
  id: string;

How Angular ICU plurals localize an order summary

i18n pluralization standalone-component
Intermediate 8 steps
javascript
function attachThousandSeparators(input, { locale = 'en-US' } = {}) {
  const formatter = new Intl.NumberFormat(locale);
  const groupSep = formatter.format(11111).replace(/\d/g, '')[0] || ',';
  const decimalSep = formatter.format(1.1).replace(/\d/g, '')[0] || '.';

Live thousand separators without losing the caret

dom intl caret-preservation
Advanced 8 steps
typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, catchError, concatMap, finalize } from 'rxjs';
import { DataSource, QueryRunner } from 'typeorm';
 

Wrapping requests in a transaction with NestJS

interceptors transactions rxjs
Advanced 7 steps
python
from functools import wraps
import asyncio
 
from fastapi import APIRouter, FastAPI, Request

Per-route request timeouts in FastAPI

decorators async timeouts
Intermediate 6 steps

Share this explainer

Here's the card — post it anywhere.

Clipboard copy with a legacy fallback — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code