Home > Software design >  knockout.js and better way to update css for hundreds of elements
knockout.js and better way to update css for hundreds of elements

Time:09-01

I currently have a scheduling grid with alot of cells that have this CSS update binding depending on a property value of the object. When a subscription of one of these elements changes, then it updates the CSS. Is there a better way to do this in terms of performance or strategy?

HTML

<div  data-bind="draggableCss: { linkedEventId: $data.LinkedEventId, disabled: $data.Disabled(), matchup: $data.Matchup(), invalid: $data.Invalid() && !$root.members.eventSchedule.disableValidation(), caution: $data.Caution() && !$root.members.eventSchedule.disableValidation(), available: $data.Available(), conflict: $data.Conflict() && !$root.members.eventSchedule.disableValidation(), current: $data.Current() }></div>

Binding

ko.bindingHandlers.draggableCss = {
    update: function (element, valueAccessor) {

        var values = valueAccessor();
      
        var addedClasses = '';
        var removedClasses = '';

        if (values.linkedEventId) {
            addedClasses  = " linked-event";
        }

        if (values.editable) {
            addedClasses  = " editable-game";
        }

        if (values.matchup) {
        
            if (values.matchup.Selected()) {
                removedClasses  = " occupied";
            }

            if (values.current) {
                updateElementColors(element, values.matchup.Color, 'current');
            }
            else if (values.invalid && !values.linkedEventId) {
                updateElementColors(element, values.matchup.Color, 'invalid');
            }
            else if (values.conflict && !values.linkedEventId) {
                updateElementColor(element, values.matchup.Color, false).parent().addClass('conflict');
            }
            else if (values.caution && !values.linkedEventId) {
                updateElementColor(element, values.matchup.Color, false).parent().addClass('caution');
            }
            else {
                updateElementColor(element, values.matchup.Color, false);
            }
            
            if (values.matchup.CrossGame) {
                addedClasses  = " cross-game";
            }

            if (values.matchup.Status === 0) {
                addedClasses  = " top-left-triangle";
            }
        }
        else if (values.available && !values.disabled && !values.linkedEventId) {
            updateElementColors(element, null, 'available');
        }
        else if (values.invalid && !values.disabled && !values.linkedEventId) {
            updateElementColors(element, null, 'invalid');
        }
        else if (values.caution && !values.disabled && !values.linkedEventId) {
           
            updateElementColors(element, null, 'caution');
        }
        else {
            removedClasses  = " invalid";
            removedClasses  = " current";
            removedClasses  = " caution";
            removedClasses  = " available";
        }

        $(element).toggleClass('ui-droppable-disabled', values.disabled);

        $(element).attr('title', values.disabled ? 'Double click to enable' : '');

        $(element).toggleClass('hide-game', (!values.matchup || !values.matchup.Selected()));
        $(element).toggleClass('show-game', (values.matchup && values.matchup.Selected()) ? true : false);

        $(element).toggleClass('empty', !values.matchup);

        if (removedClasses) {
            $(element).removeClass(removedClasses);
        }

        if (addedClasses) {
            $(element).addClass(addedClasses);
        }
    }
};

    function updateElementColors(element, borderColor, cssClass) {

        $(element)
            .removeClass('invalid')
            .removeClass('conflict')
            .removeClass('current')
            .removeClass('available')
            .addClass(cssClass);

        if (borderColor) {
            $(element).css('border-color', 'rgba('   borderColor.Red   ','   borderColor.Green   ','   borderColor.Blue   ','   borderColor.Alpha   ")");
        }
    }

    function updateElementColor(element, color, updateBorder) {

        var $element = $(element);

        $element
            .removeClass('invalid')
            .removeClass('available')
            .removeClass('current')
            .parent().removeClass('conflict');

        if (color) {

            $element
                .css('border-color', '')
                .css('background-color', 'rgba('   color.Red   ','   color.Green   ','   color.Blue   ','   color.Alpha   ')');

            if (updateBorder) {
                $element.css('border-color', 'rgba('   color.Red   ','   color.Green   ','   color.Blue   ','   color.Alpha   ')');
            }
        }

        return $element;
    }

CodePudding user response:

If you need to change a whole batch of styles at once, the most performant way I have found is to use optional style sheets in the link tags which include the various styles you might want to apply to your page.

<link rel='stylesheet' type='text/css' title='set1' href='blah.css' />
<link rel='stylesheet' type='text/css' title='set2' href='blah.css' />
<link rel='stylesheet' type='text/css' title='set3' href='blah.css' />

You can set the default set using

<meta http-equiv='Default-Style' id='swap' content='set1' />

Then in the program you can alter the default using something like

x=Math.random(3);
document.getElementById('swap')='set' x;

and the elements referenced in the optional style sheets will change instantly.

This will not effect the main style sheets declared like

<link rel='stylesheet' type='text/css' href='mainstyle.css' />

(without a title attribute)

except where the default style chosen over-rides the styles mentioned in the main style sheets.

The main style sheets must be mentioned first and the meta expressing the default must be given after the various optional style sheets have been declared.

This is not strictly a javascript solution but an HTML one albeit that you use javascript to switch the default style to the appropriate optional style sheet which is already loaded in the background but not instantiated until you toggle the default style.

Its way quicker than going through each element in javascript setting things one by one.

CodePudding user response:

I will offer minimal code optimization, perhaps it will improve performance. Try reducing the number of DOM changes, and calculations:

  1. removedClasses = " invalid current caution available";

  2. .removeClass('invalid conflict current available') or you can pass classes as an argument updateElementColors

  3. for colors, use template strings instead of concatenation.

Of course, these are general recommendations. They need a test. But I hope this helps too. As for the strategy, if there was a clear working example of how your table looks and works, I could offer something. First of all a working example would be useful for measuring performance

  • Related