網頁中使用<object> tag 載入 svg 檔案的寫法長這樣:

1
2
<object type="image/svg+xml" data="myFile.svg" id="myObj">
</object>

如果我們想用 JS 取得 svg 裡的<path> 節點呢?

首先,<object></object>裡的 svg 檔案是在 html 被解析完才載入的, 所以必須寫在這個 object 的 load 事件:

1
2
3
4
var myObj = document.getElementById('myObj'); 
myObj.addEventListener('load', function(){
...
});

那再來就是用getElemetsByTagName()去抓 path 嘛? 如果這麼單純我就不會寫這篇筆記了…

1
2
3
4
5
var myObj = document.getElementById('myObj'); 
myObj.addEventListener('load', function(){
var paths = myObj.getElementsByTagName('path');
console.log(paths); // []
});

結果抓到的是空陣列,怎麼會這樣?

用 Chrome devTool 檢查網頁結構,發現在<object></object>tag 中,與 <svg></svg>tag還隔著一層#documentwrap像這樣:

1
2
3
4
5
6
<object type="image/svg+xml" data="myFile.svg" id="myObj">
#document
<svg>
...
</svg>
</object>

就是因為中間那層 document ,object 還需要透過 contentDocument 屬性才能訪問其中的節點:

1
2
3
4
5
var myObj = document.getElementById('myObj'); 
myObj.addEventListener('load', function(){
var paths = myObj.contentDocument.getElementsByTagName('path');
console.log(paths); // (N) [path#A, path#B , ...]
});

如此便能成功獲取我們要的 path ,可喜可賀。

靠北的有趣的地方

如果我們重新整理頁面,在 console 直接輸入 document.getElementsByTagName('path') 也是傳回空陣列;但如果滑鼠右鍵直接點在我們的 svg 上檢查 ,再重新輸入一次 document.getElementsByTagName('path') 卻能正確回傳所有的 path 節點陣列。

心得

就是一個 window 裡有多個 document 的概念,跟 iframe 有九成像, document 還會隨著 client scope 而變,依靠 document 並不是好的抓取 svg 方式。