在包含SVG标记元素的页面上使用base标签会导致标记无法渲染。

55

在创建一个基于SVG的可视化时,我遇到了使用SVG标记元素的问题。我的更改是添加到一个包含每个页面的基本标签的Web应用程序中的,以便任何对CSS文件、JavaScript文件等的引用都可以是相对路径。

下面有一些示例代码来重现该问题。有一个线条元素和一个定义好的标记元素。通过uri和标记的id,在线条的“marker-end”属性中引用标记元素。没有基本标签,箭头正常显示。但有了基本标签,则不会显示箭头。原因是基本标签改变了浏览器解析URL的方式,即使是在线条的“marker-end”属性中使用简单的基于ID的URL。

是否有任何方法可以解决这个问题而不必删除基本标签?

因为我正在处理的产品中使用基本标签的频率相当高,所以我不能真正地将其删除。我需要支持Firefox、Chrome和IE9+的Web应用程序,Firefox和Chrome都出现了这个问题,而IE则没有问题(即箭头标记显示正常)。

<html>
    <head>
    <base href=".">
    <style>
    .link { stroke: #999; stroke-opacity: .6; }
    marker#arrow { fill: black; }
    </style>
</head>
<body>
    <svg width="100%" height="100%">
        <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>

1
说句实话,您可能需要重新考虑使用基本元素。几年前,我曾遇到过一些问题,那时候一些编写不良的网络爬虫在我的网站上生成了成千上万个404错误页面。 - cimmanon
7
为了帮助任何使用AngularJS的用户避免一天的痛苦,我在这里留下一个链接:https://github.com/angular/angular.js/issues/8934#issuecomment-56568466 - Bungle
8个回答

56

HTML <base> 元素 用于告诉浏览器“将所有相对URL解析为与此页面不同的新位置”。在您的情况下,已经指定了相对于HTML页面所在目录解析。

SVG marker-mid="url(…)" 属性是一个 FuncIRI 引用。当使用像 url(#foo) 这样的值时,相对IRI通常相对于当前页面进行解析,找到具有 foo ID的元素。但是,当使用 <base> 时,它会改变查找位置。

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

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

如果您的 <base> href 不同于所展示的,例如:

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

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

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

9
非常感谢你的回复,Phrogz。昨天我终于弄明白了这个问题。由于我是通过JavaScript添加标记的,所以我只需使用以下代码来设置marker-start、marker-end等属性值:"url("+ window.location + "#arrow)"。需要注意的一点是,通过设置带有绝对URL的标记属性,它确实解决了Firefox和Chrome中的问题,但现在在IE中却不起作用。不过这并不是什么大问题,我可以根据使用的浏览器来采用不同的行为方式。再次感谢。-Dave - DaViS
在我们的情况下,回到我的报告是“我们所有的渐变都停止工作了”!哈哈 当然,“url(#gradientName)”不能与基础标签一起使用。当我们转移到Cloudfront分发时,这个问题出现了,因此,当通过基础标签将完全正常的代码移动到生产环境中的Cloudfront时,它变得无用。 - Geek Stocks
@DaViS 我也遇到了这个问题。值得一提的是,使用“外部”文件引用来指定标记URL缺乏浏览器支持,这就解释了在IE中出现问题的原因(尽管我很惊讶它在Chrome中能够工作)。请参见:https://dev59.com/JYbca4cB1Zd3GeqPXZCE#27752594 - Bungle
很棒的答案,帮助我解决了使用fill:url(#id)时遇到的问题。如果您正在尝试使用svg defs中的基本url应用样式,则需要在css中设置完整路径以填充,例如#my_svg {fill:url(https://tld.com/subpage#id);}。 - Gevorg Hakobyan

5

尝试使用JavaScript:

<line id="something" />

使用本地方式:

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

使用jQuery:

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

它只是有效的。


1
好主意,甚至更好... $(..你想要定位的标签...).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+')' ); }); - user151496

5
在像 Angular 这样的丰富 Web 应用程序中,需要设置 <base> 标签才能使 HTML5 式导航工作,如果要永久性修复它,可能会变得混乱。
在我的情况下,我正在开发一个 基于 SVG 的交互式图表生成器,当我选择其中的元素时,它会更改应用程序的 URL。
我所做的是添加一个全局事件处理程序,可以修复页面中任何 <path> 元素中的所有 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
在Angular 2+中,您可以在应用程序模块中注入基本路径,而不是使用<base>标签。这样可以解决我在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...都无法使用了。 - Steve

1
Ember 2.7将用rootURL替换<base>标签,这应该可以解决此问题。同时,在我的d3中,我使用以下内容来进行渐变:.attr('fill', `url(${Ember.$(location).attr('href')}#my-gradient)`);如果不这样做,你所定位的项目看起来会是透明的。

1

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

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

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

0

您可以使用以下方式进行归档:

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

0

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

将SVG作为图像包含:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接