Использование базового тега на странице, содержащей элементы маркера SVG, не отображает маркер
У меня возникла проблема при попытке использовать элементы маркера SVG в визуализации на основе SVG. Я добавляю свои изменения в веб-приложение, которое включает базовый тег на каждой странице, поэтому любые ссылки на файлы CSS, файлы javascript и т.д. Могут быть относительными.
У меня есть пример кода ниже, который воспроизводит проблему. Существует элемент линии и определенный элемент маркера. Элемент маркера ссылается на строку в свой атрибут "маркер-конец", через uri и id маркера. Без базового тега стрелка отображается в порядке. С базовым тегом он не отображается. Причина в том, что базовый тег изменяет способ разрешения URL-адресов браузером.. даже для простого URL-адреса, основанного на идентификаторе, указанного в атрибуте конца маркера строки.
Есть ли способ обойти эту проблему, не удаляя базовый тег?
Я не могу удалить его, потому что его использование довольно укоренилось в продукте, над которым я работаю. Мне нужно поддерживать Firefox, Chrome и IE9 + для моего webapp. Проблема с Firefox и хромом. 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
HTML <base>
element используется, чтобы сказать "разрешить все относительные 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/" />
тогда вам нужно будет использовать абсолютный href, например.
<line … marker-mid="url(http://my.site.com/this_page_name.html#arrow)" />
Ответ 2
В контексте богатого веб-приложения, такого как построенный на 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
.
Ответ 3
Попробуйте использовать javascript:
<line id="something" />
С native:
document.getElementById('something').setAttribute('marker-mid', 'url(' + location.href + '#arrow)');
С помощью jQuery:
$('#something').attr('marker-mid', 'url(' + location.href + '#arrow)');
Он просто работает.
Ответ 4
Если вы не хотите изменять/анимировать svg, существует более простое решение, чем изменение параметра url()
.
Включите svg как изображение:
<img src="yourpath/image.svg">
Ответ 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
Ответ 6
Ember 2.7 заменит тег <base>
на rootURL
, который должен исправить эту проблему.
Тем временем в моем d3 для градиентов я использую следующее:
.attr('fill', `url(${Ember.$(location).attr('href')}#my-gradient)`);
Если вы этого не сделаете, элемент, на который вы нацеливаетесь, будет казаться прозрачным.
Ответ 7
В Windows в настоящее время (04-2017) все браузеры ведут себя как ожидалось (mask = url ( "# svgmask" )). Chrome, Firefox, даже IE 11! - но Edge появляется с ошибкой.
Итак, для Microsoft Edge вам все равно нужно указать абсолютный путь (mask = "url (путь/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>