Real-time Loading Indicator
by Julien Etienne
HTML
<div class="bar"><div class="progress" id="progress"></div></div>
<div id="status"> </div>
<h1 id="progress"> </h1>
<img id="img" />
CSS
.bar{
width: 100%;
height: 1rem;
background: #999;
}
.progress {
background: lime;
width: 0;
height: 100%;
}
JavaScript
const loadingIndicator = async (url, indicator, before, after) => {
if (typeof before === 'function') before();
if (typeof indicator !== 'function') return; // validation error
const response = await fetch(url);
const contentLength = response.headers.get('content-length');
const total = parseInt(contentLength, 10);
let loaded = 0;
const reader = response.body.getReader();
const res = new Response(new ReadableStream({
async start(controller) {
while (true) {
const {
done,
value
} = await reader.read();
if (done) {
if (typeof after === 'function') after(res);
break;
}
loaded += value.byteLength;
const completionValue = loaded / total * 100;
indicator(completionValue);
controller.enqueue(value);
}
controller.close();
},
pull(controller) {
console.log('pull: controller', controller)
},
cancel(reason) {
}
}));
console.log('res', res)
setTimeout(() => {
reader.cancel();
}, 300)
}
const elStatus = document.getElementById('status');
const progress = document.querySelector('#progress');
const status = (text) => elStatus.innerHTML = text;
const url = 'https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg';
const indicator = (amount) => {
progress.style.width = amount + '%';
}
const before = () => status('downloading with fetch()...');
const after = async (res) => {
status('download completed');
const blob = await res.blob();
document.getElementById('img').src = URL.createObjectURL(blob);
}
loadingIndicator(url, indicator, before, after);