Использование базового тега на странице, содержащей элементы маркера SVG, не позволяет отобразить маркер
Я столкнулся с проблемой при попытке использовать элементы маркера SVG в визуализации на основе SVG. Я добавляю свои изменения в веб-приложение, которое включает в себя базовый тег на каждой странице, так что любые ссылки на файлы CSS, файлы JavaScript и т. Д. Могут быть относительными.
У меня есть пример кода ниже, который воспроизводит проблему. Существует линейный элемент и определенный элемент маркера. На элемент маркера ссылается строка в его атрибуте marker-end, через uri и id маркера. Без базового тега стрелка отображается нормально. С базовым тегом он не отображается. Причина в том, что базовый тег изменяет способ разрешения URL браузером... даже для простого URL-адреса на основе идентификатора, указанного в атрибуте marker-end строки.
Есть ли способ обойти эту проблему, не удаляя базовый тег?
Я не могу действительно удалить это, потому что использование этого довольно укоренилось в продукте, над которым я работаю. Мне нужно поддерживать 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 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>
8 ответов
HTML <base>
Элемент используется, чтобы сказать "разрешить все относительные URL, относящиеся не к этой странице, а к новому местоположению". В вашем случае вы сказали, что он должен разрешаться относительно каталога со страницей HTML.
SVG marker-mid="url(…)"
Атрибут является ссылкой FuncIRI. Когда вы используете значение как url(#foo)
что относительный IRI обычно разрешается относительно текущей страницы, находя элемент с foo
Я бы. Но когда вы используете <base>
, вы меняете, где это выглядит.
Чтобы решить эту проблему, используйте лучшее значение. Поскольку ваша базовая ссылка - это текущий каталог, вы можете просто использовать имя текущего файла:
<line … marker-mid="url(this_page_name.html#arrow)" />
Если у вас есть другой <base>
HREF, чем то, что вы показали, как:
<base href="http://other.site.com/whee/" />
тогда вам нужно будет использовать абсолютную ссылку, например,
<line … marker-mid="url(http://my.site.com/this_page_name.html#arrow)" />
В Angular 2+ вы можете ввести базовый путь в свой модуль приложения вместо использования тега
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
Попробуйте с помощью JavaScript:
<line id="something" />
С родным:
document.getElementById('something').setAttribute('marker-mid', 'url(' + location.href + '#arrow)');
С помощью jQuery:
$('#something').attr('marker-mid', 'url(' + location.href + '#arrow)');
Это просто работает.
В контексте такого богатого веб-приложения, как построенное на Angular, где вам нужно установить <base>
тег, чтобы сделать навигацию в стиле HTML5, может быть грязно, чтобы попытаться исправить это навсегда.
В моем случае приложение, над которым я работал, показывало конструктор интерактивных диаграмм на основе SVG, который изменял бы URL-адрес приложения при выборе элементов в нем.
Что я сделал, так это добавил глобальный обработчик событий, который бы исправил все url(#...)
встроенные стили в любом <path>
элемент найден на странице:
$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
событие сработало.
Если вы не хотите изменять / анимировать SVG, есть более простое решение, чем изменение url()
параметр.
Включите SVG как изображение:
<img src="yourpath/image.svg">
Ember 2.7 заменит <base>
пометить с rootURL
который должен решить эту проблему.
Тем временем в моем d3 для градиентов я использую следующее:
.attr('fill', `url(${Ember.$(location).attr('href')}#my-gradient)`);
Если вы этого не сделаете, целевой элемент будет казаться прозрачным.
В настоящее время в Windows (04-2017) все браузеры ведут себя ожидаемым образом (mask = url ("# svgmask")). Chrome, Firefox, даже IE 11! - но Эдж приходит с ошибкой.
Поэтому для Microsoft Edge вам все равно нужно указать абсолютный путь (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>
Вы можете заархивировать его с помощью:
$("[marker-mid]").attr("marker-mid", function () {
return $(this).attr("marker-mid").replace("url(", "url(" + location.href);
});