简体   繁体   English

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

[英]Reduce CPU usage in jQuery DOM manipulation

Is there anything I can do to this code to make it more CPU efficient (it is hitting about 80 percent of my CPU now)? 我能对这段代码做些什么来提高它的CPU效率(它现在大约占我CPU的80%)? I learned javascript yesterday, so it may just be that I am inexperienced. 我昨天学习了javascript,所以可能只是因为我没经验。 This code controls the transitions of a rather large array of tiles. 此代码控制相当大的切片阵列的转换。 On mouseover, the tiles flip over and flip back on mouseoff. 在鼠标悬停时,瓷砖翻转并在鼠标移开时翻转。 There will be several threads running at once, I don't see a way around that one. 将会有几个线程同时运行,我没有看到绕过那个线程的方法。 I am using this script because I need to control exactly what the transitions do in ways that webkit-transitions does not support. 我正在使用这个脚本,因为我需要以webkit-transitions不支持的方式准确控制转换的作用。 Hopefully the comments are meaningful enough to shed some light on the code. 希望这些评论有足够的意义来阐明代码。 The function is live because the array of tiles is created in javascript when the page is loaded. 该函数是实时的,因为在加载页面时,在javascript中创建了tile数组。 After that, there are no more tiles created. 之后,没有更多的瓷砖创建。

The source can be found here. 来源可以在这里找到。 I don't have a working upload yet. 我还没有工作上传。 wikisend.com/download/811662/test.zip wikisend.com/download/811662/test.zip

Thank you. 谢谢。

    //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);
        }
    }
}));
});​

OK, if what you want to do is to make sure that the no-hover to hover transiton completes before unhovering, you can do it like this: 好吧,如果您想要做的是确保在悬停之前完成悬停转移的无悬停,您可以这样做:

$(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');
            }
        }
    });
});​

And here's a working demo with full code comments: http://jsfiddle.net/jfriend00/rhVcp/ 这是一个带有完整代码注释的工作演示: http//jsfiddle.net/jfriend00/rhVcp/

This a somewhat detailed explanation of how it works: 这是一个有关如何工作的详细解释:

  • First off, I switched to .on() because .live() is deprecated now for all versions of jQuery. 首先,我切换到.on()因为现在所有版本的jQuery都不推荐使用.live() You should replace document.body with the closest ancestor of the .tile object that is static. 您应该将document.body替换为静态的.tile对象的最近祖先。
  • The object keeps a .data("hovering", true/false) item that always tells us whether the mouse is over the object or not, independent of the .hover class state. 该对象保留一个.data("hovering", true/false)项,该项始终告诉我们鼠标是否在对象上,与.hover类状态无关。 This is needed when the timer fires so we know what the true state needs to be set to at that point. 当计时器触发时需要这样,所以我们知道在那一点需要设置真实状态。
  • When a mouseenter event occurs, we check to see if the hover class is already present. mouseenter事件发生时,我们检查,如果hover类是已经存在。 If so, there is nothing to do. 如果是这样,那就无所事事了。 If the hover class is not present, then we add it. 如果没有hover类,那么我们添加它。 Since it wasn't previously present and we just added it, this will be the start of the hover transition. 由于之前没有出现,我们只是添加了它,这将是hover过渡的开始。
  • We set a timer for the length of the transition and we set a .data("timer", timeout) item on the object so that future code can know that a timer is already running. 我们为转换的长度设置了一个计时器,我们在对象上设置了一个.data("timer", timeout)项,以便将来的代码可以知道计时器已在运行。
  • If we get mouseleave before this timer fires, we will see that the .data("timer") exists and we will do nothing (thus allowing the transition to complete). 如果我们在此计时器触发之前得到mouseleave ,我们将看到.data("timer")存在,我们将什么都不做(从而允许转换完成)​​。
  • When the timer fires, we do .removeData("timer") to get rid of that marker and then we see whether we're still hovering or not with .data("hovering") . 当计时器触发时,我们执行.removeData("timer")以摆脱该标记,然后我们看到我们是否仍在使用.data("hovering") If we are no longer hovering (because mouseleave happened while the timer was running), we do .removeClass("hover") to put the object into the desired state. 如果我们不再徘徊(因为mouseleave事当定时器正在运行),我们做.removeClass("hover")使对象到所需的状态。 If we happen to be still hovering, we do nothing because we're still hovering so the object is already in the correct state. 如果我们碰巧仍然在盘旋,我们什么也不做,因为我们仍然在盘旋,所以对象已经处于正确的状态。

In a nutshell, when we start a hover, we set the hover state and start a timer. 简而言之,当我们开始悬停时,我们设置悬停状态并启动计时器。 As long as that timer is running, we don't change the state of the object. 只要该计时器正在运行,我们就不会更改对象的状态。 When the timer fires, we set the correct state of the object (hover or no hover, depending upon where the mouse is). 当计时器触发时,我们设置对象的正确状态(悬停或不悬停,具体取决于鼠标的位置)。 This guarantees that the hover state will stay on for at least the amount of time of the transition and when the min time passes (so the transition is done), we update the state of the object. 这保证了悬停状态将至少保持在转换的时间量和最小时间过去(因此转换完成)​​,我们更新对象的状态。

I've very carefully not used any global variables so this can work on multiple .tile objects without any interference between them. 我非常小心地不使用任何全局变量,因此这可以在多个.tile对象上工作而不会产生任何干扰。

In an important design point, you can never get more than one timer going because a timer is only ever set when the hover class did not exist and we are just adding it now and once the timer is running, we never remove the hover class until the timer is done. 在一个重要的设计点,你永远不会有一个以上的计时器,因为只有在悬停类不存在时才设置计时器我们现在只是添加它,一旦计时器运行,我们永远不会删除悬停类直到计时器完成。 So, there's no code path to set another timer once one is running. 因此,一旦运行,就没有设置另一个计时器的代码路径。 This simplifies the logic. 这简化了逻辑。 It also means that the timer only ever starts running from when the hover class is first applied which guarantees that we only enforce the time with the hover class from when it's first applied. 它还意味着计时器仅在首次应用悬停类时才开始运行,这保证了我们只在第一次应用悬停类时强制执行时间。

As for performance, the CSS transition is going to take whatever CPU it takes - that's up to the browser implementation and there's nothing we can do about that. 至于性能,CSS过渡将占用所需的CPU - 这取决于浏览器的实现,我们无能为力。 All we can do to minimize the CPU load is to make sure we're doing the minimum possible on each mouse transition in/out and avoid DOM manipulations whenever possible as they are typically the slowest types of operations. 我们所能做的就是尽量减少CPU负载,以确保我们在每次鼠标转换进出时尽可能做到最小,并尽可能避免DOM操作,因为它们通常是最慢的操作类型。 Here we're only adding the hover class when it doesn't already exist and we're only removing it when the time has expired and the mouse is no longer over it. 这里我们只添加hover类,当它尚不存在时,我们只在时间到期并且鼠标不再在它上面时删除它。 Everything else is just .data() operations which are just javascript hash table manipulations which should be pretty fast. 其他所有东西都只是.data()操作,它们只是javascript哈希表操作,应该非常快。 This should trigger browser reflow only when needed which is the best we can do. 这应该只在需要时触发浏览器重排,这是我们能做的最好的。

Selector performance should not be an issue here. 选择器性能不应成为问题。 This is delegated event handling and the only selector that is being checked live (at the time of the events) is .tile and that's a very simple check (just check if the event.target has that class - no other objects need to be examined. One thing that would be important to performance is to pick an ancestor as close as possible to '.tile' for the delegated event binding because this will spend less time bubbling the event before it's processed and you will not end up with a condition where there are lots of delegated events all bound to the same object which can be slow and is why .live() was deprecated. 这是委托事件处理,并且唯一正在被检查的事件(在事件发生时)是.tile ,这是一个非常简单的检查(只检查event.target是否有该类 - 不需要检查其他对象)对性能而言重要的一件事就是为委托事件绑定选择一个尽可能接近'.tile'的祖先,因为这样可以减少在事件处理之前冒泡的时间,并且你不会最终得到一个条件:有很多委托事件都绑定到同一个对象,这可能很慢,这就是为什么.live()被弃用的原因。

Edit: See dbaupp's comment below. 编辑:请参阅下面的dbaupp评论。

Right off the bat I'd say use a more specific selector instead of just $('.tile'). 马上我会说使用更具体的选择器而不仅仅是$('。tile')。 This simply means change your selector to something like $('div.tile') or better yet $('#someparentid div.tile') instead. 这只是意味着将您的选择器更改为$('div.tile')或更好的$('#someparentid div.tile')。

\n

This way you won't have to traverse the entire DOM searching for a matching class. 这样您就不必遍历整个DOM搜索匹配的类。

I'm afraid Mikko appears to be quite right http://jsperf.com/jquery-right-to-left-selectors 我担心Mikko看起来是对的http://jsperf.com/jquery-right-to-left-selectors

Apparently my solution was an old misconception. 显然我的解决方案是一个古老的误解。 Good ways to improve jQuery selector performance? 改善jQuery选择器性能的好方法?

The only way to speed up that selector is to call it by id (eg, $('#tile')), which unfortunately doesn't seem like a solution that would work for you given that you likely have multiple tile elements. 加速该选择器的唯一方法是通过id调用它(例如,$('#tile')),遗憾的是,这似乎不是一个适合你的解决方案,因为你可能有多个tile元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM