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

Попробуйте с помощью 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); 
});
Другие вопросы по тегам