Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<svelte:element this="svg"> generates proper html but doesn't get rendered properly. #7613

Closed
sandeep-gh opened this issue Jun 17, 2022 · 19 comments
Labels
awaiting submitter needs a reproduction, or clarification bug runtime Changes relating to runtime APIs svg

Comments

@sandeep-gh
Copy link

Describe the bug

I am using a python library that builds a json describing the html components. This is then fed to a svelte code that uses svelte:element to instantiate the html components. All components (button/iinput) works fine except for svg elements. The html is generated properly as shown below

<button value="9" class="...." style=""> <svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" style=""> </path></svg></button>```

however the svg doesn't gets rendered on the screen (the button does though)

### Reproduction

Kind of tricky for this case to build a reproduction. If really needed, I can try. Let know. 

### Logs

_No response_

### System Info

```shell
System:
    OS: Linux 5.15 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
    CPU: (4) arm64 Cortex-A72
    Memory: 2.39 GB / 3.68 GB
    Container: Yes
    Shell: 5.1.4 - /bin/bash
  Binaries:
    Node: 18.3.0 - /usr/bin/node
    npm: 8.11.0 - /usr/bin/npm
  Browsers:
    Firefox: 91.10.0esr
  npmPackages:
    svelte: ^3.48.0 => 3.48.0

Severity

annoyance

@yimme
Copy link

yimme commented Jun 17, 2022

I believe you have to define the width or height of the SVG using css / inline style.
e.g:

button svg {
    height: 16px;
}

This is default behaviour. Your usage of svelte:element is fine and not causing issues.

@sandeep-gh
Copy link
Author

This didn't help. I have two svg elements : one in plain html, another using svelte. They both are identcal in definition. HTML renders fine. Svelte one doesn't. Attaching the html and the output:

<div id="components">
      <div class="container mx-auto flex justify-center">
      <button value="9" class="bg-pink-100 text-gray-600 mr-1 mb-1 px-4 py-2 font-bold outline-none shadow shadow-sm rounded-md font-bold uppercase ease-linear transition-all duration-150 outline-none focus:outline-none hover:shadow-md hover:bg-gray-200 mx-1" style=""> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" style=""> </path></svg></button>
    
      
      </div>

    <div class="mx-auto container h-screen bg-gray-100/20" style=""> <div class="flex justify-center" style=""> <button value="9" class="bg-gray-100 text-gray-600 mr-1 mb-1 px-4 py-2 font-bold outline-none shadow shadow-sm rounded-md font-bold uppercase ease-linear transition-all duration-150 outline-none focus:outline-none hover:shadow-md hover:bg-gray-200 mx-1" style=""> <svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" width="16px" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" style=""> </path></svg></button></div></div></div>

svelte_bug

@sami-baadarani
Copy link

@sandeep-gh In the code that you have shared, the first svg element does not contain a height or width values. The second one does contain width, that is the reason why the second one is rendering and the first one isn't.
As Yimmie suggested above, you need to add a height or width to it.

@magentaqin
Copy link
Contributor

As @sami-baadarani said, the first svg element does not contain a height or width values. After adding width or height, they both work well. @sandeep-gh
Screen Shot 2022-07-08 at 9 40 28 AM
Screen Shot 2022-07-08 at 9 41 45 AM

@nicksulkers
Copy link

I came across something I believe is similar to this issue.

I'm doing something like this:

App.svelte

<script>
	import DynamicTag from "./DynamicTag.svelte";
</script>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:map="http:rsvg.org/dtd/map" viewBox="0 0 1024 1024">
	<DynamicTag element={"g"}><line stroke-width="3" stroke="#000" fill="#000" x1="100" x2="400" y1="100" y2="400" /></DynamicTag>
</svg>

DynamicTag.svelte

<script>
	export let element = "div";
</script>
<svelte:element this={element}><slot /></svelte:element>

The html generated and retrieved through the browser developer tools is flawless and will work if used statically, but renders nothing within my svelte app.
The DynamicTag component will work for any non-svg tag, or if svelte:element isn't used (but that'd defeat the purpose obviously).

TLDR
Not really having looked into this much, my guess would be that Svelte underneath creates the element using document.createElement and g, along with all other svg tags, only work when created with document.createElementNS.

@dmcclory
Copy link

I got curious and looked into the hint that @nicksulkers left. It looks like there are two relevant functions in src/runtime/internal/dom.ts:

  • element -> calls document.createElement
  • svg_element calls document.createElementNS with the SVG namespace

If you use a static name, the compiler is able to figure out that you want to use svg_element & imports it. Or you can add <svelte:options namespace="svg" /> to force the import.

The generated code for DynamicTag.svelte imports element & uses it:

import {
  // ...
  element as element_1,
  // ...
} from "svelte/internal";



function create_dynamic_element(ctx) {
  // ...
  return {
    c() {
      svelte_element = element_1(/*element*/ ctx[0]);

I was thinking it could be hard for the compiler to figure out that an arbitrary name is supposed to use the svg namespace, but that maybe adding the namespace would help. When you do, the correct function is brought in, but the compiler takes a different path & in the output, the element name ends up being svelte:element (which you can even see in the inspector)

import {
  //...
  svg_element,
} from "svelte/internal";


function create_dynamic_element(ctx) {
  // ...
  return {
    c() {
      svelte_element = svg_element("svelte:element");

it seems like we'd want it to output this:

import {
  // ...
  svg_element,
  // ...
} from "svelte/internal";

function create_dynamic_element(ctx) {
  // ...
  return {
    c() {
      svelte_element = svg_element(/*svg_element*/ ctx[0]);

(but I don't know how to do that 🤗)

postscript: I don't think it's simply about the presence or absence of a height or width attribute, because adding either to the sag element in App.svelte doesn't fix the problem

@seantimm
Copy link

Seeing the same problem here using svelte:element and svelte:self to generate an SVG structure. Once I figured out it was a namespace problem, I was able to add <svelte:options namespace="svg /> and get it working, but I think the intent (based on some older issues I looked up) is that Svelte should be auto-detecting and applying the namespace as needed for these?

@Conduitry
Copy link
Member

This should be supported now as of 3.51.0.

@geoffrich
Copy link
Member

Closed by #7695.

@sandeep-gh
Copy link
Author

I am not able to get this to work. Below is setup:

package.json

{
  ...
  ...
  },
  "devDependencies": {
    "@sveltejs/vite-plugin-svelte": "^1.0.0-next.11",
    ...
    "tailwindcss": "^3.1.8",
    "vite": "^2.3.7"
  },
  "dependencies": {
    "svelte": "^3.51.0"
  }
}

The html begin generate:

<div class="flex justify-center" style=""> <div class="flex flex-col space-y-4" style=""> 
<button value="myval" class="" style="">Click me  
<svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" style=""> </path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" style=""> </path></svg>
</button>
</div>
</div>

However, no svg is getting rendered. Not sure if I am missing something.

@baseballyama
Copy link
Member

I can see the SVG.
https://svelte.dev/repl/c63e6c14acfa4b06a5af56b350261330?version=3.51.0

Maybe I can easily understand the situation if you create REPL.

@micha-lmxt
Copy link

Here is a repl, which shows that the problem persists in v.3.55.0. https://svelte.dev/repl/5e1e989bbccb4d688b571006bc3c0d12?version=3.55.0

I would expect to see both the red and the green circle, since both look identical in html, but the green circle isn't rendered.
Refreshing the parent element of the svg makes it show up. (https://stackoverflow.com/questions/36339444)

@baseballyama
Copy link
Member

What is the use case of this?
If we support this, we need to add an additional runtime check and it has a performance overhead.

@baseballyama baseballyama reopened this Jan 3, 2023
@baseballyama baseballyama added awaiting submitter needs a reproduction, or clarification runtime Changes relating to runtime APIs svg labels Jan 3, 2023
@sandeep-gh
Copy link
Author

I have an one use case for this. I am developing a web development framework in Python based on justpy (https://github.com/justpy-org/justpy/). I am using svelte as the frontend library.
All the components of the webpage is described as a json and shipped over websocket to frontend
where it is rendered using svelte.
The use case for rendering svg comes in this regard.

@baseballyama
Copy link
Member

Why just using {#if tag === 'svg'} is not enough?

@sandeep-gh
Copy link
Author

I will try the tag=== 'svg' and get back.

@micha-lmxt
Copy link

Let me summarize: the problem is that svg elements need to be created with a different namespace. The namespace can't be changed afterwards.

In general, the problem can occur on all svg tags (svg, path, circle, etc ). I say 'can', since svelte is smart enough to change the namespace, if the svelte:element is a visual child of an svg tag. Example:

<svg>
    <svelte:element this={toggle?"circle":"ellipse"} {...props}/>
</svg>

This works. If you extract just the svelte:element into a different component, it would fail.

<svelte:options namespace='svg'/> can be used to work around this problem.

@baseballyama
Copy link
Member

We have easy workaround that is using <svelte:options namespace='svg'/>.
So I close this issue for now. If still someone has a issue, we can reopen this.

@sandeep-gh
Copy link
Author

@baseballyama workaround works for me now. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting submitter needs a reproduction, or clarification bug runtime Changes relating to runtime APIs svg
Projects
None yet
Development

No branches or pull requests