Font Awesomeはキットなんかを使うと好みのアイコンセットを利用できるようですが、コピーアイコンだけ使いたい、という用途にはいささか重すぎる気がするので、簡単に扱う方法を考えてみました。
なお、このページのSVGアイコンはすべてFont Awesomeからダウンロードしています(Free版)。 Font Awesome Free License
.svg-icon-bg-copy {
display: inline-block; width: 2em; height: 2em;
background: url("copy-solid.svg") center/contain no-repeat;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">...</svg>
これを
<svg xmlns="http://www.w3.org/2000/svg"><symbol id="name-of-icon" viewBox="0 0 448 512">...</symbol></svg>
こう書き換えれば、
<svg><use href="filename-of-icon.svg#name-of-icon"></use></svg>
で貼れるっぽい。 大きさはCSSで指定できる。
svg { width: 2em; height: 2em; }
ただし、ローカルファイルだとCORSの制限に引っかかって表示されないので、ローカルにWebサーバーを立ち上げて確認する必要がある。
PHPのビルトインサーバーがお手軽(php -S localhost:8000
)。
ちなみに、画像として表示している場合はCORSの制限を受けない。
fill
やstroke
で指定する。
svg.colored { fill: blue; stroke: blue; }
<svg class="colored"><use href="filename-of-icon.svg#name-of-icon"></use></svg>
<span class="svg-icon-copy"></span>
fetch
してtext()
を得てinnerHTML
を置き換えてしまえばよい。
addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".svg-icon-copy").forEach(e => {
fetch("copy-solid.svg")
.then(resp => resp.text())
.then(text => e.innerHTML = text);
});
});
でもやっぱりローカルファイルだとCORSに引っかかる。
data-icon
として指定しちゃう。
<span class="svg-icon-rev1" data-icon="scissors"></span>
<span class="svg-icon-rev1" data-icon="copy"></span>
<span class="svg-icon-rev1" data-icon="pen-to-square"></span>
dataset.icon
で取得できる。
addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".svg-icon-rev1").forEach(e => {
fetch(`${e.dataset.icon}-solid.svg`)
.then(resp => resp.text())
.then(text => e.innerHTML = text);
});
});
基本的にはこの7行のスクリプトがあればFont Awesomeは使える。
i[data-icon=scissors] { fill: red; stroke: red; }
i[data-icon=copy] { fill: yellow; stroke-width: 3%; stroke: orange; }
i[data-icon=pen-to-square] { fill: green; stroke: green; }
<i class="svg-icon-rev1" data-icon="scissors"></i>
<i class="svg-icon-rev1" data-icon="copy"></i>
<i class="svg-icon-rev1" data-icon="pen-to-square"></i>
span
からi
に変えている。
addEventListener("DOMContentLoaded", () => {
const altText = {
"scissor": "cut"
};
document.querySelectorAll(".svg-icon-rev2").forEach(e => {
let url = `${e.dataset.icon}-solid.svg`;
fetch(url)
.then(resp => {
if (!resp.ok) throw "icon fetch";
return resp.text()
})
.then(text => e.innerHTML = text)
.catch(() => new Promise((ok, ng) => {
let img = new Image();
img.src = url;
img.addEventListener("load", () => {
e.style.backgroundImage = `url(${url})`;
e.classList.add("svg-icon-as-background");
ok();
});
img.addEventListener("error", () => ng("load as image"));
}))
.catch(() => {
e.innerText = altText[e.dataset.icon] ?? e.dataset.icon;
e.classList.add("svg-icon-not-found");
});
});
});
url(...)
を設定してPromiseを解決する。
svg-icon-as-background
クラスを設定しておく。
svg-icon-not-found
というクラスを追加。
i.svg-icon-as-background {
display: inline-block;
background: center/contain no-repeat;
width: 2em; height: 2em;
}
i.svg-icon-not-found {
display: grid; align-content: center;
font-family: sans-serif; font-style: normal;
color: #444; background-color: #eee;
border: solid 1px silver;
padding: 1px; margin: 1px;
font-size: 10pt;
}
iを使っているとフォントスタイルを何か指定しないとイタリックになっちゃう。
<i class="svg-icon-rev2" data-icon="scissor"></i>
<i class="svg-icon-rev2" data-icon="copy"></i>
<i class="svg-icon-rev2" data-icon="paste"></i>
カットはHTMLとスクリプトで仲良くアイコン名を間違えている(最後の「s」がない)。 ペーストはHTML側でpasteというアイコンを指定しているが、正しくは「pen-to-square」である。
addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".svg-icon-rev3").forEach(e => {
fetch(`${e.innerText}-solid.svg`)
.then(resp => resp.ok && resp.text())
.then(text => text && (e.innerHTML = text));
});
});
i.svg-icon-rev3 {
display: grid; align-content: center;
font-family: sans-serif; font-style: normal;
padding: 0px 2px;
}
<i class="svg-icon-rev3">scissors</i>
<i class="svg-icon-rev3">copy</i>
<i class="svg-icon-rev3">pen-to-square</i>
<i class="svg-icon-rev3">paste</i>
addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".svg-icon-rev3a").forEach(e => {
e.dataset.icon ??= e.innerText;
fetch(`${e.dataset.icon}-solid.svg`)
.then(resp => resp.ok ? resp.text() : (x=>{throw x;})())
.then(text => e.innerHTML = `${text}<span>${e.innerText}</span>`)
.catch(() => e.classList.add("svg-icon-not-found"));
});
});
p:has(.svg-icon-rev3a) {
display: flex; align-items: center;
}
i.svg-icon-rev3a {
display: grid; align-content: center;
font-family: sans-serif; font-style: normal;
padding: 0px 2px;
}
i.svg-icon-rev3a span {
font-size: 12px;
}
<i class="svg-icon-rev3a">cut</i>
<i class="svg-icon-rev3a" data-icon="scissors">cut</i>
<i class="svg-icon-rev3a">copy</i>
<i class="svg-icon-rev3a">paste</i>
<i class="svg-icon-rev3a" data-icon="pen-to-square">paste</i>
data-icon
属性がなければ
data-icon
に
innerText
を設定してしまい、アイコンのファイル名は
data-icon
から取る。
元からあるテキストは
span
の中に入れ、その前に読み込んだSVGを追加している。
アイコンがあればアイコンの下に文字列、なければ文字列だけをボタンに表示するようなことが可能。
i.svg-icon-rev3a span { display: none; }
とすれば、スクリプトをいじらなくてもアイコン下の文字を消せる。
Copyright (C) 2019-2023 akamoz.jp
$Id: index.htm,v 1.4 2023/10/26 15:04:02 you Exp $