petelacey

petelacey

Here is a dummy LV that replicates some strange behavior I’m seeing in my production application. As you can see, the app renders a mildly complicated HTML paragraph for every number from 0 to 1,000. It then allows you to filter that list to numbers evenly divisible by a user-entered value. That’s it.

defmodule ReadyroomWeb.Speriment do
  use Phoenix.LiveView

  def render(assigns) do
    use Phoenix.HTML

    ~L"""
      <form id="filter-form" phx-change="filter">
        <%= text_input(:filter, :value, value: @filter_value, class: "form-control", placeholder: "filter by", phx_debounce: "100") %>
      </form>

    <%= for i <- @renderable do %>
      <p>The number <b><%= i %></b> is <i>evenly</i> divisible by the <strong>number</strong> <code><%= @filter_value %></code>.<p>Here's a nested paragraph</p><div>This is also nested</div><p></p></p>
    <% end %>
    """
  end

  def mount(_params, _session, socket) do
    data = 0..1000
    {:ok, assign(socket, filter_value: 1, data: data, renderable: data)}
  end

  def handle_event("filter", %{"filter" => %{"value" => ""}}, socket) do
    handle_event("filter", %{"filter" => %{"value" => "1"}}, socket)
  end

  def handle_event("filter", %{"filter" => %{"value" => filter}}, socket) do
    renderable = Enum.filter(socket.assigns.data, &(rem(&1, String.to_integer(filter)) == 0))
    {:noreply, assign(socket, renderable: renderable, filter_value: filter)}
  end
end

If you run this, it initially renders very quickly. As you type filters, e.g., 10, 100, 1000, 10000, it continues to render quickly. But as you back over the digits in the filter, making it more and more inclusive, (1000, 100, 10, 1) it renders slower and slower. If you simply delete the filter, changing it from 10000 to 1 or blank, it takes (on my machine) nearly seven seconds to render.

Even more unusual, it slows down appreciably every time I stick a junk tag in the output. That’s what those noisy HTML tags are all about. If, for instance, I add an empty div tag to the beginning of the paragraph, the 7 seconds increases to 22 seconds!

I suspect I am missing something fundamental above LV and Morphdom. Any ideas?

Showing Posts 1 to 10

cmo

cmo

Not sure if it is important but you cannot nest <p> tags, put divs inside p tags etc.

petelacey

petelacey OP

While I don’t have

tags in production, changing them for this example made a marked improvement. While it’s still slow-ish when the filter is deleted, it’s no longer pathologically slow. That is, 7 seconds has fallen to 1. Maybe, I have violated another HTML constraint and that’s why I’m seeing 30 second redraw times in prod. I’ll let everyone know if I find anything. And thanks!

BartOtten

BartOtten

Is it server side or client side delay? If the server sends a diff within milliseconds, morphdom
/js is having issues. If the server responds slow, we can start looking at LiveView/the elixir part

petelacey

petelacey OP

Sorry, I meant to mention that. This is purely client side. Using the dev tools I can see the response come back nearly immediately, then the thread visibly freezes while it updates the DOM.

While the above code does reproduce the problem, keep in mind that making the HTML syntactically correct improves things dramatically. I reviewed my actual code, and don’t see any obvious HTML syntax problems. I will try and fine tune the example and see if I can get it to go pathological even with correct syntax.

chrismccord

chrismccord

Creator of Phoenix

Your for comprehension is going to render 1000 rows on the server, then send that over the wire, to re-render all 1000’s of DOM elements. The delay you see is probably 99% client side, but in general this is not the way to go about rendering such a large collection. Look into phx-update="append" for handling this kind of operation efficiently :slight_smile:

BartOtten

BartOtten

To add: Browsers in general do not like thousands of elements. If your lists’ root element has 10 children you are looking at 10.000 elements. Any browser will lag when you feed it such numbers without static positions and fixed sizes.

As relative position forces the browser to first compute the height of list element 1 (say 20px), then place the second element below (start 20px) and compute it’s height (again 20px) go on to the third and place it at 20+20=40 px and so on and on. Now this is an example with fixed height of 20px. Imagine if it is variable and it needs to include the calculations of child elements and you start to see the number of calculations which must be performed.

Browsers are highly optimized for this task but only when you match the specs. Once you nests a Paragraph in a Paragraph that optimization breaks and will cause even more render delay.

The solutions? None fits all but here are some candidates:
1.) ensure you keep the amount of elements to the minimum
2.) ensure only those within the visual space are rendered. This can be done with pagination and/or virtual lists. (Like in React with react-virtualized)
3. Use fixed sizes for elements
4. Use static positioning

petelacey

petelacey OP

I appreciate the feedback, and I will adjust my approach accordingly. However, the production issue that prompted this does not actually have 1,000 “entities,” it has just 200. To paint a picture, it’s a filterable list of requests for documents. And while I’ve used phx-update="append" for the simple chat interface that’s also in the product, I’m not sure how I would use it for a filter, that is, for a list the shrinks and grows.

Now, each “request” is made up of a number of elements, divs, and checkboxes, and whatnot, but not excessively so. In fact, the page initially renders very quickly, which leads me to believe the browser can handle it reasonably well. Even as the user types in the filter field and the list shrinks, it continues to render quickly.

It’s when the filter is cleared that the page becomes unresponsive for 20-60 seconds as it is rebuilt. It’s that excessive delay, combined with the quick initial render, that leads me to suspect morphdom/liveview and not page complexity or browser rendering challenges.

That said, I’m more than prepared to hear that I’m doing something stupid or complex, I just don’t know what it is right now. Filtering a list of 200 entities, should not be that taxing.

derek-zhou

derek-zhou

Can you check your browser console to see how much data it is receiving from the web socket? Are the number of messages and the size reasonable close to what you were assuming?

It still should not b that bad. I sent a very large nested list of 650 items that total about 500KB, my browser struggled for maybe 1~2 seconds. In both Firefox and Safari. On a phone, it only feels slightly slower.

petelacey

petelacey OP

Okay, I have deployed an accessible version of this issue here: https://staging.readyroom.net/speriment

This version reasonably faithfully replicates my production issue in that the (dummy) HTML is structured the same as in prod and there are 200 rows (which is not an unreasonable page size). As you can see, it renders quickly at first.

There’s a filter field on top with a 500ms debounce setting. If you type a number in the field, it will filter the list down to just those rows that have an “ID” evenly divisible by that number, as before. Slowly typing 1 then 12 then 123 then 1234 will work as you expect. However, if you back over the filter (123, 12, 1), it will render progressively slowly. But simply deleting the filter is the most dramatic. In that case the 23K response returns in approximately 500ms, but the page doesn’t finish rendering until some 20-30 seconds later!

While I do appreciate the work browsers go through to render a page, in this case it seems clear that the issue is not due to page size or complexity, but it’s somewhere in LV/morphdom.

I do have a workaround, however. If I wrap that list in an element with an ID tied to the filter assign, then LV/morphdom doesn’t merge each node, but just chunks in a whole new list, which renders quickly. IOW, if I change the wrapping div to something like the following, it all becomes snappy again: <div id="released-tasks-<%= @filter_value %>"

Hopefully, this will help people in tracking things down.

cmo

cmo

Can we see the code for it?

Do you put IDs on the rows?

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews