<image> response for <svg> is transformed into <img>
I have an svg with an <image> tag like this:
<html>
<head>
<script src="https://unpkg.com/[email protected]" integrity="sha384-QWGpdj554B4ETpJJC9z+ZHJcA/i59TyjxEPXiiUgN2WmTyV5OEZWCD6gQhgkdpB/" crossorigin="anonymous"></script>
</head>
<body>
<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400'>
<g id='container'>
<image x="0" y="0" width="200" height="200" href="https://www.w3.org/html/logo/img/mark-word-icon.png" hx-post='/doSomething' hx-target='#container'/>
</g>
</svg>
</body>
</html>
The response of POST /doSomething is another <image>:
<image x="0" y="0" width="200" height="200" href="https://www.w3.org/html/logo/img/one-color.png" hx-post='/doSomething' hx-target='#container'/>
However, the result is that inside the <g id='container'>, an <img> element is placed, instead of <image>:
Result:
<g id="container">
<img x="0" y="0" width="200" height="200" href="https://www.w3.org/html/logo/img/one-color.png" hx-post="/doSomething" hx-target="#container">
</g>
Expected:
<g id="container">
<image x="0" y="0" width="200" height="200" href="https://www.w3.org/html/logo/img/one-color.png" hx-post="/doSomething" hx-target="#container">
</g>
Minimal server to showcase the behaviour:
package main
import (
"fmt"
"log"
"net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
fmt.Fprintf(w, `
<html>
<head>
<script src="https://unpkg.com/[email protected]" integrity="sha384-QWGpdj554B4ETpJJC9z+ZHJcA/i59TyjxEPXiiUgN2WmTyV5OEZWCD6gQhgkdpB/" crossorigin="anonymous"></script>
</head>
<body>
<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400'>
<g id='container'>
<image x="0" y="0" width="200" height="200" href="https://www.w3.org/html/logo/img/mark-word-icon.png" hx-post='/doSomething' hx-target='#container'/>
</g>
</svg>
</body>
</html>`)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func doSomethingHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
fmt.Fprintf(w, `
<image x="0" y="0" width="200" height="200" href="https://www.w3.org/html/logo/img/one-color.png" hx-post='/doSomething' hx-target='#container'/>`)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/doSomething", doSomethingHandler)
fmt.Println("Server is running at http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Hey, I would say that's because htmx parses HTML and expects text/html responses.
You can see the behavior or the native DOMParser with various responses on this JSFiddle
Notice how when parsing as text/hml, <image> is automatically converted to the HTML valid img tag (which doesn't have an image tag), but when parsing as image/svg+xml (so XML, not HTML), the <image> tag is correctly preserved.
So, you'll either want to wrap your SVG tags in a SVG element here, given that htmx expects a HTML response, or you could make an extension to support SVG tags in this case.
Hope this helps!
Thanks for your response!
Wrapping the <image> inside a <svg> tag would beat the purpose of htmx, where I want to only update a part of the DOM, not the whole tag (all elements of SVG are considered part of the DOM).
or you could make an extension to support SVG tags in this case.
Can you expand on this? I don't know what you mean
I fiddled with the example code above, turns out it's a great usecase for hx-select.
You can wrap your <image> inside a svg one, so that the DOM parser correctly parses it, then use hx-select to only extract what you want, i.e. the children of that <svg> tag, without the <svg> itself
See this JSFiddle for a demo.
The relevant part is the attribute hx-select="svg *" added to the image making a request on click.
Hope this helps!
That works! Thanks a lot!
I'm still confused though about the transformation. For instance, if instead of <image> I use <something href="aa"/> (which does not conform HTML DOM), the result is what I would expect (almost):
<g id='container'>
<something href="aa"></something>
</g>
In the <image> case, it "understands" it's referring to an image, and converting it into <img>. I'm not sure this should be the correct behaviour.
Anyway, there's an easy workaround I can work with, thanks a lot!
I'm still confused though about the transformation
Tbh I discovered this behavior when investigating the issue, I didn't know that at all either and I'm also a bit confused as to why they would apply some "autocorrect" for this tag but not others (for example <bold>, <italic> won't get turned into the correct tag, they'll just stay as is, i.e. incorrect html tags in this case)
I'm not sure this should be the correct behaviour.
Imho we shouldn't do anything about it at the htmx level anyway, since that's a behavior of the native API. Might be worth it to add a tip for this specific usecase in the docs though?
@elpadrinoIV @Telroshan HTML actually used to have an <image> tag in its very early days, as noted by the Obsolete and deprecated elements section of the MDN HTML elements reference. <image> is described as follows:
An ancient and poorly supported precursor to the
<img>element. It should not be used.
I couldn't find it via a quick glance at the source code for WebKit's DOMParser, but it's possible that <image> specifically is upgraded to its supported counterpart, as other obsolete elements with clear equivalents—like <strike> becoming <s>—are not converted.
@heyainsleymae - I think <image> within SVG elements is different and not outdated: it is to include images into svg.
I guess a possible solution is to swap the whole SVG element and not only the image element which would be interpreted as outdated HTML by the browser.
Thanks for that information @heyainsleymae, makes sense now! It was retro-compatibility all along 😁
@heyainsleymae - I think
<image>withinSVGelements is different and not outdated: it is to include images into svg.
@dalito Yes, you're correct, but the question was why <image> was transformed into <img> outside of <svg>.
I guess a possible solution is to swap the whole SVG element and not only the image element which would be interpreted as outdated HTML by the browser.
You got it! Sending down the whole SVG and then using hx-select to grab its children was decided above.