Inserire elemento SVG creato a partire da una stringa

di il
6 risposte

Inserire elemento SVG creato a partire da una stringa

Buongiorno a tutti.
Sto sperimentando con JavaScript per manipolare degli elementi SVG in una pagina, di seguito codice e spiegazione del problema:

HTML:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./css/svg-007.css" />
</head>

<body>
  <div id="container">
  </div>
</body>

<script src="./js/svg-007.js">
</script>

</html>

CSS (url: ./css/svg-007.css) :

*{
margin: 0;
padding: 0;
box-sizing: border-box;
}

div {
background-color: rgb(250, 235, 215);
border: solid 1px black;
}

#container {
width: 90vw;
height: 90vh;
}

svg {
border: green solid;
}

JAVASCRIPT (url: ./js/svg-007.js) :

const svgNS = 'http://www.w3.org/2000/svg';

const diagramCode = '<rect x="20" y="20" width="70" height="70" fill="green"></rect>';

const container = document.querySelector('#container');

const diagramElement = document.createElement('g');
diagramElement.innerHTML = diagramCode;

const svgContainerElm = document.createElementNS(svgNS, 'svg');

svgContainerElm.setAttribute('width', '100%');
svgContainerElm.setAttribute('height', '100%');

let rectangle = document.createElementNS(svgNS, 'rect');
rectangle.setAttribute('x', 20);
rectangle.setAttribute('y', 20);
rectangle.setAttribute('width', 50);
rectangle.setAttribute('height', 50);
rectangle.setAttribute('fill', 'red');

svgContainerElm.append(rectangle);
svgContainerElm.append(diagramElement);

container.append(svgContainerElm);

Sto lavorando su Ubuntu 22.04 e sto testando la pagina sia su Chromium ver.>116.0 che Firefox ver.>116.0. Scritto con VSCode anche se non dovrebbe avere importanza, credo.
Al di la del codice che di per se non ha alcun senso pratico trattandosi per ora di un esperimento, succede è che il rettangolo verde creato con la costante "diagramCode" che a sua volta viene inserito con qualche passaggio nell'elemento <svg> non viene visualizzato nella pagina a differenza invece dell'elemento “rectangle” rosso creato con document.createElementNS.
Ispezionando il codice con i developer tools la pagina sembra ben formata.

La cosa che trovo strana è che editando il codice HTML/SVG dai developer tool, se semplicemente faccio CUT dell'elemento corrispondente a “diagramCode” e poi faccio PASTE dello stesso elemento nella stessa identica posizione il rettangolo verde compare nella pagina.
Il codice della pagina appare assolutamente identico prima e dopo.

Una cosa che ho notato è che nel riquadro degli stili la seguente regola:

rect[Stile degli attributi] {
    x: 20;
    y: 20;
    width: 70;
    height: 70;
    fill: green;
}

compare quando seleziono il rettangolo verde solo dopo averlo tagliato e reincollato manualmente nel codice, mentre prima era del tutto assente. Invece per il rettangolo rosso la regola analoga (con valori diversi ovviamente) è presente fin da subito.

So che potrei generare entrambi gli elementi <rect> allo stesso modo del rosso, ma vorrei veramente capire dove sto sbagliando, visto che apparentemente lo stesso codice HTML funziona o non funziona a seconda dei casi e senza un motivo apparente. Penso sia legato alla regola “fantasma” ma in che modo non lo so.

Grato a chi avrà la pazienza di leggere e illuminarmi….!

6 Risposte

  • Re: Inserire elemento SVG creato a partire da una stringa

    20/08/2023 - martin.p ha scritto:


    compare quando seleziono il rettangolo verde solo dopo averlo tagliato e reincollato manualmente nel codice, mentre prima era del tutto assente. Invece per il rettangolo rosso la regola analoga (con valori diversi ovviamente) è presente fin da subito.

    Siamo sicuri che non sia un problema di profondità (z-order)? Ossia che uno dei due rettangoli “copre” l'altro in quanto viene mostrato sopra?

    Non ho analizzato il codice a fondo perché è abbastanza corposo.
    Sarebbe utile caricarlo su un Fiddle o qualcosa del genere per testare meglio la casistica.

  • Re: Inserire elemento SVG creato a partire da una stringa

    Ho provato a creare un fiddle con il tuo codice e l'ho modificato in modo da generare il rettangolo verde allo stesso modo in cui generi quello rosso. Così pare funzionare: https://jsfiddle.net/d35cxr79/

    Adesso non vorrei dire castronerie ma è come se inserendo il diagramCode come stringa HTML all'interno del diagramElement questo non venisse correttamente interpretato come elemento HTML. Per intenderci, qui:

    diagramElement.innerHTML = diagramCode;

    Probabilmente quando vai a tagliare e incollare da dev tool il browser ti aiuta a reinserirlo correttamente come HTML.

    Ci tengo a precisare, però, che le mie sono solamente supposizioni…

  • Re: Inserire elemento SVG creato a partire da una stringa

    Stando a quanto indicato in questa pagina, apparentemente è obbligatorio usare createElementNS() per creare elementi dello standard SVG.

    La ragione risiederebbe nel fatto che, sebbene SVG sia integrato in HTML5, appartiene a uno standard derivante da XML e quindi con regole più “stringenti” rispetto all'HTML tradizionale, nel quale potrebbero trovare posto degli elementi con nome “non standard” (grazie alla flessibilità di HTML5 stesso) eventualmente in conflitto con quelli di SVG, che invece deve seguire una struttura più rigorosa e rigida.

    La pagina indicata sopra appartiene alla guida di un tool che segnala questo utilizzo scorretto per prevenire le problematiche che hai incontrato.

    Quindi, continua pure con createElementNS() per i suddetti motivi, e grazie per l'occasione di fare luce su questa particolarità. :D

  • Re: Inserire elemento SVG creato a partire da una stringa

    Grazie a voi per le risposte super-veloci!

    Non credo sia lo z-index perché sullo sfondo si dovrebbe vedere il verde che é apposta leggermente più grande.

    Per Pietrantonio: si così funziona avendo utilizzato lo stesso metodo del rettangolo rosso. Quindi probabilmente la via corretta é createElementNS come dice Alka.

    Forse, per una serie di ragioni, é preferibile creare gli elementi senza dover passare delle stringhe e utilizzare invece le API specifiche per creare e manipolare gli elementi.

    Approfondirò meglio e vi terrò informati, se l'argomento é interessante.

  • Re: Inserire elemento SVG creato a partire da una stringa

    Allora modificando il codice JS in questo modo tutto funziona:

    const svgNS = 'http://www.w3.org/2000/svg';
    
    const diagramCode = '<rect x="20" y="20" width="70" height="70" fill="green"></rect>';
    
    const container = document.querySelector('#container');
    
    const diagramElement = document.createElementNS(svgNS,'g');
    diagramElement.innerHTML = diagramCode;
    
    const svgContainerElm = document.createElementNS(svgNS, 'svg');
    
    svgContainerElm.setAttribute('width', '100%');
    svgContainerElm.setAttribute('height', '100%');
    
    let rectangle = document.createElementNS(svgNS, 'rect');
    rectangle.setAttribute('x', 20);
    rectangle.setAttribute('y', 20);
    rectangle.setAttribute('width', 50);
    rectangle.setAttribute('height', 50);
    rectangle.setAttribute('fill', 'red');
    
    svgContainerElm.append(diagramElement);
    svgContainerElm.append(rectangle);
    
    container.append(svgContainerElm);

    in pratica ho creato l'elemento <g> con createElementNS e poi ho impostato la proprietà .internalHTML con il codice-stringa del rettangolo verde che viene cosi parsato correttamente.

    Poi ho provato cosi:

    const container = document.querySelector('#container');
    
    const diagramCode = `<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
    <rect x="20" y="20" width="70" height="70" fill="green"></rect>
    </svg>`;
    
    //parsing SVG code
    const parser = new DOMParser().parseFromString(diagramCode, 'image/svg+xml');
    
    const diagramElement = document.importNode(parser.documentElement, true);
    
    container.append(diagramElement);

    e di nuovo funziona correttamente (in questo caso genero solo il rettangolo verde, il rosso l'ho momentaneamente eliminato).

    Infine ultimo test ho provato a inserire l'elemento <svg> direttamente nell' HTML e ad agganciarlo con una variabile, per poi cercare di appendere sotto lo stesso rettangolo rosso, creato con la funzione .createElementNS

    const svgNS = 'http://www.w3.org/2000/svg';
    
    const container = document.querySelector('#container');
    
    const svg=document.querySelector('svg');
    
    const diagramCode = `<rect x="20" y="20" width="70" height="70" fill="green"></rect>`;
    
    //parsing SVG code
    const parser = new DOMParser().parseFromString(diagramCode, 'image/svg+xml');
    
    const diagramElement = document.importNode(parser.documentElement, true);
    
    svg.append(diagramElement);

    e nuovamente lo stesso problema: pagina apparentemente ben formata ma il rettangolo non compare, se non dopo averlo eliminato e reinserito con copy-paste dal developer tool. Probabilmente l'oggetto DOMParser o la funzione .importNode fa a cazzotti con elementi della famiglia <SVG> che non abbiano come root proprio l'elemento <svg>, probabilmente un limite della decodifica delle stringhe HTML di input.

    In ogni caso, bazzicando per la rete e dando un'occhiata a librerie che permettono di manipolare SVG non mi sembra di aver visto funzioni che consentono di parsare stringhe, o per lo meno mi sembra che la via preferita sia quella di creare gli elementi e settarne gli attributi tramite funzioni. E mi sono convinto che questa è la via migliore, implementandola magari con delle classi ad hoc.

  • Re: Inserire elemento SVG creato a partire da una stringa

    22/08/2023 - martin.p ha scritto:


    Probabilmente l'oggetto DOMParser o la funzione .importNode fa a cazzotti con elementi della famiglia <SVG> che non abbiano come root proprio l'elemento <svg>, probabilmente un limite della decodifica delle stringhe HTML di input.

    Non è un limite, ma è proprio studiato così: come da documentazione, nella stringa che passi deve esserci un elemento <svg> completo.

Devi accedere o registrarti per scrivere nel forum
6 risposte