A way to turn specific regions of an image into links, letting one picture lead to multiple destinations.
<map>
は、画像内の特定の領域だけをリンクにできます。たとえば「日本地図の北海道だけ詳細へ」「製品写真の各パーツを個別ページへ」など、1枚の画像を複数のリンク先に分割できます。
構成は「<img usemap>
で画像にイメージマップを関連付け、<map>
内の複数の <area>
でクリック領域を定義する」という形です。
現在の仕様では、<map>
は name
でも id
でも識別できます。
互換性を最大化したい場合は、両方に同じ値を指定しておくと安全です(既存コードや古い実装との整合を取りやすい)。
<img>
の usemap
には、先頭に #
を付けて、対応する name/id
の値をそのまま書きます(例:usemap="#shapes"
)。
形状を選択してください:
HTML
<p>形状を選択してください:</p>
<!-- 実画像。パスは環境に合わせて変更してください -->
<img src="imgs/map01.jpg"
width="595" height="156"
alt="4つの形状をクリックすると各説明ページへ移動します(赤いボックス、緑の円、青の三角形、黄色の星)"
usemap="#shapes">
<!-- map は name と id を両方付ける -->
<map name="shapes" id="shapes">
<!-- 1) 穴領域:非リンク。重なり時に「穴」が優先されてクリック不可にできるよう、最初に定義 -->
<!-- href を付けない(obsolete な nohref は使わない)/ alt も不要 -->
<area shape="rect" coords="47,48,101,96">
<!-- 2) 各リンク領域:href を付ける要素には alt を必ず付与 -->
<area shape="rect" coords="11,12,139,133" href="map/red.html" alt="赤色のボックス">
<area shape="circle" coords="221,76,65" href="map/green.html" alt="緑色の円">
<area shape="poly" coords="365,19,294,134,435,135" href="map/blue.html" alt="青色の三角形">
<area shape="poly" coords="505,13,486,61,434,78,486,96,506,141,526,97,577,78,526,61"
href="map/yellow.html" alt="黄色の星">
</map>
alt
の付与<area>
には必ず alt
を付け、スクリーンリーダー利用者にリンク先の意味を伝えます。href
が無い <area>
はリンクではないため alt
は不要です。alt
alt
は「何ができるのか(リンク目的)」も伝える文にします。shape="rect"
(長方形)coords="left,top,right,bottom"
shape="circle"
(円)coords="centerX,centerY,radius"
shape="poly"
(多角形)coords="x1,y1,x2,y2,..."
shape
を省略すると rect
と同義。
shape="default"
は「他のどのエリアにも当たらない残り全部」。最後に置くのがセオリー。
座標系は CSS ピクセル(画像の表示サイズ基準)。小数は避け、整数で記述するとトラブルが少ないです。
<img usemap="#foo">
は <map name="foo">
を参照します。name
と id
を同じ値で両方付ける のが安全(将来の参照やスクリプトから扱いやすい)。<map>
は文書内のどこにあってもOK(画像の前後どちらでも可)。usemap
を付ければよい)。<area>
には必ず alt
を。リンクテキストの代替になります。aria-label
や title
を追加してもOKですが、最優先は alt
。alt
も意味のある説明に(例:「フロア図: A〜Cエリア」)。<figcaption>
などで補助説明を入れるとベスト。href
のある <area>
はリンクとしてフォーカス可能。キーボード操作で順番にたどれるよう、小さい領域→大きい領域→default の順で並べると誤フォーカスを減らせます。テキストの冗長だが確実な代替例:
HTML
<figure>
<img src="floor.png" alt="フロア図: A〜Cエリア" usemap="#floorMap">
<figcaption>
<p>各エリアの詳細:</p>
<ul>
<li><a href="/area-a">Aエリア</a></li>
<li><a href="/area-b">Bエリア</a></li>
<li><a href="/area-c">Cエリア</a></li>
</ul>
</figcaption>
</figure>
座標は表示サイズ基準なので、画像が縮尺すると座標も比例させる必要があります。主な対応は4通り。
width
/height
属性やCSSで画像サイズを固定。画像の実表示幅 / 元画像の実幅 の比率で coords
を計算し直します。
形状を選択してください:
HTML
<!-- 実装例(レスポンシブ対応:CSSで伸縮 + JSで座標スケール) -->
<div class="responsive-image-map">
<p>形状を選択してください:</p>
<figure class="mapWrap">
<!-- 画像はCSSで可変。width/height属性は“元画像の実寸”を明示(比率保持のヒント) -->
<img
id="shapesImg"
src="imgs/map01.jpg"
width="595" height="156"
alt="4つの形状をクリックすると各説明ページへ移動します(赤いボックス、緑の円、青の三角形、黄色の星)"
usemap="#shapesMapB"
decoding="async"
loading="lazy">
</figure>
<!-- map は name と id を両方付ける(互換性のため) -->
<map name="shapesMapB" id="shapesMapB">
<!-- 1) 穴領域(非リンク)。クリック不可の穴を先に定義 -->
<!-- 元座標は data-raw-coords に保存しておき、JSで coords にスケールを書き戻す -->
<area shape="rect" data-raw-coords="47,48,101,96">
<!-- 2) 各リンク領域(href のある area には alt を付ける) -->
<area shape="rect" data-raw-coords="11,12,139,133" href="map/red.html" alt="赤色のボックス">
<area shape="circle" data-raw-coords="221,76,65" href="map/green.html" alt="緑色の円">
<area shape="poly" data-raw-coords="365,19,294,134,435,135" href="map/blue.html" alt="青色の三角形">
<area shape="poly" data-raw-coords="505,13,486,61,434,78,486,96,506,141,526,97,577,78,526,61"
href="map/yellow.html" alt="黄色の星">
</map>
</div>
<script>
<!-- JavaScript code here -->
</script>
CSS
/* 画像をコンテナ幅で伸縮(縦横比保持) */
.mapWrap { max-width: 100%; }
.mapWrap > img {
display: block;
max-width: 100%;
height: auto;
}
/* タッチでも狙いやすいようにフォーカス可視化(お好みで) */
area[href]:focus {
outline: 2px solid currentColor; /* UA依存だが最低限 */
}
JavaScript
(function () {
const img = document.getElementById('shapesImg');
const map = document.getElementById('shapesMapB');
// 元座標(raw)を coords に反映させるユーティリティ
function applyScaledCoords() {
// naturalWidth/Height が読めることを前提(画像読み込み後)
const baseW = img.naturalWidth || parseInt(img.getAttribute('width'), 10);
const baseH = img.naturalHeight || parseInt(img.getAttribute('height'), 10);
// 表示サイズ(CSSピクセル)。通常は等倍比率だが念のためX/Yを別々に
const dispW = img.clientWidth;
const dispH = img.clientHeight;
const scaleX = dispW / baseW;
const scaleY = dispH / baseH;
map.querySelectorAll('area').forEach(area => {
const raw = area.getAttribute('data-raw-coords');
if (!raw) return;
const nums = raw.split(',').map(n => parseFloat(n.trim()));
const shape = (area.getAttribute('shape') || 'rect').toLowerCase();
let scaled = [];
if (shape === 'circle') {
// [cx, cy, r]
const cx = Math.round(nums[0] * scaleX);
const cy = Math.round(nums[1] * scaleY);
// 半径は等方スケール。歪み回避のため min を採用
const r = Math.round(nums[2] * Math.min(scaleX, scaleY));
scaled = [cx, cy, r];
} else {
// rect/poly/default: 各点(x,y)を順にスケール
for (let i = 0; i < nums.length; i += 2) {
const x = Math.round(nums[i] * scaleX);
const y = Math.round(nums[i + 1] * scaleY);
scaled.push(x, y);
}
}
area.coords = scaled.join(',');
});
}
// 画像の読み込み完了後に初回適用
function ready(fn) {
if (img.complete && img.naturalWidth) { fn(); }
else { img.addEventListener('load', fn, { once: true }); }
}
// リサイズに追随(高頻度イベントは rAF で間引き)
let ticking = false;
function onResize() {
if (!ticking) {
window.requestAnimationFrame(() => {
applyScaledCoords();
ticking = false;
});
ticking = true;
}
}
// CSSによる画像サイズ変化を確実に捕捉(コンテナの幅変化含む)
const ro = new ResizeObserver(onResize);
ready(() => {
applyScaledCoords();
ro.observe(img);
window.addEventListener('orientationchange', onResize);
});
// ページ非表示→再表示やDPR変化(ズーム)にも念のため対応
document.addEventListener('visibilitychange', () => {
if (!document.hidden) onResize();
});
})();
実装メモ(要点)
max-width:100%
にし、縦横比を保って縮小/拡大させます。naturalWidth/Height
(=元画像の実寸)と、clientWidth/Height
(=現在の表示サイズ)からスケールを求め、data-raw-coords
に保存した元座標を等倍変換して coords
に上書きします。min(scaleX, scaleY)
でスケールしています(通常は等比なので同値)。ResizeObserver
と orientationchange
を使い、表示幅が変わるたびに再計算されます。srcset
/sizes
+ スクリプトnaturalWidth
を使うと楽。svg
内に <image>
を置き、<a>
+ <rect>/<circle>/<polygon>
でクリック領域を定義。