Как уменьшить количество DOM при перерисовке таблицы?

У меня есть таблица с возможностью изменения размера. На самом деле это не элемент <table>. Я использую кластер из элементов <div>. Вкратце, моя функция изменения размера - это вложенные петли for. Это примерно так:

function resizeTable (computedCellWidth) {
    for (var r = 0; r < rows.length; r++) {
        for (var c = 0; c < rows[r].cells.length; c++) {
            rows[r].cells[c].domNode.style.width = computedCellWidth + 'px';
        }
    }
}

Как я понимаю, каждый rows[r].cells[c].domNode.style.width = computedCellWidth + 'px'; вызывает полное перерисование документа. Таким образом, если таблица достаточно большая, то изменение размера таблицы крайне замедляет работу браузера.

Я попытался использовать дросселирование (100 мс), чтобы уменьшить количество событий изменения размера, но если таблица достаточно большая, то даже одно изменение размера занимает слишком много времени.

Можно ли сначала назначить все свойства width и только после этого запуска выполнить однократное перерисовку?

Ответ 1

Дайте ширину в процентах, несмотря на пиксели, чтобы получить гибкие макеты. Если проценты нельзя использовать по какой-либо причине, тогда DocumentFragment - лучший выбор в этом случае. Он предназначен для временного хранения структур DOM. Он не будет называть механизм рендеринга браузера, пока измененный фрагмент не будет вставлен обратно в документ.

Вот как часть документа будет фрагментирована.

var elem = document.getElementById("resizableTable");//assumes resizableTable is the id of the element has to modify
var frag = document.createDocumentFragment(); //method creates an imaginary node object with all properties.
frag.appendChild(elem);

Теперь итератируйте фрагмент, чтобы внести изменения, а затем вставьте обратно фрагмент. Это не будет перерисовывать документ каждый раз, когда вы что-то измените в фрагменте.

PS: Я знаю, я опоздал. Но, думал, что это может помочь кому-то, кто должен сделать то же самое.

Ответ 2

Из фрагмента кода, который вы поделили, я подозреваю, что вы избегаете использования jquery... но, к счастью, это именно то, что делает jquery; DOM-манипуляция.

Я не знаю, как запускается событие изменения размера вашей "таблицы"... (изменение размера контейнера мыши?? изменение размера?);

Если бы я был вами, я бы справился с этим.

  • Пометьте "таблицу" (outermost div) с именем класса, например. .draggable-table. Также пометьте "строки" как .row или .tr, а ячейки - как .cell или .td;
  • Введите следующий обработчик событий:

    function resizeTable(computedCellWidth){
        $('.draggable-table .td').css('width', computedCellWidth+'px');
    }
    

Я не смотрел на внутренности того, как jquery реализует его, но я уверен, что это будет эффективно, что будет намного быстрее, чем ваш вложенный цикл.

Если вы вообще не используете jquery, вам действительно нужно, если вы много работаете с DOM. Если вас беспокоит размер, который он добавляет к вашему проекту, используйте cdn, и вы получаете выгоду от того, что он уже может быть кэширован в браузерах ваших пользователей с первого раза, когда они посещают ваш сайт.

Ответ 3

После другого взгляда на мой ответ, я не думаю, что это будет намного лучше для производительности. Я нашел функцию на другом ответе, которая могла бы решить проблему производительности, заменив правила класса.

  • добавьте тег <style> в разделе <head> документа html с одним классом: .dynamic-width {width: /*initial width here*/} и добавьте этот класс во все ваши "ячейки таблицы";
  • Создайте функцию ниже (из @Oriol answer):

    function setStyle(cssText) {
        var sheet = document.createElement('style');
        sheet.type = 'text/css';
        /* Optional */ window.customSheet = sheet;
       (document.head || document.getElementsByTagName('head')[0]).appendChild(sheet);
       return (setStyle = function(cssText, node) {
            if(!node || node.parentNode !== sheet)
                return sheet.appendChild(document.createTextNode(cssText));
            node.nodeValue = cssText;
            return node;
       })(cssText);
    };
    

Затем вызовите его в обработчике событий resize следующим образом:

    function resizeTable(computedCellWidth){
        setStyle('.dynamic-width{ width: '+ computedCellWidth+'px; }');
    }

Ожидается, что это будет быстрее, потому что изменение правил класса заставляет писать в DOM O (1) вместо изменения правила для каждой ячейки O (n). Это верно, если скорость визуального применения правила css незначительна.

Ответ 4

Я считаю, что вы можете использовать requestAnimationFrame для пакетного обновления DOM и предотвращения разметки макета. Я слышал об этой идее из различных виртуальных реализаций DOM.

например. в вашем случае может работать что-то подобное.

function resizeTableCell (domNode, computedCellWidth) {
    requestAnimationFrame(function() {
        domNode.style.height = computedCellWidth + 'px';
    });
}

Здесь написано здесь.

Ответ 5

Есть несколько способов, по которым вы можете это сделать. У одного есть событие, которое запускается, когда пользователь перестает изменять размер своей страницы дольше, чем 150 мс. Таким образом, он срабатывает только один раз в конце.

Преобразование анимаций, таких как масштаб, не вызывает перерисовки, и если вы в порядке с уменьшением текста, что будет дешевле. Вы можете масштабировать всю таблицу.

Я не знаю, каковы ваши требования, но лучший подход - позволить css естественно обрабатывать изменение размера, поскольку оно быстрее на нем. Вам нужно будет заранее указать свои столбцы в процентах.