JSFiddle - React, Tailwind, and code Playground
by Hugo Vale Pereira
HTML
<div id="status">preloading...</div>
<button id="btn" disabled onclick="showAll()">Show all</button>
<div class="grid" id="grid"></div>
CSS
body { font-family: monospace; padding: 20px; background: #111; color: #ccc; }
#status { margin-bottom: 10px; color: yellow; }
button { padding: 10px 20px; font-size: 14px; cursor: pointer; margin-bottom: 20px; }
button:disabled { opacity: 0.4; cursor: not-allowed; }
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.cell { background: #222; padding: 10px; }
.cell h3 { font-size: 11px; margin-bottom: 4px; color: #fff; }
.cell p { font-size: 10px; color: #666; margin-bottom: 6px; }
.cell .time { font-size: 10px; color: #4caf50; min-height: 14px; }
.wrap { height: 140px; background: #333; overflow: hidden; }
.wrap img, .wrap canvas, .wrap svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.bg { width: 100%; height: 100%; background-size: cover; background-position: center; }
JavaScript
const IMG_URL = 'https://picsum.photos/5000/5000'
let blobUrl = null
let bitmap = null
// --- two real-world preload approaches, race them ---
// approach A: fetch() — modern, promise-based
const viaFetch = fetch(IMG_URL)
.then(r => r.blob())
.then(async blob => {
blobUrl = URL.createObjectURL(blob)
bitmap = await createImageBitmap(blob)
console.log('preloaded via fetch()')
})
// approach B: new Image() + canvas — classic, wider compat
const viaImage = new Promise((resolve, reject) => {
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = () => {
const c = document.createElement('canvas')
c.width = img.naturalWidth
c.height = img.naturalHeight
c.getContext('2d').drawImage(img, 0, 0)
c.toBlob(async blob => {
// only use if fetch didn't already get it
if (!blobUrl) {
blobUrl = URL.createObjectURL(blob)
bitmap = await createImageBitmap(blob)
console.log('preloaded via new Image()')
}
resolve()
})
}
img.onerror = reject
img.src = IMG_URL
})
// whoever wins, enable the button
Promise.race([viaFetch, viaImage])
.then(() => {
document.getElementById('status').textContent = 'ready'
document.getElementById('btn').disabled = false
})
.catch(() => {
document.getElementById('status').textContent = 'preload failed'
})
// --- display approaches ---
const approaches = [
{
name: '<img> src direct',
desc: 'sets src on click — downloads fresh',
init: wrap => { const el = document.createElement('img'); wrap.appendChild(el); return el },
show: el => { el.src = IMG_URL + '?r=' + Date.now(); return el }
},
{
name: '<img> src blob',
desc: 'src = blobUrl — from memory',
init: wrap => { const el = document.createElement('img'); wrap.appendChild(el); return el },
show: el => { el.src = blobUrl; return el }
},
{
name: 'createElement +...