Использование базового тега на странице, содержащей элементы маркера 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>