11540 sujets

JavaScript, DOM et API Web HTML5

Bonjour, ou plutôt devrais-je dire bonsoir,

je suis entrain de tenter de de faire interagir un évènement extérieur et une animation svg.

voici le scénario (simple) :
un lien <a id="monBouton">Bouton</a>
un svg inclus dans un <object> qui dessine un carré rouge
quand je clic sur le lien, je veux obtenir un carré jaune

du côté du html :

<a id="monBouton">Bouton</a>
<object id="objet1" type="image/svg+xml" data="monanimsvg.svg"></object>

et du côté de svg :

<svg width="300px" height="300px" viewBox="0 0 300 300"  version="1.1" xmlns="http://www.w3.org/2000/svg">
<!--un carré rouge-->
<rect id="carre" width="300" height="300" fill="red" />


comment intervenir de manière extérieure sur le svg?
j'ai essayé plusieurs choses:
1/ insérer un <animate> :

<rect id="carre" width="300" height="300" fill="red" >
<animate id="monAnim" attributeName="fill" attributeType="XML" from="red" to="yellow" begin="monBouton.click" dur="2s" fill="freeze" />
</rect>


...mais ça ne marche pas

2/ mettre un onClick sur le lien + fonction javascript ds le svg
<a onclick="doSomething()">Bouton</a>


<script type="text/ecmascript"><![CDATA[
  function doSomething() {
   carre = document.getElementById("carre"); 
   carre.setAttribute("fill", "yellow"); 
]]>
</script>
<rect id="carre" width="300" height="300" fill="red" />


...mais ça ne marche pas non plus

3/ mettre un onClick sur le lien + fonction javascript ds le html
<a onclick="doSomething()">Bouton</a>
<script type="text/javascript">
  function doSomething() {
   carre = document.getElementById("carre"); 
   carre.setAttribute("fill", "yellow"); 
</script>


<rect id="carre" width="300" height="300" fill="red" />


...mais ça ne marche pas non plus.
Je soupçonne qu'il y ait un problème de ciblage de l'élément "carre" puisqu'il se trouve dans un <object> mais je ne sais pas comment l'atteindre dans le DOM

Voilà où j'en suis, j'aimerais bien vos retours sur ce type de petit jeu.
Merci à tous Smiley smile
Bonjour,

Le problème ici c'est que vous faites une inclusion de votre SVG via une balise object, c'est donc un élément extérieur à votre page et il est accessible sous certaines contraintes. C'est un peu comme pour les iframes si vous avez déjà manipulé ce type d'élément :
- Il faut que votre élément inclus appartienne au même domaine que votre page (par exemple a.com/page.html ne pourra pas accéder au DOM de b.com/obj.svg). Il existe cependant des techniques
- Le DOM de votre SVG doit être chargé au moment ou vous souhaitez le parcourir

Donc déjà si vous faites vos tests en local il faut que vous fassiez tourner un petit serveur (type WAMP) pour ne pas être embêté avec les blocages cross domain security. (=> si vous utilisez Chrome vous pouvez toujours essayer ça pour éviter d'avoir recours à un serveur)

Voici une solution JS natif, placé dans un script avant la fermeture de body (ou dans une fonction s'exécutant lorsque votre document est chargé).

var svg = document.getElementById('objet1'),
    bouton = document.getElementById('monBouton'),
    svgDoc = null;
		
// On écoute l'évènement onload sur l'objet SVG
svg.addEventListener('load', function(){
   // une fois que le doc est prêt on l'assigne à svgDoc
   svgDoc = svg.contentDocument;	
});

// Au clic sur le lien
bouton.addEventListener('click', function(e){
   // on ne suit pas le lien
   e.preventDefault(); 
			
   if (svgDoc !== null) {
      // changement de couleur
      svgDoc.getElementById('carre').style.fill = 'yellow';
   }
});

Sinon il y a plus rapide et moins propre
var get = document.getElementById.bind(document); // petit helper

get('monBouton').addEventListener('click', function(e){
   e.preventDefault(); 
   // on suppose qu'au moment du clic le document SVG aura eu le temps de se charger
   get('objet1').contentDocument.getElementById('carre').style.fill = 'yellow';
});

Encore plus rapide avec jQuery
$('#monBouton').click(function(e){
   e.preventDefault(); 
   // mais un peu moche...
   $( '#carre', $('#objet1')[0].contentDocument ).css('fill','red'); 
});


En revanche, vous n'êtes pas obligé d'inclure votre SVG via la balise <object>, il peut faire partie du DOM de votre page parente, et dans ce cas vous n'aurez aucun problème à y accéder directement en sélectionnant l'id de votre carré. Dans ce cas, une solution pure CSS est même possible :
Voir ici et pour la blague ici (pas très utile ni maintenable)
Modifié par Freez (20 Feb 2015 - 12:09)
Merci beaucoup pour votre explication.
pour le coup, c'est la version simplifiée en javascript pur qui fonctionne le mieux, donc elle est adoptée.
J'ai également réussi grâce à vous à lancer un <animate> :

côté commandes (html & js)

<a id="monBouton">ANIMATION</a>
<object id="objet1" type="image/svg+xml" data="anime-simple.svg"></object>
<script type="text/javascript">
var get = document.getElementById.bind(document);
get('monBouton').addEventListener('click', function(e){
e.preventDefault(); 
get('objet1').contentDocument.getElementById('carre').style.fill='yellow';
get('objet1').contentDocument.getElementById('anim').beginElement();
});
</script>


et côté anim (svg)

<svg width="300px" height="300px" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect id="carre" width="300" height="300" style="fill:red;"/>
<rect id="test" width="50" height="50" style="fill:pink;">
<animate id="anim" attributeName="y" attributeType="XML" from="0" to="40" begin="indefinite" dur="2s" fill="freeze" />
</rect>
</svg>


Donc problème : [RESOLU] !
Merci beaucoup Smiley smile