レイアウトシフトと長時間タスクの対策
画像の読込み前後で段落の位置を測り、幅と高さを予約した場合と比較します。続いて画面から遠い画像の遅延読込みを確認します。後半ではタイマーを待たせる連続処理と、途中で他のタスクへ処理を譲る分割を比較します。
説明
ページの使いにくさには、読込み後に内容が動く問題と、処理中に次の操作を受け付けられない問題があります。画像の領域を先に確保することと、JavaScriptの長い処理を分割することは、それぞれ違う原因への対策です。
画像の読込み前後で段落の位置を測り、幅と高さを予約した場合と比較します。続いて画面から遠い画像の遅延読込みを確認します。後半ではタイマーを待たせる連続処理と、途中で他のタスクへ処理を譲る分割を比較します。
1寸法を指定していない画像
srcのない画像と、その後ろの段落を用意します。
(globalThis.doc=await loadDocument("/performance")画像と段落の文書,globalThis.photo=doc.getElementById("photo"),globalThis.startY=rect("#caption").y読込み前の段落位置,{image:rect("#photo"),captionY:startY})まだ画像自体の寸法が分からず、後続段落は現在の画像領域に続いて配置されます。altの表示なども初期寸法に影響するため、実際の位置を保存して比較します。
2画像の読込みで段落が動く
200×100の画像を読み込みます。
(photo.src=画像を要求"/asset.svg?tag=unreserved",await photo.decode()画像の復号完了を待つ,{image:rect("#photo"),captionY:rect("#caption").y,move:段落の移動量Math.round((rect("#caption").y-startY)*100)/100})画像の実寸が決まり、後続段落の位置が変わります。こうした読込み後の移動は、読んでいた場所や押そうとしていた場所をずらす原因になります。位置差の測定と、複数のずれを集計するCLSの計算は別です。
3画像用の幅と高さを先に確保する
新しい同じ文書で、srcより先にwidthとheightを指定します。
(globalThis.doc=await loadDocument("/performance"),globalThis.photo=doc.getElementById("photo"),photo.width=200,photo.height=100画像の領域を先に指定,globalThis.reservedY=rect("#caption").y予約後の段落位置,{image:rect("#photo"),captionY:reservedY})画像が届く前から200×100の領域を確保します。widthとheightは表示比率にも使われるため、実画像の比率に合う値を指定します。
4寸法が一致すれば位置が変わらない
同じ200×100の画像を読み込みます。
(photo.src="/asset.svg?tag=reserved",await photo.decode()画像が届いた後,{captionY:rect("#caption").y,move:Math.round((rect("#caption").y-reservedY予約前後の差)*100)/100})段落の移動量は0です。必要な領域を先に確保したことで、画像が届いても後ろの文章の位置を保てました。
5遠い画像を遅延読込みにする
文書の下へ10000pxの間隔を置き、loading=lazyの画像を加えます。
(globalThis.spacer=doc.createElement("div"),spacer.style.height="10000px"表示領域から遠ざける,doc.body.append(spacer),globalThis.lazy=doc.createElement("img"),lazy.id="lazy",lazy.width=200,lazy.height=100,lazy.loading="lazy"遅延読込みを指定,lazy.alt="遅延画像",lazy.src="/asset.svg?tag=lazy",doc.body.append(lazy),await pause(50),{loading:lazy.loading,complete:lazy.complete,requested:サーバーへの要求有無(await(await fetch("/requests")).json()).tags.includes("lazy")})この配置では、遠い画像の要求がまだ始まっていません。lazyの取得開始距離はブラウザや条件で変わるため、「表示領域へ入った瞬間だけ読み込む」という厳密な境界ではありません。先に寸法を指定し、取得の遅延で配置が崩れないようにします。
6画像を表示領域へ近付ける
画像までスクロールし、復号を待ちます。
(lazy.scrollIntoView()画像を表示領域へ移動,await lazy.decode()取得・復号を待つ,{complete:lazy.complete,width:lazy.naturalWidth取得後の画像幅,requested:(await(await fetch("/requests")).json()).tags.includes("lazy")})画像が取得され、naturalWidthは200になります。loading=lazyは取得タイミングの制御で、取得後の画像形式や内容を変更するものではありません。
7長い処理がタイマーを待たせる
0msのタイマーを予約し、約80msの連続処理を実行します。
(globalThis.order=[],setTimeout(()=>order.push("timer"),0)次のタスクとして予約,order.push("start"),busy(80)約80msの連続処理,order.push("work-end"),await pause(20)他のタスクが動く機会を作る,order)start→work-end→timerの順です。0msは「今のJavaScriptを中断してすぐ実行する」という意味ではありません。busyは指定時間まで計算を続ける関数で、その間は同じスレッドの次のタスクへ進めません。
8Promiseだけでは次のタスクへ進まない
分割の間にPromise.resolveだけを挟みます。
(order=[],setTimeout(()=>order.push("timer"),0),await(async()=>{for(let i=1;i<=3;i++){busy(10)一回10msの処理;order.push("chunk"+i);await Promise.resolve()microtaskとして継続;}})(),await pause(20)最後に別タスクへ譲る,order)三つのchunkが終わってからtimerが動きます。解決済みPromiseをawaitしても継続はmicrotaskで、次のタイマータスクより先に処理されます。awaitを置くだけで毎回描画や入力処理へ譲れるとは限りません。
9処理を分割して他のタスクに実行機会を渡す
今度は各chunkの間で0msタイマーを待ちます。
(order=[],setTimeout(()=>order.push("timer"),0),await(async()=>{for(let i=1;i<=3;i++){busy(10)同じ単位の処理;order.push("chunk"+i);await pause(0)別タスクへ処理を譲る;}})(),order)chunk1の後にtimerが入り、その後でchunk2・chunk3が続きます。処理の合計量を減らしたわけではありませんが、途中で他のタスクが動く機会を作れました。分割単位が大きすぎれば待ち時間も残ります。
10一括処理と分割処理で結果を合わせる
1から6までの和を、一括と分割の二方式で計算します。
(globalThis.values=[1,2,3,4,5,6],globalThis.total=0,await(async()=>{for(let i=0;i<values.length;i+=2二件ずつ処理){total+=途中結果を保持values.slice(i,i+2).reduce((a,b)=>a+b,0);await pause(0)合間に他の処理へ譲る;}})(),{whole:values.reduce((a,b)=>a+b,0),chunked:total最終結果を比較})両方とも21です。分割では途中の状態を保持して、同じ最終結果になるように組み立てます。合間に入力値が変わり得るなら、開始時の値を固定するか、取消しと再計算の扱いも決めます。
まとめ
画像の寸法を予約すると、読込み後の周囲の位置変化を抑えられます。
遅延読込みは取得時期の制御であり、領域の予約も合わせて行います。
microtaskの継続と別タスクへの譲渡は異なり、分割後も計算結果と途中状態を整合させます。
操作履歴と解説を振り返れます。
ブラウザ / 描画・実行記録
(globalThis.doc=await loadDocument("/performance"),globalThis.photo=doc.getElementById("photo"),globalThis.startY=rect("#caption").y,{image:rect("#photo"),captionY:startY}){
"image": {
"x": 16,
"y": 66.87,
"width": 607.93,
"height": 24,
"offsetLeft": 16,
"offsetTop": 67,
"offsetWidth": 608,
"offsetHeight": 24
},
"captionY": 106.86
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像"><p id="caption">画像の後の段落</p></body></html>(photo.src="/asset.svg?tag=unreserved",await photo.decode(),{image:rect("#photo"),captionY:rect("#caption").y,move:Math.round((rect("#caption").y-startY)*100)/100}){
"image": {
"x": 16,
"y": 66.87,
"width": 199.99,
"height": 100,
"offsetLeft": 16,
"offsetTop": 67,
"offsetWidth": 200,
"offsetHeight": 100
},
"captionY": 182.86,
"move": 76
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p></body></html>(globalThis.doc=await loadDocument("/performance"),globalThis.photo=doc.getElementById("photo"),photo.width=200,photo.height=100,globalThis.reservedY=rect("#caption").y,{image:rect("#photo"),captionY:reservedY}){
"image": {
"x": 16,
"y": 66.87,
"width": 200,
"height": 100,
"offsetLeft": 16,
"offsetTop": 67,
"offsetWidth": 200,
"offsetHeight": 100
},
"captionY": 182.86
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100"><p id="caption">画像の後の段落</p></body></html>(photo.src="/asset.svg?tag=reserved",await photo.decode(),{captionY:rect("#caption").y,move:Math.round((rect("#caption").y-reservedY)*100)/100}){
"captionY": 182.86,
"move": 0
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p></body></html>(globalThis.spacer=doc.createElement("div"),spacer.style.height="10000px",doc.body.append(spacer),globalThis.lazy=doc.createElement("img"),lazy.id="lazy",lazy.width=200,lazy.height=100,lazy.loading="lazy",lazy.alt="遅延画像",lazy.src="/asset.svg?tag=lazy",doc.body.append(lazy),await pause(50),{loading:lazy.loading,complete:lazy.complete,requested:(await(await fetch("/requests")).json()).tags.includes("lazy")}){
"loading": "lazy",
"complete": false,
"requested": false
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p><div style="height: 10000px;"></div><img id="lazy" width="200" height="100" loading="lazy" alt="遅延画像"></body></html>(lazy.scrollIntoView(),await lazy.decode(),{complete:lazy.complete,width:lazy.naturalWidth,requested:(await(await fetch("/requests")).json()).tags.includes("lazy")}){
"complete": true,
"width": 200,
"requested": true
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p><div style="height: 10000px;"></div><img id="lazy" width="200" height="100" loading="lazy" alt="遅延画像" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"></body></html>(globalThis.order=[],setTimeout(()=>order.push("timer"),0),order.push("start"),busy(80),order.push("work-end"),await pause(20),order)[ "start", "work-end", "timer" ]<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); } #box { width: 200px; height: 40px; background: rgb(34, 67, 75); } #following { margin-top: 8px; } img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p><div style="height: 10000px;"></div><img id="lazy" width="200" height="100" loading="lazy" alt="遅延画像" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"></body></html>
(order=[],setTimeout(()=>order.push("timer"),0),await(async()=>{for(let i=1;i<=3;i++){busy(10);order.push("chunk"+i);await Promise.resolve();}})(),await pause(20),order)[ "chunk1", "chunk2", "chunk3", "timer" ]<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); } #box { width: 200px; height: 40px; background: rgb(34, 67, 75); } #following { margin-top: 8px; } img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p><div style="height: 10000px;"></div><img id="lazy" width="200" height="100" loading="lazy" alt="遅延画像" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"></body></html>
(order=[],setTimeout(()=>order.push("timer"),0),await(async()=>{for(let i=1;i<=3;i++){busy(10);order.push("chunk"+i);await pause(0);}})(),order)[ "chunk1", "timer", "chunk2", "chunk3" ]<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); } #box { width: 200px; height: 40px; background: rgb(34, 67, 75); } #following { margin-top: 8px; } img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p><div style="height: 10000px;"></div><img id="lazy" width="200" height="100" loading="lazy" alt="遅延画像" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"></body></html>
(globalThis.values=[1,2,3,4,5,6],globalThis.total=0,await(async()=>{for(let i=0;i<values.length;i+=2){total+=values.slice(i,i+2).reduce((a,b)=>a+b,0);await pause(0);}})(),{whole:values.reduce((a,b)=>a+b,0),chunked:total}){
"whole": 21,
"chunked": 21
}<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'"><style>:root{color-scheme:dark}body{margin:16px;background:#111;color:#eee;font:14px/1.6 system-ui}body::after{content:"";display:block;height:calc(100vh - 32px)}a{color:#80c4d6}table{border-collapse:collapse}th,td{border:1px solid #555;padding:4px 8px}img{max-width:100%}body { font: 16px / 24px system-ui; margin: 16px; background: rgb(17, 17, 17); color: rgb(238, 238, 238); }
#box { width: 200px; height: 40px; background: rgb(34, 67, 75); }
#following { margin-top: 8px; }
img { display: block; }</style></head><body inert><h1>画像の領域</h1><img id="photo" alt="200×100の画像" width="200" height="100" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"><p id="caption">画像の後の段落</p><div style="height: 10000px;"></div><img id="lazy" width="200" height="100" loading="lazy" alt="遅延画像" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22100%22%20viewBox%3D%220%200%20200%20100%22%3E%3Crect%20width%3D%22200%22%20height%3D%22100%22%20fill%3D%22%2322434b%22%2F%3E%3Ctext%20x%3D%2212%22%20y%3D%2255%22%20fill%3D%22white%22%20font-size%3D%2220%22%3E200%20x%20100%3C%2Ftext%3E%3C%2Fsvg%3E"></body></html>演習完了