WebKit策略:<foreignObject>可用于绘制svg中的html标签,但与<use>搭配不生效
在<svg>里面可以利用<foreignObject>绘制html标签,原本是我在iconfont采用Font class方式引入svg的无奈之举。
起初的设计是所有icon先在<defs>中先渲染,以达到icon复用的效果,icon采用Symbol方式引入svg感觉也是比较合适的,比较规范的。
<template>
<defs>
<g v-for="item in list" :key="item._id" :id="'icon-' + item._id">
<svg aria-hidden="true" width="16" height="16" x="0" y="0">
<use :xlink:href="'#' + item.icon"></use>
</svg>
</g>
</defs>
</template> <script>
export default {
data() {
return {
list: [],
};
},
};
</script>
然后再需要用到的地方用<use :xlink:href="'#icon-' + id" />克隆下来,感觉很完美。
但是理想很丰满,现实很骨感。由于某些功能会被影响到,不能使用Symbol方式引入,最后只能选择Font class引入svg。于是代码变为了下列
<template>
<defs>
<g v-for="item in list" :key="item._id" :id="'icon-' + item._id">
<foreignObject width="16" height="16" x="16" y="16">
<div xmlns="http://www.w3.org/1999/xhtml">
<span class="iconfont" :class="item.icon"></span>
</div>
</foreignObject>
</g>
</defs>
</template> <script>
export default {
data() {
return {
list: [],
};
},
};
</script>
但是在需要的地方使用<use :xlink:href="'#icon-' + id" />克隆下来,会发现在谷歌浏览器上却完全显示不出<span>标签的内容,即不显示iconfont图标。
刚开始,我以为是不能在<defs>标签中使用<foreignObject>标签,于是我就去查看了SVG规范,传送门:https://www.w3.org/TR/SVG/struct.html#DefsElement,SVG规范是支持这种写法的。打开F12,查看<defs>标签下的dom结构,也可以看到<foreignObject>标签其实是有生成的,也是佐证了这一点。

但是查看引用<use>标签的地方,就没有生成对应的<foreignObject>标签,我查看SVG规范文档并没有提到<use>标签不能与<foreignObject>标签共同使用的限制。最后我打开了github,在w3c的【SVG工作组规范】项目下寻找答案,传送门:https://github.com/w3c/svgwg,最后找到了一个讨论:https://github.com/w3c/svgwg/issues/511。这位程序员在讨论中说除了 Gecko 之外的所有浏览器都限制<svg:use>元素中的<foreignObject>,他在思考为什么Gecko之类的浏览器允许这么做。

这下就有点头绪了,原来是浏览器内核原因。那简单,我们找个Gecko内核的浏览器验证下就知道了,Gecko内核最出名的就是FireFox浏览器(火狐浏览器)了。其实我的电脑也装了火狐浏览器,但是由于我开发一直用的是谷歌浏览器,确实也是好久好久没打开火狐了,放着吃灰,这次也确实没想到可能是浏览器本身的问题。打开火狐浏览器,果然能显示<span>标签的内容,即显示了iconfont图标。

不过为什么会出现这样的情况呢,另一个叫Dirk Schulze的程序员表示:出于复杂性的原因,WebKit不允许引用foreignObject。 我们没有时间查看所有影响(包括安全影响),如果内容是基于HTML的,那么对foreignObject的支持永远不会很好。(Blink修复了后半部分)
也就是说Blink内核修复了后半段,使浏览器更好的支持了<foreignObject>标签,但是对于引用<foreignObject>标签的情况,还是没有任何进展。那也就是说谷歌浏览器现在是支持的<foreignObject>标签的,只是不支持被<use>标签引用。
最后直接弃用<defs>和<use>,在需要的地方直接渲染。简单粗暴,最有效。
<foreignObject width="16" height="16" x="16" y="16">
<div class="icon-div" xmlns="http://www.w3.org/1999/xhtml">
<span class="iconfont" :class="classRef.ModuleClassType.Icon"></span>
</div>
</foreignObject>
虽然不够优雅,但是真香。
事情原本到这就应该结束了,但是我还是不死心,不知道为什么WebKit要做这样的一个策略。最后,功夫不负有心人,我在Bugzilla又找到了一个提交给WebKit的bug:https://bugs.webkit.org/show_bug.cgi?id=91515。底下有一名名为Nikolas Zimmermann的程序员对此进行了回应:

原文大意是:
是的。由于与foreignObject相关的潜在问题,我们故意禁止它。它需要经过充分测试,仅此而已。
当启用它时,我们需要注意新类型的循环引用,这就是它变得棘手的地方。
foo.svg,包含 <symbol id="symbol"><foreignObject> <iframe src="other.html"/></foreignObject></symbol>
blub.svg 引用"symbol"。other.html包含foo.svg作为html:img。... -> 循环
或者考虑<foreignObject>包含<div style="background-image: blub.svg" 的情况...
我们基本上需要将循环检测扩展到所有可以引用其他文件的 HTML 元素/属性。
如果您感到有挑战,请随时开始,否则我将不实施解决这个问题。
不过这个bug之后在2020年被其他人重新提起,于是,应该是Nikolas Zimmermann的同事Said Abou-Hallawa在底下也对这个bug进行了补充评论。
原文大意是:
上述测试用例在FireFox中有效,但在WebKit或Chrome中无效。
由于foreignObjectTag不是createAllowedElementSet允许的标记之一,因此foreignObject 及其后代被removeDisallowedElementsFromSubtree() 删除。但是即使添加它也不能解决问题,因为 HTML<p>元素将被删除(此处应该是指bug提交人的示例中的p标签),因为它的标签是不允许的。
为了解决这个问题,我们需要重新实现removeDisallowedElementsFromSubtree(),并且正如 Nikolas 上面提到的,我们需要将循环检测扩展到所有 HTML 元素,以防它们中的任何一个引用其他文件。
所以,很明显,到目前为止,他们也没解决这个问题,导致他们做出这个策略的一个原因是因为removeDisallowedElementsFromSubtree()这个方法写的不够完善,在某些场景下会出现循环引用的bug,最简单粗暴的办法就是直接不让你在<use>标签中引用<foreignObject>标签,于是他们直接就从源头解决了这个问题。妙,妙,妙啊,真是妙蛙种子吃着妙脆角进了米奇妙妙屋,妙到家了。为了确认这两人的权威性,我特地去查看了WebKit团队的名单,传送门https://webkit.org/team/,确实找到了这两个大佬的名字,上文提到的Dirk Schulze也是这个团队中的一员。

这下事情是真的结束了,最后大致扫一眼名单,这个团队的很多人最后都去了苹果,不得不说苹果真的挖人有一套,满屏的apple。
WebKit策略:<foreignObject>可用于绘制svg中的html标签,但与<use>搭配不生效的更多相关文章
- 绘制SVG内容到Canvas的HTML5应用
SVG与Canvas是HTML5上绘制图形应用的两种完全不同模式的技术,两种绘制图形方式各有优缺点,但两者并非水火不容,尤其是SVG内容可直接绘制在Canvas上的功能,使得两者可以完美的融合在一起, ...
- FireFox下Canvas使用图像合成绘制SVG的Bug
本文适合适合对canvas绘制.图形学.前端可视化感兴趣的读者阅读. 楔子 所有的事情都会有一个起因.最近产品上需要做一个这样的功能:给一些图形进行染色处理.想想这还不是顺手拈来的事情,早就研究过图形 ...
- d3.js svg中 g 标签问题一览
svg 中的g标签, 算是比较特殊 1 没有x y属性 2 没有width height 属性 3 不能fill 4 .... g标签基本只管分组问题, 其他功能一概不提供 要解决这些问题, 直接在g ...
- CSS和SVG中的剪切——clip-path属性和<clipPath>元素
剪切是什么 剪切是一个图形化操作,你可以部分或者完全隐藏一个元素.被剪切的元素可以是一个容器也可以是一个图像元素.元素的哪些部分显示或隐藏是由剪切的路径来决定的. 剪切路径定义了一个区域,在这个区域内 ...
- 【转】CSS和SVG中的剪切——clip-path属性和<clipPath>元素
本文由大漠根据SaraSoueidan的<Clipping in CSS and SVG – The clip-path Property and <clipPath> Elemen ...
- [翻译svg教程]svg中的circle元素
svg中的<circle> 元素,是用来绘制圆形的,例如 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink= ...
- SVG中的坐标系统和坐标变换
视野和世界 2D绘图中很多人会有一个误区,就是我绘图的区域是一个矩形区域.无论新建一个画布还是创建了一个容器,心里都想象里面有一个矩形区域.其实,在SVG当中,矩形区域只是视野,是我们看到的部分.实际 ...
- UE4 Tutorial - Custom Mesh Component 用于绘制自定义网格的插件CustomMeshComponent
UE4 中用于绘制自定义网格的插件CustomMeshComponent. 转载: UE4 Tutorial - Custom Mesh Component Over the last few w ...
- 在 SVG 中添加交互性
原文地址:http://www.ibm.com/developerworks/cn/xml/x-svgint/ SVG 中的交互性可以分为三个领域 -- 链接.事件和脚本.本文将依次讨论这三个领域. ...
随机推荐
- qt调用quit()后未结束线程解决方案
正常操作 正常写Qt的多线程,需要继承QThread,然后重写run函数,调用start后,在run函数中实现子线程的运行,这个时候会开启事件循环,可以调用quit()函数来结束当前的线程. 其他操作 ...
- 第四十九篇:webpack的基本使用(三) --安装和配置html-webpack-plugin插件
好家伙, 1.html-webpack-plugin的作用 讲一下为什么需要这个插件 存在问题:在点开locahost:8080之后出现的是项目的根目录,而不是网页 这时候需要再点开scr文件夹才能看 ...
- day31-线程基础01
线程基础01 1.程序 进程 线程 程序(program):是为完成的特定任务,用某种语言编写的一组指令的集合.简单来说,就是我们写的代码. 进程: 进程是指运行中的程序,比如我们使用QQ,就启动了一 ...
- ifort + mkl + impi (全套intel)编译安装量子化学软件GAMESS 2022 R1版本
说明:linux下编译软件都需要先配置好该软件依赖的系统环境.系统环境可以通过软件的安装说明了解,例如:readme.md等文件或网页.这个前提条件很重要!后面正式编译出错基本都可以归结到系统环境配置 ...
- 【读书笔记】C#高级编程 第十五章 反射
(一)在运行期间处理和检查代码 自定义特性允许把自定义元数据与程序元素关联起来.反射是一个普通术语,它描述了在运行过程中检查和处理程序元素的功能.例如,反射允许完成的任务: 枚举类型的成员 实例化新对 ...
- 使用 Loki 微服务模式部署生产集群
转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247500523&idx=1&sn=0994af2b50 ...
- PAT (Basic Level) Practice (中文)1015 德才论 分数 25
宋代史学家司马光在<资治通鉴>中有一段著名的"德才论":"是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人.凡取人之术,苟不得圣人,君子 ...
- SpringBoot 项目部署(初级)
之前的项目一直在本地电脑上写,最近需要将项目部署到服务器上进行联调测速度.于是,在网上搜集资料后简单的进行一下总结. 由于本次打包部署是为了测试,于是很多内容做的还不算详尽,只是将项目简单的打包为ja ...
- 实验02_Proteus仿真数码管显示报告
一.原理总结 利用两个寄存器 R4 和 R5 来存储两个数码管的显示数字,R4 用来存储前一个数码管显示数字,而 R5 用来存储后一个数码管显示数字,利用左移操作 RLC 取 A 中首位放入 C ...
- P3834 【模板】可持久化线段树 2
P3834 主席树模板,求区间第k小. 1 #include <bits/stdc++.h> 2 using namespace std; 3 #define lc tr[i].ch[0] ...