Update html table without flicker

320 views
Skip to first unread message

Edward Elliott

unread,
Dec 30, 2020, 2:27:52 PM12/30/20
to brython
Hi, I'm having trouble updating an html table.  It causes the page to flicker and is very unpleasant.  Hope someone knows how to avoid this.

I have a table with data rows.  Every half second I update a few rows if their data has changed.

My first attempt was to simply rebuild all the cells in the row (naive I know, but it's a starting point):
row = get_row (rowid)    # returns a <tr> element
row.replaceChildren ()   # clears all child elements
for data in source :
    cell = make_node ('td', data)
    row.append (cell)

However this makes a noticeable flicker.  Not fast enough to see a row disappear, but the entire table continually dances.  Very hard on the eyes.

Ok, I thought.  I'll make all the new cells first, then assign them as the row's children.  So I tried this:
container = [ ]
for data in source :
    cell = make_node ('td', data)
    container.append (cell)
row.replaceChildren (*container)
Which works because replaceChildren takes each new child as an arg (instead of a list for inscrutable reasons).

Doesn't work, still have the flicker.  What else I tried:
  • assigning to .children directly doesn't work (read only in brython)
  • making a new row (<tr> node) as container doesn't work.  I tried  replacing the original row with the new row using old_row.parent.replaceChild (new_row, old_row).  So one DOM change for the entire row, instead of X replacements for each child cell.  Doesn't matter.  Still get the same flicker.
What's the solution for updating a table in the DOM?  Am I cursed with editing each existing cell one at a time?  Simple things like this shouldn't be so ridiculously complicated.  Web development is a jenga tower with half the pieces already gone. 🙄

Thanks if anyone has advice.  Happy holidays!
Edward

Pierre Quentel

unread,
Dec 31, 2020, 2:34:29 AM12/31/20
to brython
This code using replaceChild updates a table every half second and doesn't make any flicker:

import random

from browser import document, html, timer

td_style = {"font-family": "Consolas"}

t = html.TABLE(html.TR(html.TD(j, style=td_style) for j in range(i, i + 20))
                      for i in range(10, 30))

document <= t

def change_row(*args):
    row_num = random.randint(0, 19)
    row = t.children[row_num]
    new_row = html.TR(html.TD(int(td.text) + 1, style=td_style)
                      for td in row.children)
    t.replaceChild(new_row, row)

timer.set_interval(change_row, 500)

Edward Elliott

unread,
Dec 31, 2020, 7:57:50 AM12/31/20
to bry...@googlegroups.com
Thanks Pierre.  Always nice to see someone else's approach to the problem.  I didn't know about timer, good to have that option. :)

Thanks to your example I figured out my problem.  The update works fine with text.  However my data has a small icon image in each row.  It's just 32x32, but it's a "data:image/png" string.  Apparently inserting it in the table causes a noticeable delay, as chrome has to reprocess the image data from scratch (prob not smart enough to cache image-data strings like it would <img> urls).

Here's my solution.  Because the data comes from an external source, I can't replace image-strings with img urls.  So instead of inserting each image in the table directly with <img src=image-string>, I created a css class for each image, like "img_123".  Then I made a css rule with the image-string like this:

.img_123  {
        background-size: cover !important;
        background: url(img_string); 
}

Now create a new stylesheet element and stick it in the document head.  Write all the css rules to the new stylesheet.  For each data row, add the correct image class to the cell <td class="icon">.  
Use a dict to track which images have been seen and add new ones to the stylesheet.

Voila, no more flicker.  The browser converts each image-string from the css into image data and caches it.  When updating a row, the browser just reads the img_123 class and displays the correct cached image.

The background-size rule makes sure that the image fills the entire cell.  In my static css I have a rule that sets width and height on td.icon so the icon cell is always the same size.

Hopefully this explanation helps others in a similar predicament.  It's not a brython issue, but it does illustrate a class of problems with updating the DOM.

Happy (almost) 2021 everyone!
Edward


--
You received this message because you are subscribed to a topic in the Google Groups "brython" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/brython/89V8JIzb_7c/unsubscribe.
To unsubscribe from this group and all its topics, send an email to brython+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/brython/d80e01f4-4ecc-454a-aa72-4b5b0306aa08n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages