J'ai rencontré un problème en essayant d'utiliser des éléments de marqueur SVG dans une visualisation basée sur SVG. J'ajoute mes modifications à une application Web qui inclut une balise de base sur chaque page, de sorte que toute référence à des fichiers CSS, à des fichiers javascript, etc. puisse être relative.
J'ai un exemple de code ci-dessous qui reproduit le problème. Il existe un élément de ligne et un élément marqueur définis. L'élément marker est référencé par la ligne dans son attribut 'marker-end', via uri et id of marker. Sans la balise de base, la flèche s’affiche correctement. Avec la balise de base, elle n’est pas affichée. La raison en est que la balise de base modifie la façon dont le navigateur résout les URL. Même pour l'URL basée sur l'ID simple spécifiée dans l'attribut marker-end de la ligne.
Est-il possible de contourner ce problème sans avoir à supprimer la balise de base?
Je ne peux pas vraiment l'enlever car son utilisation est assez enracinée dans le produit sur lequel je travaille. Je dois prendre en charge Firefox, Chrome et IE9 + pour mon application Web. Firefox et Chrome produisent tous les deux ce problème. IE fonctionne bien (c'est-à-dire que les marqueurs de flèche sont affichés).
<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>
L'élément HTML <base>
est utilisé pour dire "résolvez toutes les URL relatives relatives non pas à cette page, mais à un nouvel emplacement". Dans votre cas, vous lui avez dit de résoudre relative au répertoire avec la page HTML.
L'attribut SVG marker-mid="url(…)"
est un Référence FuncIRI . Lorsque vous utilisez une valeur telle que url(#foo)
, cet IRI relatif est normalement résolu par rapport à la page en cours, en recherchant l'élément avec l'ID foo
. Mais lorsque vous utilisez <base>
, vous modifiez son apparence.
Pour résoudre ce problème, utilisez une meilleure valeur. Puisque votre référence de base est le répertoire courant, vous pouvez simplement utiliser le nom du fichier actuel:
<line … marker-mid="url(this_page_name.html#arrow)" />
Si vous avez un <base>
href différent de ce que vous avez montré, par exemple:
<base href="http://other.site.com/whee/" />
alors vous devrez utiliser un href absolu, par exemple.
<line … marker-mid="url(http://my.site.com/this_page_name.html#arrow)" />
Dans le contexte d'une application Web enrichie, telle que celle basée sur Angular, dans laquelle vous devez définir la balise <base>
pour permettre une navigation de style HTML5, il peut être compliqué d'essayer de résoudre ce problème de manière permanente.
Dans mon cas, l'application sur laquelle je travaillais montrait un constructeur de diagramme interactif basé sur SVG qui modifierait l'URL de l'application lorsque j'y sélectionnais des éléments.
Ce que j'ai fait est d'ajouter un gestionnaire d'événement global qui résoudrait tous les styles en ligne url(#...)
dans n'importe quel élément <path>
trouvé dans la page:
$rootScope.$on 'fixSVGReference', ->
$('path').each ->
$path = $ this
if (style = $path.attr 'style')?
$path.attr 'style', style.replace /url\([^)#]*#/g, "url(#{location.href}\#"
Puis déclenchez ce gestionnaire aux endroits clés, comme lorsque l'état de l'application change (j'utilise ui-router ).
$rootScope.$on '$stateChangeSuccess', ->
$timeout (-> $rootScope.$emit 'fixSVGReference'), 5
Ainsi que partout où je sais qu'il y aurait des chemins nouveaux/mis à jour comme ceux-ci. $timeout
doit ici tenir compte du fait que les nœuds DOM sont réellement modifiés de manière asynchrone après le déclenchement de l'événement $stateChangeSuccess
.
Essayez avec javascript:
<line id="something" />
Avec natif:
document.getElementById('something').setAttribute('marker-mid', 'url(' + location.href + '#arrow)');
Avec jQuery:
$('#something').attr('marker-mid', 'url(' + location.href + '#arrow)');
Ça fonctionne.
Dans Angular 2+, vous pouvez injecter le chemin de base dans votre module d'application au lieu d'utiliser la balise <base>. Cela a résolu le problème dans Edge et Firefox pour moi.
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
Si vous ne souhaitez pas modifier/animer le svg, il existe une solution plus simple que de modifier le paramètre url()
.
Inclure le svg en tant qu'image:
<img src="yourpath/image.svg">
Sous Windows actuellement (04-2017), tous les navigateurs se comportent comme prévu (mask = url ("# svgmask")). Chrome, Firefox, même IE 11 !! - mais Edge arrive avec une erreur.
Donc, pour Microsoft Edge, vous devez toujours donner le chemin absolu ( mask = "url (chemin/à/this-document.htm # svgmask)" ) pour votre ID de masque lorsque vous utilisez une balise de base votre document:
<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>
Vous pouvez l'archiver avec:
$("[marker-mid]").attr("marker-mid", function () {
return $(this).attr("marker-mid").replace("url(", "url(" + location.href);
});