Dynamically created SVG element not rendered by the browser

Similar Dynamically created SVG elements are not rendered by the browser but my code already corresponded to their solutions.

I am trying to use SVG markup in my HTML in order to render some texts. The problem was very tricky because I just realized that the issue is when generating the SVG programmatically.

The markup

What I want to end up in my page is this fragment of code:

html {
  background-color: #151515;
  color: white;
  background-image: url(https://picsum.photos/id/210/500/500);
  background-size: 500px 500px;
}

body {
  margin: 0;
  padding: 0;
}

#navbar {
  font-family: 'Katahdin Round', 'Arial Narrow Bold', sans-serif;
  list-style: none;
}

#navbar .letter-path {
  fill: white;
  stroke: transparent;
  stroke-width: 2.5;
  stroke-dasharray: 400;
  stroke-dashoffset: 400;
  transition: fill 0.1s ease-in 0.1s, stroke 0.25s ease-out, stroke-dashoffset 0s linear 0.25s;
}

#navbar svg:hover .letter-path {
  fill: rgba(255, 255, 255, 0.3);
  stroke: white;
  stroke-dashoffset: 0;
  transition: fill 0.25s ease-out, stroke 0s, stroke-dashoffset 0.5s ease-in 0.1s;
}

#blurred-clip-background {
  clip-path: url(#blur-letter-paths);
  filter: blur(3px);
}
<body>
  <ul id="navbar" class="relative inline-block">
    <li><svg xmlns="http://www.w3.org/2000/svg" height="100" viewBox="0 0 279.468 100">
                <defs>
                    <path id="letter-path-0" class="letter-path-T"
                        d="M27.934 79.496c-3.127 0-4.69-1.564-4.69-4.69V22.06H7.19c-3.127 0-4.691-1.563-4.691-4.69V7.05c0-3.127 1.563-4.69 4.69-4.69h51.807c3.127 0 4.69 1.563 4.69 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H42.944v52.744c0 3.127-1.563 4.69-4.69 4.69z"
                        transform="translate(0, 0)"></path>
                    <path id="letter-path-1" class="letter-path-E"
                        d="M56.6 74.805c0 3.127-1.564 4.69-4.692 4.69H7.191c-3.127 0-4.691-1.563-4.691-4.69V7.051c0-3.127 1.563-4.69 4.69-4.69h44.718c3.128 0 4.691 1.563 4.691 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H22.2v9.694h21.89c3.127 0 4.69 1.564 4.69 4.69v8.861c0 3.127-1.563 4.69-4.69 4.69H22.2v9.8h29.707c3.128 0 4.691 1.563 4.691 4.69z"
                        transform="translate(69.187, 0)"></path>
                    <path id="letter-path-2" class="letter-path-X"
                        d="M38.05 23.104l11.987-17.72c1.356-1.981 3.336-3.024 5.733-3.024h13.342c3.857 0 5.212 2.502 3.023 5.733L49.933 40.615l22.515 33.148c2.19 3.231.833 5.733-3.023 5.733H56.083c-2.397 0-4.378-1.042-5.734-3.023l-12.3-18.241-12.404 18.241c-1.356 1.98-3.335 3.023-5.733 3.023H6.57c-3.857 0-5.212-2.502-3.023-5.733l22.515-33.148L3.86 8.093C1.67 4.862 3.027 2.36 6.883 2.36h13.342c2.396 0 4.378 1.043 5.733 3.023z"
                        transform="translate(131.286, 0)"></path>
                    <path id="letter-path-3" class="letter-path-T"
                        d="M27.934 79.496c-3.127 0-4.69-1.564-4.69-4.69V22.06H7.19c-3.127 0-4.691-1.563-4.691-4.69V7.05c0-3.127 1.563-4.69 4.69-4.69h51.807c3.127 0 4.69 1.563 4.69 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H42.944v52.744c0 3.127-1.563 4.69-4.69 4.69z"
                        transform="translate(210.281, 0)"></path>
                    <clipPath id="blur-letter-paths">
                        <use href="#letter-path-0"></use>
                        <use href="#letter-path-1"></use>
                        <use href="#letter-path-2"></use>
                        <use href="#letter-path-3"></use>
                    </clipPath>
                </defs>
                <image id="blurred-clip-background" x="-40" y="-16" width="500" height="500"
                    href="https://picsum.photos/id/210/500/500"></image>
                <use href="#letter-path-0" class="letter-path"></use>
                <use href="#letter-path-1" class="letter-path"></use>
                <use href="#letter-path-2" class="letter-path"></use>
                <use href="#letter-path-3" class="letter-path"></use>
            </svg></li>
    <!-- <li>PROJECTS</li>
        <li>CONTACT</li> -->
  </ul>

  <!-- <script src="script.js"></script> -->
</body>

If you take this and paste it inside a page, all is fine and the text is rendered!

Creating the SVG dynamically

But I want to create this content using JavaScript, so I have this:

// Dictionary of paths for each letter
const letterPaths = {
    E: "M56.6 74.805c0 3.127-1.564 4.69-4.692 4.69H7.191c-3.127 0-4.691-1.563-4.691-4.69V7.051c0-3.127 1.563-4.69 4.69-4.69h44.718c3.128 0 4.691 1.563 4.691 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H22.2v9.694h21.89c3.127 0 4.69 1.564 4.69 4.69v8.861c0 3.127-1.563 4.69-4.69 4.69H22.2v9.8h29.707c3.128 0 4.691 1.563 4.691 4.69z",
    T: "M27.934 79.496c-3.127 0-4.69-1.564-4.69-4.69V22.06H7.19c-3.127 0-4.691-1.563-4.691-4.69V7.05c0-3.127 1.563-4.69 4.69-4.69h51.807c3.127 0 4.69 1.563 4.69 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H42.944v52.744c0 3.127-1.563 4.69-4.69 4.69z",
    X: "M38.05 23.104l11.987-17.72c1.356-1.981 3.336-3.024 5.733-3.024h13.342c3.857 0 5.212 2.502 3.023 5.733L49.933 40.615l22.515 33.148c2.19 3.231.833 5.733-3.023 5.733H56.083c-2.397 0-4.378-1.042-5.734-3.023l-12.3-18.241-12.404 18.241c-1.356 1.98-3.335 3.023-5.733 3.023H6.57c-3.857 0-5.212-2.502-3.023-5.733l22.515-33.148L3.86 8.093C1.67 4.862 3.027 2.36 6.883 2.36h13.342c2.396 0 4.378 1.043 5.733 3.023z",
};

const letterWidths = {
    E: 54.099,
    T: 61.187,
    X: 70.995,
}

// Letter spacing constant
const LETTER_SPACING = 8;
const HEIGHT = 100;

// Function to create SVG for a word
function createWordSVG(word)
{
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
    const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
    clipPath.setAttribute('id', 'blur-letter-paths');
    const image = document.createElementNS('https://www.w3.org/2000/svg', 'image')
    image.setAttribute('id', 'blurred-clip-background');
    image.setAttribute('x', '-40'); // TODO: dynamic depending on svg pos
    image.setAttribute('y', '-16'); // TODO: dynamic depending on svg pos
    image.setAttribute('width', '500');
    image.setAttribute('height', '500');
    image.setAttribute('href', 'https://picsum.photos/id/210/500/500'); // xlink:href didn't work either
    
    svg.appendChild(defs);
    svg.appendChild(image);

    let totalWidth = 0;
    word.toUpperCase().split('').forEach((letter, index) =>
        {
            if (letterPaths[letter])
            {
                defs.appendChild(createLetterPath(letter, index));
                clipPath.appendChild(createLetterUse(index, false))
                svg.appendChild(createLetterUse(index, true))

                totalWidth += letterWidths[letter] + LETTER_SPACING;
            }
        });

    defs.appendChild(clipPath);

    function createLetterPath(letter, index) {
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute("id", `letter-path-${index}`)
        path.setAttribute("class", `letter-path-${letter}`)
        path.setAttribute("d", letterPaths[letter]);
        path.setAttribute("transform", `translate(${totalWidth}, 0)`);
        return path
    }

    function createLetterUse(index, isPath) {
        const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
        use.setAttribute("href", `#letter-path-${index}`)
        if (isPath) {
            use.setAttribute("class", `letter-path`)
        }
        return use
    }

    svg.setAttribute("height", `${HEIGHT}`);
    svg.setAttribute("viewBox", `0 0 ${totalWidth} ${HEIGHT}`);

    return svg;
}

// Main function to replace text in list items with SVGs
function replaceTextWithSVG()
{
    const listItems = document.querySelectorAll('li');

    listItems.forEach(li =>
    {
        const text = li.textContent.trim();
        const svg = createWordSVG(text);
        li.textContent = ''; // Clear the text
        li.appendChild(svg);
    });
}

// Call the function when the document is ready
document.addEventListener('DOMContentLoaded', replaceTextWithSVG);
html {
    background-color: #151515;
    color: white;
    background-image: url(https://picsum.photos/id/210/500/500);
    background-size: 500px 500px;
}

body {
    margin: 0;
    padding: 0;
}

#navbar {
    font-family: 'Katahdin Round', 'Arial Narrow Bold', sans-serif;
    list-style: none;
}

#navbar .letter-path {
    fill: white;
    stroke: transparent;
    stroke-width: 2.5;
    stroke-dasharray: 400;
    stroke-dashoffset: 400;
    transition: fill 0.1s ease-in 0.1s, stroke 0.25s ease-out, stroke-dashoffset 0s linear 0.25s;
}

#navbar svg:hover .letter-path {
    fill: rgba(255, 255, 255, 0.3);
    stroke: white;
    stroke-dashoffset: 0;
    transition: fill 0.25s ease-out, stroke 0s, stroke-dashoffset 0.5s ease-in 0.1s;
}

#blurred-clip-background {
    clip-path: url(#blur-letter-paths);
    filter: blur(3px);
}
<body>
    <ul id="navbar" class="relative inline-block">
        <li>TEXT</li>
        <!-- <li>PROJECTS</li>
        <li>CONTACT</li> -->
    </ul>

    <script src="script.js"></script>
</body>

Well, try to execute this in fiddle or in your browser and you’ll see it will not be rendered. When you inspect the HTML, you see that the -tag is not taking any space.

What is the problem?

This post is almost the same as the one made by Andry a while ago, where the problem was the creation of svg-elements using createElement instead of createElementNS. This isn’t my problem though, because in my case all svg-elements are already generated using the correct way. This becomes apparent, because everything else works, not the -tag though.