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

3

25

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?

Mikimikihisa answered 8/10, 2012 at 5:25 Comment(1)
possible duplicate of What is the best way to hide the screen while knockout js bindings are being built?Vernavernacular
P
57

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 }"
Preconize answered 8/10, 2012 at 5:31 Comment(7)
What they said ^. Thanks for this brilliant answer!Jacinto
Nifty indeed. I made one small adjustment to this and replaced "hidden" with "ko-hidden", just for more context and to avoid stepping on an existing "hidden" class: class="ko-hidden" data-bind="css: { 'ko-hidden': false }"Dani
It is worth noting bootstrap 3 has a .hidden class. getbootstrap.com/css/#helper-classes-show-hideDaisie
I thought of doing this, but couldn't grasp why the visible binding didn't seem to do exactly this. As the question says, the visible binding doesn't seem to take precedent over the stylesheet rules, which is rather baffling. At least this works.Satiety
@Satiety Because the binding doesn't take effect until you call applyBindings.Preconize
@Preconize That's not what I'm talking about at all. In the case where you have applied a display: none via a CSS class, the visible binding won't work when the observable to which it is bound is true. When the visible binding's bound value evaluates to true, it simply sets element.style.display = '' which effectively removes style="display: none" from the element. Then, the underlying CSS class, which has display: none is applied - thus the element never shows. Therefore, the only work-arounds are to use style="display: none", instead of classes, or a special presentational class.Satiety
The only way that the visible binding could be made to work with css classes in the instance where you want the class to start as display: none, would be to have an optional binding argument that specifies what display style to use when the visible binding evaluates to true since it wouldn't be specified in the css class (it's specified as none there). Something like visible: isVisible(), visibleDisplayType: 'block'. Of course, this doesn't exist, so it would need to be added.Satiety
L
5

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 :-)

Lifeless answered 15/4, 2013 at 15:5 Comment(1)
this has the bonus of not putting display:none on everything (when someone else removes data-bind they may not remember to remove display:noneLyndseylyndsie
M
0

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>
Myranda answered 8/7, 2016 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.