HTML
SVG
outerHTML

SVGをネストした場合のouterHTMLでxmlnsが補完された話

TL;DR

事象
svgタグをネストした場合に、ネストしているsvgタグに勝手にxmlns=""が付加されていた。(ChromeとFirefoxで再現。Safariは再現しない)

何が起こる?
表示して欲しいsvg(ネストしているsvg)が表示されない。

解決策
1. ネストするsvgを作るときにちゃんとxmlnsを指定しておく。
2. 勝手に付加されたxmlns=""を削る。
3. 事前にsetAttributeでxmlnsを指定してあげる。

はじめに

そもそも何をしたかったのかと言うと、svgでテンプレートを作っておいて、後から作成したsvgをそのテンプレートに埋め込んでダウンロードさせたかった。
svgはネストできるので、問題なくできるだろうと思っていたら意外とハマった、という話の備忘録。

事象

まず、以下のコードをjsfiddleとかで実行すると再現できると思います。(2017/12/28現在のChromeとFirefoxでは再現確認)
本来のコードではちゃんとwidthとかviewboxとか様々指定していますが、今回は事象再現のためだけに簡略化しています。

sample.js
const parser = new DOMParser();
const doc = parser.parseFromString( `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>`, "image/svg+xml" );

const templateSVG = doc.childNodes[ 0 ];
const savingSVG = parser.parseFromString( `<svg></svg>`, "image/svg+xml" ).firstElementChild;

templateSVG.appendChild( savingSVG );

console.log(doc.documentElement.outerHTML);

ChromeかFirefoxで実行した結果、コンソールに出力されるのは以下のような文字列です。

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><svg xmlns=""/></svg>

Safariだと以下になります。

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><svg/></svg>

差としては、ネストされているsvgの属性に、勝手にxmlns=""が付加されているかどうか。

この状態のsvgをBlobにしてダウンロードして、それを開くとあら不思議。
ネストしたsvgの方だけ表示されない。。。

ちなみに、ネストしている方のsvgを単体で保存した場合にはちゃんと表示されます。

原因と解決策

調べてみると、どうやらタイミング的にはouterHTMLで書き出した時に<svg xmlns=""></svg>といった具合にxmlns=""が補完されていました。

xmlns=""を削れば動きます。
ただ、outerHTMLをすると勝手についてしまうので、ちゃんとsetAttributexmlnsの値を指定してあげました。

これで解決!

const parser = new DOMParser();
const doc = parser.parseFromString( templateString, "image/svg+xml" );
const templateSVG = doc.childNodes[ 0 ];

const partSVG = parser.parseFromString( svgString, "image/svg+xml" ).firstElementChild;

// xmlnsは必須。ない場合はouterHTMLで勝手にxmlns=""が追加され、結果的に表示されない。
partSVG.setAttribute( "xmlns", "http://www.w3.org/2000/svg" );
templateSVG.appendChild( partSVG );

console.log(doc.documentElement.outerHTML);

ただ、そもそも上記のコードでいうところのsvgStringを作る際にxmlnsを指定しておけば良かったという。。。