changeset 9f6f8d5ed852 in sao:default
details: https://hg.tryton.org/sao?cmd=changeset;node=9f6f8d5ed852
description:
        Limit browser reflow when rendering tree rows

        We add at once the tbody or the new rows only after being rendered to 
generate
        a single browser reflow.
        The same is done for the children rows when a row is expanded.

        issue9658
        review306521003
diffstat:

 src/view/tree.js |  111 +++++++++++++++++++++++++++---------------------------
 1 files changed, 55 insertions(+), 56 deletions(-)

diffs (185 lines):

diff -r 95d9965fbd89 -r 9f6f8d5ed852 src/view/tree.js
--- a/src/view/tree.js  Sun Oct 11 16:14:09 2020 +0200
+++ b/src/view/tree.js  Sun Oct 11 16:26:25 2020 +0200
@@ -558,6 +558,7 @@
         display: function(selected, expanded) {
             set_treeview_height(this.treeview);
 
+            var tbody = this.tbody;
             var current_record = this.record;
             if (jQuery.isEmptyObject(selected)) {
                 selected = this.get_selected_paths();
@@ -740,13 +741,47 @@
             }
 
             this.update_arrow();
-            return this.redraw(selected, expanded).done(
+            return this.redraw(selected, expanded).then(function() {
+                var tbody = this.table.children('tbody');
+                if (!tbody.length) {
+                    this.table.append(this.tbody);
+                } else if (tbody !== this.tbody) {
+                    tbody.replaceWith(this.tbody);
+                }
+                this.tbody.append(this.rows.filter(function(row) {
+                    return !row.el.parent().length;
+                }).map(function(row) {
+                    return row.el;
+                }));
+                if (this.display_size < this.group.length) {
+                    var more_row = jQuery('<tr/>', {
+                        'class': 'more-row',
+                    });
+                    var more_cell = jQuery('<td/>');
+                    var more_button = jQuery('<button/>', {
+                        'class': 'btn btn-default btn-block',
+                        'type': 'button'
+                    }).text(Sao.i18n.gettext('More')
+                    ).click(function() {
+                        this.display_size += Sao.config.display_size;
+                        this.display();
+                    }.bind(this));
+                    more_cell.append(more_button);
+                    more_row.append(more_cell);
+                    this.tbody.append(more_row);
+                    if (moreObserver) {
+                        moreObserver.observe(more_button[0]);
+                    }
+                }
+            }.bind(this)).done(
                 Sao.common.debounce(this.update_sum.bind(this), 250));
         },
         construct: function(extend) {
-            var tbody = this.tbody;
             if (!extend) {
                 this.rows = [];
+                // The new tbody is added to the DOM
+                // after the rows have been rendered
+                // to minimize browser reflow
                 this.tbody = jQuery('<tbody/>');
                 if (this.draggable) {
                     this._add_drag_n_drop();
@@ -755,7 +790,8 @@
             } else {
                 this.tbody.find('tr.more-row').remove();
             }
-            var start = this.rows.length;
+            // The rows are added to tbody after being rendered
+            // to minimize browser reflow
             var add_row = function(record, pos, group) {
                 var RowBuilder;
                 if (this.editable) {
@@ -763,36 +799,10 @@
                 } else {
                     RowBuilder = Sao.View.Tree.Row;
                 }
-                var tree_row = new RowBuilder(this, record, this.rows.length);
-                this.rows.push(tree_row);
-                tree_row.construct();
+                this.rows.push(new RowBuilder(this, record, this.rows.length));
             };
-            this.group.slice(start, this.display_size).forEach(
+            this.group.slice(this.rows.length, this.display_size).forEach(
                     add_row.bind(this));
-            if (!extend) {
-                tbody.replaceWith(this.tbody);
-            }
-
-            if (this.display_size < this.group.length) {
-                var more_row = jQuery('<tr/>', {
-                    'class': 'more-row',
-                });
-                var more_cell = jQuery('<td/>');
-                var more_button = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-block',
-                    'type': 'button'
-                }).text(Sao.i18n.gettext('More')
-                    ).click(function() {
-                    this.display_size += Sao.config.display_size;
-                    this.display();
-                }.bind(this));
-                more_cell.append(more_button);
-                more_row.append(more_cell);
-                this.tbody.append(more_row);
-                if (moreObserver) {
-                    moreObserver.observe(more_button[0]);
-                }
-            }
         },
         redraw: function(selected, expanded) {
             return redraw_async(this.rows, selected, expanded);
@@ -1176,6 +1186,7 @@
             this._drawed_record = null;
             this.el = jQuery('<tr/>');
             this.el.on('click', this.select_row.bind(this));
+            this._construct();
         },
         get group_position() {
             if (this._group_position === null) {
@@ -1206,26 +1217,14 @@
         is_expanded: function() {
             return this.tree.expanded.has(this);
         },
-        get_last_child: function() {
-            if (!this.children_field || !this.is_expanded() ||
-                    jQuery.isEmptyObject(this.rows)) {
-                return this;
-            }
-            return this.rows[this.rows.length - 1].get_last_child();
-        },
         get_id_path: function() {
             if (!this.parent_) {
                 return [this.record.id];
             }
             return this.parent_.get_id_path().concat([this.record.id]);
         },
-        construct: function() {
-            var el_node = this.el[0];
-            while (el_node.firstChild) {
-                el_node.removeChild(el_node.firstChild);
-            }
-
-            var td, drag_img;
+        _construct: function() {
+            var td;
             this.tree.el.uniqueId();
             if (this.tree.draggable) {
                 td = jQuery('<td/>', {
@@ -1321,12 +1320,6 @@
 
                 this.el.append(td);
             }
-            if (this.parent_) {
-                var last_child = this.parent_.get_last_child();
-                last_child.el.after(this.el);
-            } else {
-                this.tree.tbody.append(this.el);
-            }
         },
         _get_column_td: function(column_index, row) {
             row = row || this.el;
@@ -1501,13 +1494,19 @@
                     var children = this.record.field_get_client(
                         this.children_field);
                     children.forEach(function(record, pos, group) {
-                        var tree_row = new this.Class(
-                            this.tree, record, pos, this);
-                        tree_row.construct(selected, expanded);
-                        this.rows.push(tree_row);
+                        // The rows are added to the tbody after being rendered
+                        // to minimize browser reflow
+                        this.rows.push(new this.Class(
+                            this.tree, record, pos, this));
                     }.bind(this));
                 }
-                redraw_async(this.rows, selected, expanded);
+                redraw_async(this.rows, selected, expanded).then(function() {
+                    this.el.after(this.rows.filter(function(row) {
+                        return !row.el.parent().length;
+                    }).map(function(row) {
+                        return row.el;
+                    }));
+                }.bind(this));
             }.bind(this));
         },
         switch_row: function() {

Reply via email to