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

Adding SVG subelements via Turbo Streams leads to them not being rendered #1223

Open
AsherWright opened this issue Mar 13, 2024 · 2 comments

Comments

@AsherWright
Copy link

Overview

I'd like to edit SVG elements with turbo streams. One problem is that when I try to add an element via a turbo_stream, it isn't rendered. For instance, appending a <circle> inside of an SVG.

Code example

Showing the code for a rails app, but the idea would be the same with another framework:

html page:

<div class="w-3/4 h-96">
  <svg>
    <g>
      <g is="turbo-frame" id="circles">
        <circle r="15" fill="#004d40" transform="translate(50, 50)"></circle>
      </g>
    </g>
  </svg>
</div>
<div>
  <%= button_to "Action", action_path, method: :post %>
</div>

Turbo stream event:

<%= turbo_stream.append "circles" do %>
  <circle r="15" fill="#004d40" transform="translate(50, 100)"></circle>
<% end %>

After the event fires, we see it in the browser's inspect, and it's in the right spot. But it's not visible:
image

We can "fix" it by wrapping the <circle> in its own <svg>, but then there's a bunch of unneeded SVGs.

My uninformed hypothesis is that it has to do with element namespaces? And that SVGs may require the namespace when adding elements?

@AsherWright
Copy link
Author

After checking the namespaceURI of each element, I do think it might be the problem. For the existing SVG circle, it's 'http://www.w3.org/2000/svg' and for the circle that the turbo stream adds it's 'http://www.w3.org/1999/xhtml'

@omarluq
Copy link
Contributor

omarluq commented Mar 18, 2024

The append action uses document.append under the hood which by default adheres to the XHTML namespace http://www.w3.org/1999/xhtml and has very strict rules about it, you can get around this by using a custom turbo stream action that implements document.insertAdjacentHTML on 'beforeend' to get the same append behavior but more flexibility with namespaces.

This is an example of a custom action to handle this that I've tested

import { StreamActions } from "@hotwired/turbo"

StreamActions.appendSvgCircle = function() {
  this.removeDuplicateTargetChildren()
  this.targetElements.forEach((targetElement) => {
    let div = document.createElement('div');
    div.appendChild(this.templateContent.cloneNode(true));
    targetElement.insertAdjacentHTML('beforeend', div.innerHTML);
  })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants