solefald
solefald

Reputation: 1799

Knockout JS: Stop divs binded to visible: from flashing on screen

I am working on a single page applications that has a bunch of hidden divs, binded (or is it bound?) to KnockoutJS with visible:. When page loads, they all momentarily flash on screen. I have tried moving my JS into the <head></head>, but that had no effect, so loading JS at the bottom of the page is not what's causing it.

Unfortunately, visible: binding does not propagate to CSS display attribute, so I can not use display: none; on page load, or visible: will not work at all. Unless... I load the page with display: none; and then change it the very first time I show the div to a user.

But is there a more elegant way to achieve this?

Upvotes: 25

Views: 6646

Answers (3)

blues_driven
blues_driven

Reputation: 331

I ended up writing a custom binding to use instead of the default visible.

function isHidden(el) {
    var style;

    style = window.getComputedStyle(el);

    return (style.display === 'none')
}

ko.bindingHandlers['cssVisible'] = {
    'update': function (element, valueAccessor) {
        var value,
            isCurrentlyVisible;

        value = ko.utils.unwrapObservable(valueAccessor());
        isCurrentlyVisible = !isHidden(element);

        if (value && !isCurrentlyVisible) {
            ko.utils.toggleDomNodeCssClass(element, 'ko-visible', true);
        }
        else if ((!value) && isCurrentlyVisible) {
            ko.utils.toggleDomNodeCssClass(element, 'ko-visible', false);
        }
    }
}

Then some CSS to handle visibility

[data-bind*="cssVisible"]:not(.ko-visible) {
    display: none;
}

Usage is the same as the visible binding

<div data-bind="cssVisible: true"></div>

Upvotes: 0

Milaan
Milaan

Reputation: 108

I solved this by putting my "flashy" content in a script template and use ko's virtual elements to load the template when the variable is set by another virtual element.

For example:

<!-- ko if: myVariable -->
<!-- ko template: { name: 'myTemplate' } --><!-- /ko -->
<script type="text/html" id="myTemplate">
    <ul data-bind="foreach: blah...">
        <li></li>
    </ul>
</script>
<!-- /ko -->

So when myVariable is set, the content of the script container will be put in place of the template virtual element. With this method you dont see any flashing content :-)

Upvotes: 5

Tuan
Tuan

Reputation: 5412

Wth KnockoutJS, I work around this problem by defining a CSS class called hidden with display:none, then I'll add this class and binding to the flashing container:

class="hidden" data-bind="css: { hidden: false }"

Upvotes: 57

Related Questions