延迟 CSS 渲染,而 Javascript 可以正常工作

Delayed CSS rendering while Javascript does it's work

我有一个非常大且复杂的 jQuery 插件,为简单起见,我将避免在此处发布。我的问题很简单,我就把它简化成相关代码:

我有一个点击事件附加到一组按钮:

$("ul.tick-boxes button").click(function(event) {
    event.preventDefault();
    $("ul.product-grid").addClass("loading");
    $(this).toggleClass("active");
    $theElement.trigger("filterOptionsChanged");
});

如果您转到此 link,您可以在左侧边栏中看到这些操作:

http://mazer.com/collections/refrigerator?preview_theme_id=22019779

这是单击按钮时产生复选标记的 css:

ul.tick-boxes button.active .tick-box::after {
    content: "\e603";
    font-family: "custom-icons";
    color: #51425d;
    top: 2px;
    left: 2px;
    font-size: 0.75rem;
    position: absolute;
}

如果您的计算机和我的一样慢,那么当您单击这些过滤器选项时,"tick" "tick-box" 需要一秒钟左右的时间。如果您看不到它,请尝试取消选中它,这对我来说需要更长的时间。 "tick" 可见渲染的时间点始终与产品网格重建和渲染同时进行。我没有发布操作产品网格的代码,但你可以知道行 $theElement.trigger("filterOptionsChanged") 触发大量数组和对象处理以构建新产品列表的文档片段,并更新 DOM 最后。我知道这可能需要一秒钟,这不是我的问题。但我不明白的是为什么我的 "tick-boxes" 一直等到该事件的代码完成呈现之后。根据我的 css,我需要的只是按钮上的 class active,并且该代码在触发 "filterOptionsChanged" 事件的上方一行触发,因此它应该在触发之前触发任何产品网格都会发生变化。

现在。如果我在 chrome 中打开我的检查器,我实际上可以在产品网格更新之前看到活动的 classes 在单击时立即切换。但是,在我的 "filterOptionsChanged" 代码完成之前,添加刻度的 css 不会捕获元素上的活动 class。

我第一次尝试解决这个问题将在下面发布。我阅读了很多关于 "expensiveness" of css 伪选择器的内容。对于浏览器来说,这就像每次创建 ::after 元素时的 dom 操作。所以我然后写这个 css:

ul.tick-boxes button .tick-box::after {
    content: "\e603";
    font-family: "custom-icons";
    color: #51425d;
    top: 2px;
    left: 2px;
    font-size: 0.75rem;
    position: absolute;
    opacity: 0;
}

ul.tick-boxes button.active .tick-box::after {
    opacity: 1;
}

所以现在 ::after 元素总是从一开始就存在,任何渲染成本都是在一开始就支付的,所以我的想法是,现在当我点击这个按钮时,勾号已经存在了,我们是只是给它一个 1 的不透明度。没有修复延迟。

现在,我尝试完全删除 "filterOptionsChanged" 事件触发器。这使我的整个排序插件停止工作,但此时我不在乎,因为我想了解导致问题的原因。当我删除该事件触发器时,按钮和 css 呈现活泼。没有更多的问题。

我有一个模糊的想法,好吧,如果点击事件在没有该事件触发器的情况下也可以很活泼,我需要一种将两者分开的方法。首先添加 active class,然后触发 "filterOptionsChanged"。我想,好的,jQuery 延期。这是代码:

$("ul.tick-boxes button").click(function(event) {
    event.preventDefault();
    var showLoading = jQuery.Deferred();
    $("ul.product-grid").addClass("loading");
    $(this).toggleClass("active");
    showLoading.resolve();
    $.when(showLoading).done(function() {
        $theElement.trigger("filterOptionsChanged");
    });
});

所以 showLoading 是一个空白的延迟,然后我添加我的 classes 以显示复选框,然后我解决延迟。现在我说,当 showLoading 完成后,然后进行整个产品网格操作。不要同时做这些,javascript,等一个做完再做另一个。还是没有用。有什么想法吗?

根据 this,JavaScript 中的所有函数调用都会阻塞 UI 直到它们完成;我敢打赌,这包括启动点击事件的 original 函数调用。一个廉价的解决方案可能是用类似的东西替换触发器:

setTimeout(function() { $theElement.trigger("filterOptionsChanged"); }, 200);

这将有望延迟触发器足够长的时间,以便浏览器重新绘制 UI(您 could/should 在原始函数中添加一个小加载图标,然后在超时时将其删除)。您还可以查看 web workers,它们看起来几乎都是线程。

试试这个,

CSS:

ul.tick-boxes button.active .tick-box::after {
    content: "\e603";
    font-family: "custom-icons";
    color: #51425d;
    top: 2px;
    left: 2px;
    font-size: 0.75rem;
    position: absolute;
}

JS:

    $("ul.tick-boxes button").click(function(event) {
        event.preventDefault();
        $("ul.product-grid").addClass("loading");
        $(this).toggleClass("active").fadeIn(10, function () {
            $theElement.trigger("filterOptionsChanged");
        });
    });

使用回调延迟触发。看看这是否适合你。