menu

Questions & Answers

Dynamically generated SVG not using clip-path

I have an SVG file that I import with Vue JS, using vite-svg-loader. I import the file as a raw string, like so:

import DelawareBounds from './assets/images/DelawareOutlineNew.min.svg?raw'

Then, I read the string into an SVG element like so:

        const parser = new DOMParser();
        const DelawareMask = parser.parseFromString(DelawareBounds, "image/svg+xml").documentElement

I then add some content to the svg and add a clip-path from #DelawareBounds that already existed in the svg

            const dataGroup = document.createElementNS('http://www.w3.org/2000/svg','g')
            dataGroup.setAttributeNS('http://www.w3.org/2000/svg','id','data')
            dataGroup.setAttributeNS('http://www.w3.org/2000/svg','clip-path','url(#DelawareBounds)')

            let pathGroup
            for (let path of paths) {
                pathGroup = document.createElementNS('http://www.w3.org/2000/svg','g')                                
                pathGroup.setAttribute('style','transform: scale('+xScale+','+yScale+')')
                pathGroup.setAttributeNS('http://www.w3.org/2000/svg','class','delaware-bounds-scale')
                pathGroup.appendChild(path)

                dataGroup.appendChild(pathGroup)
            }

            DelawareMask.appendChild(dataGroup)

The clip-path does not work when I add the content this way. If I take the generated content and paste it into a new file, the clip-path works perfectly.

From my research, the most of these types of issues comes down to making sure to allocating the proper svg namespace. For all my dynamic content, I've made sure to add the namespace.

The only thing I can think of is that when I'm parsing the imported svg string, the data content is not going into the proper namespace. I'm not sure what else to do here? Any suggestions are appreciated.

Before the dynamic content is added, the svg looks like this:

<svg version="1.2" viewBox="0 0 2431 5145" xmlns="http://www.w3.org/2000/svg" fill="none">
    <defs>
      <clipPath id="DelawareBounds">
        <path d="...some content here..."/>
      </clipPath>
    </defs>
</svg>

after the dynamic content is added, the svg looks like this:

<svg version="1.2" viewBox="0 0 2431 5145" xmlns="http://www.w3.org/2000/svg" fill="none">
    <defs>
      <clipPath id="DelawareBounds">
        <path d="...some content here..."/>
      </clipPath>
    </defs>
    <g id="data" clip-path="url(#DelawareBounds)">
      ...some data here... 
    </g>
</svg>

The result is that the data shows appropriately, but the clip-path does not clip the data content.

Comments:
2023-01-24 23:10:17
Why not use a .vue file and add dynamic values regarding the state?
2023-01-24 23:10:17
@kissu I need to add the SVG as an overlay to a Leaflet Map.
Answers(2) :

Generally only elements are created in a specific namespace.

Attributes are usually in the null namespace, so the correct code is.

            const dataGroup = document.createElementNS('http://www.w3.org/2000/svg','g')
            dataGroup.setAttribute('id','data')
            dataGroup.setAttribute('clip-path','url(#DelawareBounds)')

            let pathGroup
            for (let path of paths) {
                pathGroup = document.createElementNS('http://www.w3.org/2000/svg','g')                                
                pathGroup.setAttribute('style','transform: scale('+xScale+','+yScale+')')
                pathGroup.setAttribute('class','delaware-bounds-scale')
                pathGroup.appendChild(path)

                dataGroup.appendChild(pathGroup)
            }

            DelawareMask.appendChild(dataGroup)

I found a solution to my problem, but still curious if there are other ways to do this.

My solution was to include:

<g id="data" clip-path="url(#DelawareBounds)"></g>

Then adding content inside the #data group worked. Instead of dynamically generating that line above as well as the content that goes inside.