在包含 SVG 标记元素的页面上使用基本标记无法呈现标记

Posted

技术标签:

【中文标题】在包含 SVG 标记元素的页面上使用基本标记无法呈现标记【英文标题】:Using base tag on a page that contains SVG marker elements fails to render marker 【发布时间】:2013-08-18 00:24:31 【问题描述】:

我在尝试在基于 SVG 的可视化中使用 SVG 标记元素时遇到了问题。我正在将我的更改添加到一个 Web 应用程序中,该应用程序恰好在每个页面上都包含一个基本标记,因此对 CSS 文件、javascript 文件等的任何引用都可以是相对的。

我在下面有一些示例代码可以重现该问题。定义了一个线元素和一个标记元素。标记元素通过标记的 uri 和 id 由其“标记结束”属性中的行引用。没有基本标签,箭头显示正常。对于基本标签,它不显示。原因是因为 base 标签改变了浏览器解析 url 的方式。即使是在行的 marker-end 属性中指定的简单的基于 id 的 url。

有什么方法可以解决这个问题而不必删除基本标记?

我无法真正删除它,因为它的使用在我正在开发的产品中已经根深蒂固。我的 webapp 需要支持 Firefox、Chrome 和 IE9+。 Firefox 和 chrome 都会产生这个问题。 IE 工作正常(即显示箭头标记)。

<html>
    <head>
    <base href=".">
    <style>
    .link  stroke: #999; stroke-opacity: .6; 
    marker#arrow  fill: black; 
    </style>
</head>
<body>
    <svg  >
        <defs>
            <marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="20" markerHeight="20" orient="auto">
                <path d="M0,-5L10,0L0,5"></path>
            </marker>
        </defs>
        <line x1="100" y1="100" x2="333" y2="333" marker-start="url(#arrow)" class="link"></line>
    </svg>
</body>
</html>

【问题讨论】:

对于它的价值,您可能需要重新考虑使用基本元素。几年前,我遇到了写得不好的爬虫通过我的网站生成数千个 404 的问题。 我会把这个留在这里,让任何 AngularJS 用户为他们节省一天的痛苦:github.com/angular/angular.js/issues/8934#issuecomment-56568466 【参考方案1】:

HTML &lt;base&gt; element 用于表示“将所有相对 URL 解析为不是与此页面相关的,而是与新位置相关的”。在您的情况下,您已经告诉它相对于带有 HTML 页面的目录进行解析。

SVG marker-mid="url(…)" 属性是FuncIRI Reference。当您使用像url(#foo) 这样的值时,通常相对于当前页面解析相对IRI,找到具有foo id 的元素。但是,当您使用 &lt;base&gt; 时,您会改变它的外观。

要解决此问题,请使用更好的值。由于您的基本引用是当前目录,因此您可以简单地使用当前文件的名称:

<line … marker-mid="url(this_page_name.html#arrow)" />

如果您的&lt;base&gt; href 与您显示的不同,例如:

<base href="http://other.site.com/whee/" />

那么您将需要使用绝对 href,例如

<line … marker-mid="url(http://my.site.com/this_page_name.html#arrow)" />

【讨论】:

非常感谢 Phrogz 的回复。实际上,我昨天在一天结束时就想到了这一点。由于我通过 javascript 添加标记,我只使用以下设置标记开始、标记结束等属性值:“url(”+window.location+“#arrow)”需要注意的一点是,通过设置带有绝对 url 的标记属性,它确实解决了 Firefox 和 Chrome 中的问题,但现在它在 IE 中不起作用。不过,这没什么大不了的。我可以根据使用的浏览器使用不同的行为。再次感谢。 -戴夫 在我们的案例中,回复给我的报告是“我们所有的渐变都停止工作了”!大声笑当然,“url(#gradientName)”不会与基本标签一起飞行。当我们迁移到 Cloudfront 分区时出现了这种情况,因此当通过基本标签将完美工作的代码迁移到 Cloudfront 中的生产环境时,它变得毫无用处。 @DaViS 我也遇到了这个问题。 FWIW,使用“外部”文件引用来指定标记 URL 缺乏浏览器支持,这可以解释 IE 中的问题(尽管我很惊讶它在 Chrome 中工作)。见:***.com/questions/27752494/… 很好的答案,帮助我找出了我自己的问题:url(#id)。如果您尝试使用基本 url 从 svg defs 应用样式,则需要在 css 中设置完整路径以进行填充,例如#my_svg fill:url(tld.com/subpage#id);【参考方案2】:

如果您不想修改/动画 svg,有一个比更改 url() 参数更简单的解决方案。

将 svg 包含为图像:

<img src="yourpath/image.svg">

【讨论】:

【参考方案3】:

用javascript试试:

<line id="something" />

原生:

document.getElementById('something').setAttribute('marker-mid', 'url(' + location.href + '#arrow)');

使用 jQuery:

$('#something').attr('marker-mid', 'url(' + location.href + '#arrow)');

它只是工作。

【讨论】:

好主意,甚至更好...$(..tags you wanna target...).each(function() var clip_path=$(this).attr('clip-path') var actual_id=clip_path.substring( (clip_path.indexOf('(')+1) , clip_path.indexOf(')') ); $(this).attr('clip-path', 'url('+location.href+actual_id+')' ); );【参考方案4】:

在诸如基于 Angular 构建的富 Web 应用程序的上下文中,您需要设置 &lt;base&gt; 标记以使 HTML5 样式的导航工作,尝试以永久方式修复它可能会变得混乱。

在我的例子中,我正在开发的应用程序显示一个 SVG-based interactive diagram builder,当我选择其中的元素时,它会更改应用程序的 URL。

我所做的是添加一个全局事件处理程序,它将修复页面中任何 &lt;path&gt; 元素中的所有 url(#...) 内联样式:

$rootScope.$on 'fixSVGReference', ->
    $('path').each ->
        $path = $ this
        if (style = $path.attr 'style')?
            $path.attr 'style', style.replace /url\([^)#]*#/g, "url(#location.href\#"

然后在关键的地方触发这个处理程序,比如当应用状态改变时(我用的是ui-router)

$rootScope.$on '$stateChangeSuccess', ->
    $timeout (-> $rootScope.$emit 'fixSVGReference'), 5

以及我知道会有类似这样的新/更新路径的任何地方。在这里,$timeout 是为了说明 DOM 节点确实在 $stateChangeSuccess 事件触发后的某个时间发生了异步更改。

【讨论】:

【参考方案5】:

Ember 2.7 将用 rootURL 替换 &lt;base&gt; 标记,这应该可以解决此问题。

与此同时,在我的 d3 中,我正在使用以下渐变:

.attr('fill', `url($Ember.$(location).attr('href')#my-gradient)`);

如果您不这样做,您所定位的项目将看起来是透明的。

【讨论】:

【参考方案6】:

在 Angular 2+ 中,您可以在应用模块中注入基本路径,而不是使用 标记。这为我解决了 Edge 和 Firefox 中的问题。

import  APP_BASE_HREF  from '@angular/common';

@NgModule(
   providers: [
     provide: APP_BASE_HREF,
     useValue: '/'
   ]
)
export class AppModule  

https://angular.io/docs/ts/latest/api/common/index/APP_BASE_HREF-let.html

【讨论】:

这适用于 Angular,但是我所有的 /assets/images... 图像链接都被破坏了。【参考方案7】:

在当前(04-2017)的 Windows 上,所有浏览器都按预期运行(mask=url("#svgmask"))。 Chrome、Firefox,甚至 IE 11! - 但 Edge 出现错误。

因此,对于 Microsoft Edge,您仍然需要为您的掩码 ID 提供绝对路径 ( ma​​sk="url(path/to/this-document.htm#svgmask)" )在您的文档中使用基本标签:

<svg viewBox="0 0 600 600" >
  <defs>
    <mask id="svgmask">
      <image   xlink:href="path/to/mask.svg" ></image> 
    </mask>
  </defs>
  <image mask="url(path/to/this-document.htm#svgmask)"   xlink:href="path/to/image.jpg"></image>
</svg>

【讨论】:

【参考方案8】:

您可以使用以下方式存档:

$("[marker-mid]").attr("marker-mid", function () 
    return $(this).attr("marker-mid").replace("url(", "url(" + location.href); 
);

【讨论】:

以上是关于在包含 SVG 标记元素的页面上使用基本标记无法呈现标记的主要内容,如果未能解决你的问题,请参考以下文章

将 SVG 标记从 HTML 页面保存到 SVG 图像

带有USE标记的SVG无法呈现

o'Reill的SVG精髓(第二版)学习笔记——第二章

使用嵌入或对象标签中的元素

无法在 svg 中获得鼠标指针的精确 xy 坐标(在路径标记上)

如何使用 JavaScript 在 <object> 标记内选择 SVG 元素?