减少jQuery DOM操作中的CPU使用率

时间:2012-03-18 05:45:11

标签: javascript performance live transitions

我可以对这段代码做些什么来提高它的CPU效率(它现在大约占我CPU的80%)?我昨天学习了javascript,所以可能只是因为我没经验。此代码控制相当大的切片阵列的转换。在鼠标悬停时,瓷砖翻转并在鼠标移开时翻转。将会有几个线程同时运行,我没有看到绕过那个线程的方法。 我正在使用这个脚本,因为我需要以webkit-transitions不支持的方式准确控制转换的作用。希望这些评论有足够的意义来阐明代码。该函数是实时的,因为在加载页面时,在javascript中创建了tile数组。之后,没有更多的瓷砖创建。

可在此处找到来源。我还没有工作上传。 wikisend.com/download/811662/test.zip

谢谢。

    //By default, javascript will not complete a hover transition unless the mouse 
remains over the entire duration of the transition. This scrip will force the hover 
transition to completion.
$(document).ready(function() {
    $('.tile').live('mouseenter mouseleave', (function() {

    if (event.type == 'mouseover') {
        var $this = $(this);
        $this.addClass('hover');

        //prevents mouseleave from happening when user re-enters after exiting before time is up
        $this.data('box-hover-hovered', false);
        //tile is not ready for leaving hover state
        $this.data('box-hover-not-ready', true);
        var timeout = setTimeout(function() {
            //box will be ready after time is up
            var state = $this.data('box-hover-hovered');
            if (state) { //this is entered when user exits before
                //time is up
                $this.removeClass('hover');
            }
            $this.data('box-hover-not-ready', false);
            //it's ready
        }, 400); // .1 second
        // Remove previous timeout if it exists
        clearTimeout($this.data('box-hover-timeout'));
        //stores current timer id (current timer hasn't executed yet)
        $this.data('box-hover-timeout', timeout);
    }

    else {
        var $this = $(this);

        // If not not-ready, do nothing
        // By default, the value is `undefined`, !undefined === true
        var not_ready = $this.data('box-hover-not-ready');
        if (!not_ready) {
            //if user remains hovering until time is up.
            $this.removeClass('hover');
        } else {
            //user would not have completed the action
            $this.data('box-hover-hovered', true);
        }
    }
}));
});​

2 个答案:

答案 0 :(得分:3)

好的,如果您想要做的是确保在悬停之前完成悬停转移的无悬停,您可以这样做:

$(document).ready(function() {
    $(document.body).on('mouseenter mouseleave', '.tile', function(event) {
        var $this = $(this);
        if (event.type == 'mouseenter') {
            $this.data("hovering", true);
            if (!$this.hasClass('hover')) {
                $this.addClass('hover');
                var timeout = setTimeout(function() {
                    $this.removeData("timer");
                    if (!$this.data("hovering")) {
                        $this.removeClass('hover');
                    }
                }, 400);
                $this.data("timer", timeout);
            }
        } else {
            $this.data("hovering", false);
            // if no timer running, then just remove the class now
            // if a timer is running, then the timer firing will clear the hover
            if (!$this.data("timer")) {
                $this.removeClass('hover');
            }
        }
    });
});​

这是一个包含完整代码注释的工作演示:http://jsfiddle.net/jfriend00/rhVcp/

这是一个有关工作原理的详细说明:

  • 首先,我切换到.on()因为现在所有版本的jQuery都不推荐使用.live()。您应该将document.body替换为.tile静态对象的最近祖先。
  • 该对象保留一个.data("hovering", true/false)项,该项始终告诉我们鼠标是否在对象上,与.hover类状态无关。当计时器触发时需要这样,所以我们知道在那时需要设置真实状态。
  • 当发生mouseenter事件时,我们会检查hover类是否已存在。如果是这样,那就无所事事了。如果hover类不存在,那么我们添加它。由于之前没有,我们只是添加了它,这将是hover转换的开始。
  • 我们为转换的长度设置了一个计时器,我们在对象上设置了.data("timer", timeout)项,以便将来的代码可以知道计时器已在运行。
  • 如果我们在此计时器触发之前得到mouseleave,我们会看到.data("timer")存在,我们将不执行任何操作(从而允许转换完成)​​。
  • 当计时器触发时,我们会.removeData("timer")删除该标记,然后我们会看到我们是否仍在使用.data("hovering")进行悬停。如果我们不再悬停(因为mouseleave在计时器运行时发生),我们会.removeClass("hover")将对象置于所需状态。如果我们碰巧仍然在盘旋,我们什么也不做,因为我们仍然在盘旋,所以对象已经处于正确的状态。

简而言之,当我们开始悬停时,我们设置悬停状态并启动计时器。只要该计时器正在运行,我们就不会更改对象的状态。当计时器触发时,我们设置对象的正确状态(悬停或不悬停,具体取决于鼠标的位置)。这可以保证悬停状态至少保持转换的时间和最小时间过去(所以转换完成)​​,我们更新对象的状态。

我非常小心地不使用任何全局变量,因此这可以在多个.tile对象上工作而不会有任何干扰。

在一个重要的设计点,你永远不会有多个计时器,因为只有在悬停类不存在时才设置计时器,我们现在只是添加它,一旦计时器运行,我们永远不会删除悬停类直到计时器完成。因此,一旦运行,就没有设置另一个计时器的代码路径。这简化了逻辑。这也意味着计时器只在第一次应用悬停类时才开始运行,这保证了我们只在第一次应用悬停类时强制执行时间。

至于性能,CSS过渡将占用所需的CPU - 这取决于浏览器的实现,我们无能为力。我们所能做的就是尽量减少CPU负载,以确保我们在每次鼠标转换进出时尽可能做到最小,并尽可能避免DOM操作,因为它们通常是最慢的操作类型。这里我们只添加hover类,当它还不存在时我们只在时间到期并且鼠标不再在它上面时删除它。其他所有操作只是.data()操作,这些操作只是javascript哈希表操作,应该非常快。这应该只在需要时触发浏览器重排,这是我们能做的最好的。

选择器性能不应成为问题。这是委托事件处理,并且实时检查的唯一选择器(在事件发生时)是.tile,这是一个非常简单的检查(只检查event.target是否有该类 - 没有其他需要检查对象。对性能来说重要的一件事就是为委托事件绑定选择尽可能接近'.tile'的祖先,因为这将花费更少的时间在事件处理之前冒泡它并且你不会结束在一个条件中,有许多委托事件都绑定到同一个对象,这可能很慢,这就是为什么.live()被弃用的原因。

答案 1 :(得分:1)

编辑:请参阅下面的dbaupp评论。

马上我会说使用更具体的选择器而不仅仅是$('。tile')。这只是意味着将您的选择器更改为$('div.tile')或更好的$('#someparentid div.tile')。

<击>

这样您就不必遍历整个DOM搜索匹配的类。

我担心Mikko看起来是对的 http://jsperf.com/jquery-right-to-left-selectors

显然我的解决方案是一个古老的误解。 Good ways to improve jQuery selector performance?

加速该选择器的唯一方法是通过id调用它(例如,$('#tile')),遗憾的是,这似乎不是一个适合你的解决方案,因为你可能有多个磁贴元素。