I have an issue with an <img> tag getting re-rendered, and I can’t figure out how to stop this.
So any help would be greatly appreciated.
Issue description.
Steps from the browser’s perspective:
<img> tag gets rendered.
Then the <img> is removed from the DOM, causing a layout change.
Then the <img> is re-rendered, together with an additional get request for the already loaded image.
This issue causes a distracting jittery effect on the web page.
Context for debugging: Liveview re-renders views, by design.
It is expected that pages render twice with Liveview, first for the static view, then for the “stateful” view after socket connection.
However I am not able to understand why, in this case: an already rendered element is re-rendered (with an additional get request for the already fetched resource).
Code example for reference.
Test image component:
attr :src, :string
def test_img(assigns) do
~H"""
<img src={@src} />
"""
end
(Which is called with <.test_img src="/images/test.jpg" />)
Link to website where the issue is occurring – issue is more prominent when refreshing the page on mobile: anisweetslice.com
I’m on mobile so can’t check developer tools. To me this looks like a progressive compression format.
If you directly open the image on mobile I get a link to an SVG that doesn’t progress in the clear picture.
In the production website the browser steps for this are issue:
The low quality placeholder .svg gets loaded.
The actual image .jpg gets loaded.
Then both the placeholder & actual image are removed from the DOM.
Then, the low quality placeholder gets re-rendered.
Then, the actual image gets re-rendered.
So there are 4 get requests in total – two for the placeholder & two for the actual image.
You can see this when you scroll in the image gallery. When images are loaded, there is a flicker effect.
The issue occurs for any <img> tag on the website.
In development for example, I added that example test_img component, and the issue occurred in the simplified form:
<img> gets rendered with test.jpg.
<img> gets removed from the DOM.
<img> gets re-rendered, with an additional get request for test.jpg.
Thanks also for suggesting that I minimally reproduce the problem. For some reason my previous minimal example with “test_img” didn’t work properly.
The issue was that the <img> tag was being generated using a random id.
So when LiveView re-renders the view: a new random id is set for the <img> tag (which gets treated as a new DOM element).