SVG Sprite(這裡特指基於svg symbol)作為傳統css sprite和icon font的替代方案,在現代瀏覽器(ie9+)上能夠正常執行,關於它的技術背景、特點和降級方案等不再贅述,可以參考以下幾篇文章,寫的都非常不錯:
在具體實踐過程中,遇到以下兩個問題:
-
IE瀏覽器不支援外鏈svg sprite檔案,也就是說svg sprite必須嵌入到頁面裡面(最前面)
-
IE瀏覽器下,擁有use標籤到svg元素上到點選事件無法冒泡,現象是在使用代理的方式繫結事件時(繫結到svg元素的父元素上,繫結到svg元素本身就更沒辦法了),點選svg元素本身無法觸發點選事件。
下面就這兩個問題逐個看看解決方案。
問題1
我們一是要保證ie下svg sprite圖示能正常顯示,所以必須採用嵌入到頁面的方案,同時還要解決快取的問題,如果直接放在模版檔案裡,那每次都需要重新下載,就失去了原來css spirte和icon font可以快取的特點。
這裡我採用的方案是本地利用構建工具(我用的是基於grunt的grunt-svg-sprite,gulp也有對應的外掛)生成svg sprite檔案,然後單獨在頁面最前面放一個js檔案,js檔案包含svg sprite的內容,並在自身載入完成後把svg sprite插入到頁面最前面。
tpl:
<body>
<script type="text/javascript" src="/static/js/svgsprite.js" async></script>
...
</body>
svgsprite.js:
// 構建工具生成的svg sprite檔案
var svgsprite = require(`./svg/symbol/svg/sprite.symbol.svg`);
document.body.insertAdjacentHTML(
`afterBegin`, // 插入body第一個子節點前面
`<div class="hide">` + svgsprite + `</div>`
);
browserify配置:
// 是svg檔案可以作為string型別被require
brs.transform(stringify([`.svg`]));
因為js檔案是可以快取的,所以相當於變相把svg sprite快取起來了。
問題2
第二個問題比較棘手一點,關於這個問題,jquery開發人員也做了解答:
Delegated event not firing for click within SVG use element
前面說了是由於包含use標籤的svg元素無法冒泡,那麼直接在svg元素或者svg父元素上繫結事件就可以了,但是在涉及新增、刪除等操作時,可操作的按鈕都是動態新增的,如果每次新增元素都要重新繫結事件,是非常麻煩的。
幸好有pointer-events
這個神器,這是css3的新屬性,雖然對於普通html元素的支援性不是太好,但對於支援svg的瀏覽器,基本都支援這個屬性。
http://caniuse.com/#search=pointer-events
Already part of the SVG specification, and all SVG-supporting browsers appear to support the property on SVG elements.
通過對svg元素應用pointer-events:none
,可以讓svg相對點選事件“透明”,只要事件繫結到svg的父元素上,且父元素具備寬高(需要設定為inline-block,或者應用float、position:absolute等,否則ie下點不中)。
針對以上需求,我寫了個簡單的sass mixin:
@mixin svg-sprite(
$width,
$heigth,
$fill: default,
$hover-fill: default
) {
display: inline-block;
width: $width;
height: $heigth;
svg.g-ico {
width: $width;
height: $heigth;
pointer-events:none;
}
@if $fill != default {
svg {
fill: $fill;
}
}
@if $hover-fill != default {
&:hover { // tips: hover是在父元素上
svg {
fill: $hover-fill;
}
}
}
}
給svg元素的父元素應用這個mixin,這樣就算是圓滿解決了這個問題。
Tips: 因為我們的網站相容ie9+,所以ie9以下的沒有做降級處理,如果需要,可以參考前面列出的幾篇文章。