♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II
リンクやボタンの子孫object要素で読み込んだSVGをインタラクティブにする
a要素の子孫のobject要素やbutton要素の子孫のobject要素でSVGを読み込む場合には、そのままではリンクやクリックが効かず、ちょっと困ったことになる。
その対処法はすでにいろんな方が指摘していて、
a, button {
display: inline-block;
}
a object, button object {
pointer-events: none;
}
といった具合にCSSを指定すればよい。
SVGをobject要素で活用する | 水無月ばけらのえび日記
SVGをobject要素で表示してリンクにする - ういはるかぜの化学 - subtech
Retina対応にSVGを使う方法とリンクを張る時の注意点 - ROCHAS
ところが、当然のことながらSVGにマウスイベントなどが伝わらないので、:hover
で色が変わるようにSVG内にCSSで指定していても色は変わらない…orz
そこで、a要素やbutton要素が受け取ったイベントを利用して、子孫のSVGをインタラクティブにするJavaScriptを書いてみた。 object要素のSVGでインタラクティブにCSSを変更するテスト
スクリプトを利用するにあたり事前の準備としてHTMLとSVGに一手間が必要で、SVG内のCSSには、たとえば
/* SVGのCSS */
#target {
fill: green;
}
#target:hover, #target.hover {
fill: red;
}
といった具合に:hover、:focus、:activeのいわゆるダイナミック疑似クラスと同じ名前のclass名の指定を付記しておく。
一方、HTML側では、object要素にdata-svgtarget
属性を付与して、SVGで変化させたい要素のid値を指定しておく。
これは必須ではなく、省略されていた場合はルートのsvg要素にclassが付与される。
<a href="http://asamuzak.jp">
<object type="image/svg+xml" data="mysvg.svg" data-svgtarget="mytarget">
</object>
</a>
JavaScript (最終更新:2016/3/14)
/*
* Make SVG Interactive
* Toggle SVG class from an ancestor anchor element or button element's event type and make SVG interactive
* Copyright (c) 2016 Kazz
* http://asamuzak.jp
* Dual licensed under MIT or GPL
* http://asamuzak.jp/license
*/
(function(_win, _doc) {
"use strict";
function toggleClassByEventType(obj, type) {
if(obj) {
var x = "";
switch(type) {
case "pointerover":
case "pointerout":
case "touchstart":
case "mouseover":
case "mouseout":
x = "hover";
break;
case "focus":
x = "focus";
break;
case "pointerdown":
case "pointerup":
case "mousedown":
case "mouseup":
case "keydown":
case "keyup":
x = "active";
break;
default:
}
if(obj.classList) {
obj = obj.classList;
switch(type) {
case "pointerover":
case "touchstart":
case "mouseover":
case "focus":
case "pointerdown":
case "mousedown":
case "keydown":
obj.add(x);
break;
case "pointerout":
case "mouseout":
case "pointerup":
case "mouseup":
case "keyup":
obj.remove(x);
break;
case "blur":
obj.remove("hover", "focus", "active");
break;
default:
}
}
else {
if(obj.hasAttributeNS(null, "class")) {
var a = (obj.getAttributeNS(null, "class")).trim(), b, c, i, l;
switch(type) {
case "pointerover":
case "touchstart":
case "mouseover":
case "focus":
case "pointerdown":
case "mousedown":
case "keydown":
a.indexOf(x) === -1 &&
obj.setAttributeNS(null, "class", (a + " " + x).trim());
break;
case "pointerout":
case "mouseout":
case "pointerup":
case "mouseup":
case "keyup":
if(a.indexOf(x) !== -1) {
for(a = a.split(/\s+/), b = [], i = 0, l = a.length; i < l; i++) {
c = a[i];
c !== x && b.push(c);
}
obj.setAttributeNS(null, "class", b.join(" "));
}
break;
case "blur":
for(a = a.split(/\s+/), b = [], i = 0, l = a.length; i < l; i++) {
c = a[i];
c !== "hover" && c !== "focus" && c !== "active" && b.push(c);
}
obj.setAttributeNS(null, "class", b.join(" "));
break;
default:
}
}
else {
obj.setAttributeNS(null, "class", x);
}
}
}
}
function getSvgObject(evt) {
var x = evt.target.querySelectorAll("[data-svgtarget]"),
l = x.length;
if(l > 0) {
for(var i = 0; i < l; i++) {
var y = x[i];
if(y.contentDocument) {
var z = y.contentDocument,
a = y.getAttribute("data-svgtarget");
toggleClassByEventType(
a !== "_svg" && z.getElementById(a) ?
z.getElementById(a) : z.documentElement,
evt.type
);
}
}
}
}
_doc.addEventListener("DOMContentLoaded", function() {
var x = _doc.querySelectorAll("a object, button object"),
l = x.length;
if(l > 0) {
for(var i = 0; i < l; i++) {
var y = x[i];
if(y.hasAttribute("type") &&
y.getAttribute("type") === "image/svg+xml" ||
y.hasAttribute("data") &&
/^data:image\/svg\+xml|\.svgz?$/.test(y.getAttribute("data"))) {
var z = y.parentNode;
while(z.parentNode) {
if(/^a|button$/i.test(z.nodeName)) {
break;
}
z = z.parentNode;
}
!y.hasAttribute("data-svgtarget") &&
y.setAttribute("data-svgtarget", "_svg");
if(_win.PointerEvent) {
z.addEventListener("pointerover", getSvgObject, false);
z.addEventListener("pointerdown", getSvgObject, false);
z.addEventListener("pointerup", getSvgObject, false);
z.addEventListener("pointerout", getSvgObject, false);
}
else {
_win.TouchEvent &&
z.addEventListener("touchstart", getSvgObject, false);
z.addEventListener("mouseover", getSvgObject, false);
z.addEventListener("mousedown", getSvgObject, false);
z.addEventListener("mouseup", getSvgObject, false);
z.addEventListener("mouseout", getSvgObject, false);
}
z.addEventListener("focus", getSvgObject, false);
z.addEventListener("keydown", getSvgObject, false);
z.addEventListener("keyup", getSvgObject, false);
z.addEventListener("blur", getSvgObject, false);
}
}
}
}, false);
})(window, document);
注意点
-
iOSではa要素やbutton要素の子孫のobject要素に設定した
pointer-events:none;
が効かず、祖先がイベントのターゲットにならない。 WebKit Bugzillaには報告してあるが、このバグが修正されるまでは次のようなハックが必要。
なんとも前世紀の遺物的なハックだが…wa, button { display: inline-block; position: relative; } a::after, button::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; content: ""; }
-
EdgeやIEでは、ある状態(たとえば
:hover
)でtransform:translate()
で要素を移動しようとしても移動しない。 Chromeにも、transform:translate()
で要素を移動したら元の位置に戻らないというバグを見つけたがあっという間にFixされて51.0.2672.1で修正済み。 Issue 592206 - chromium - IE9では、buttonの子孫objectでSVGを読み込んだ場合に、SVG上ではbuttonの:hoverが外れてclickイベントも伝わらない。 IEは今後修正されることはないので、2017年春にVistaのサポートが切れるまでは、button要素でSVGを使いたい場合は、object要素ではなくimg要素で読み込む方が吉かと。 IE11ではOK。 IE9もa要素では問題ない。
"♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II"へのTwitter上でのコメントやRT
7件のツイートがあります。
ツイート 1
asamuzaK.jp : ♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II ~ リンクやボタンの子孫object要素で読み込んだSVGをインタラクティブにする https://t.co/QsewiESpWq
ツイート 2
RT @asamuzakjp: asamuzaK.jp : ♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II ~ リンクやボタンの子孫object要素で読み込んだSVGをインタラクティブにする https://t.co/QsewiESpWq
ツイート 3
個人的には来年春以降のSVGはobjectから読み込みの一択でいいような気がしている インラインSVGもいらない(うちでは元々使ってないけど) ♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II https://t.co/QsewiESpWq
ツイート 4
asamuzaK.jp : ♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II リンクやボタンの子孫object要素で読み込んだSVGをインタラクティブにする https://t.co/6t4SZknxbj
ツイート 5
うちの記事を紹介してくれてます https://t.co/QsewiESpWq 2016年03月19日のSVG:実装済みのSVG2の新機能など - 週刊SVG https://t.co/qMobvJ0Uin
ツイート 6
RT @asamuzakjp: うちの記事を紹介してくれてます https://t.co/QsewiESpWq 2016年03月19日のSVG:実装済みのSVG2の新機能など - 週刊SVG https://t.co/qMobvJ0Uin
ツイート 7
ただし、アンカーやボタンでobjectからSVGを使いたい場合にはちょっと工夫が必要だけど… こちらがその解決法 asamuzaK.jp : ♪1999 Scalable Object 輝ける時代の救世主 (c) 聖飢魔II https://t.co/QsewiESpWq