0

I am using HTMX with Django to render a page which is basically an SVG with a lot of sticky notes () that you can move around on the page, like you might use for brainstorming. When you edit the text, htmx POSTs that back to a Django view which returns the updated content.

This is the Django template for a single form:

<g id="g1-{{form.instance.pk}}"
   hx-post="{% url 'architech:updatetech' form.instance.map.pk form.instance.pk %}"
   hx-swap="outerHTML"
   hx-trigger="focusout"
   hx-include="#fo-{{form.instance.pk}}">
<g id="g2-{{form.instance.pk}}" transform="{{form.instance.presentation|safe|default:"matrix(1,0,0,1,50,50)"}}" class="ui drags" draggable="true">
<foreignObject id="fo-{{form.instance.pk}}" height="203" width="319">
<form class="card"
      id="form-{{form.instance.pk}}"
      draggable="true">
<div id="header-{{form.instance.pk}}" class="card-header" draggable="true">
    {{ form.name.errors}}
    {{ form.name }}
    {{ form.presentation }}
    <input type="button" value="x" hx-post="{% url 'architech:deletetech' form.instance.map.pk form.instance.pk %}"
           hx-swap="outerHTML" hx-target="#g-{{form.instance.pk}}" hx-trigger="click">

</div>
<div id="body-{{form.instance.pk}}" class="card-body" draggable="true">
    {{ form.notes.errors}}
    {{ form.notes }}
</div>
</form>
</foreignObject>
</g>
</g>

I've been using hx-swap="outerHTML" (and more recently "morphdom") to re-render and replace this block of code. When its reloaded, the same SVG/HTML doesn't get rendered correctly or respond to javascript.

The key element is the second group, with id beginning g2.

When loaded with the whole page, height and width are honored, and attributes like 'transform' are accessible with element.transform:

# console interaction on initial state using 'save to a global variable'
>temp1
  <g id="g2-22a443de-35c4-4bcd-8b48-183961c9d363" transform="matrix(1, 0, 0, 1, 1870, 1080)" 
  class="ui drags" draggable="true">
>temp1.transform
  SVGAnimatedTransformList {baseVal: SVGTransformList, animVal: SVGTransformList}

When the same SVG/HTML is loaded through htmx with hx-swap="outerHTML" or "morphdom", I can see in the inspector, but it is rendered at 0 x 0 size, and element.transform is null.

# console interaction on reloaded state
>temp1
  <g id="g2-22a443de-35c4-4bcd-8b48-183961c9d363" transform="matrix(1, 0, 0, 1, 1870, 1080)" 
  class="ui drags" draggable="true">
>temp1.transform
  undefined

I'm mostly using Chrome and Firefox on Win 11 to investigate this, if that matters.

I would love some pointers - very likely that I'm misunderstanding something basic about the sequence of events in rendering in the browser or making Namespace explicit or something. (I thought I was having this problem [setAttribute not working] for a while and am still a bit unsure of the mechanics that cause this. )1setAttribute is not working in JavaScript

Atcrank
  • 439
  • 3
  • 11
  • I think this question is the jQuery equivalent but from 11 years ago. https://stackoverflow.com/questions/3642035/jquerys-append-not-working-with-svg-element – Atcrank Apr 28 '22 at 00:58
  • The second `temp1` element looks strange. What is this `"10" id="id_notes">[[Children]]` string? – Dauros Apr 28 '22 at 19:12
  • Sorry, that was just me unsuccessfully editing out all the child elements for brevity. – Atcrank Apr 29 '22 at 00:20

1 Answers1

0

[See edit. leaving this for historical reasons, because I learned something going through this iteration, but to actually get things working I went back to the htmx docs on morphdom, and followed them carefully.]

The link in my comment held a pathway to solving this, but although it works, its not pretty and there may be a bunch of reasons to avoid doing this. Anyway, the jQuery version was:

$("#content").html($("#content").html());

I dug into the jQuery source with the help of jQuery Source Viewer and found that the part that does all the work is basically getting the changed innerHTML of a region and setting the innerHTML, which triggers ??????? (rendering, registering in DOM, whatnot??), solving my problems. I added the following one line to the htmx:afterSettle event listener. It updates the whole inner HTML, so all the elements lose any event listeners or special properties you created for them. This works for my example [EDIT: this worked only the first and second time, then htmx would stop working]: grab a div that contains an SVG document that contains svg foreignObject elements that contain HTML.

document.getElementById("mapdiv-{{map.pk}}").innerHTML = document.getElementById("mapdiv-{{map.pk}}").innerHTML;

EDIT: Although this allowed me to apply htmx-sourced elements, for some reason it seemed to break htmx after being applied a couple of times.

Currently I'm having much better results with the htmx-recommended htmx extension that enables swapping via the "morphdom" library. I had tried this early on but blamed it for a failure that I now think was actually on my code & templates.

Atcrank
  • 439
  • 3
  • 11