click 事件的执行顺序和优化问题

场景:

有100个li,点击 button 时,给 li 添加一个 class,然后再执行一个 for 循环

代码

    <script>
        $('.btn').on('click', function () {
            $('li').addClass('add')
            for (var i = 0; i < 100000; i++) {
                console.log(i)
            }
        });
    </script>

问题

1、发现每次执行这个 click 事件时,都必须要执行完 for 后才能给 li 添加 class,这是为什么?

2、每次执行 click 时,里面的循环是不可少的,如果优化这个循环?

阅读 3.2k
2 个回答

首先你要明白,浏览器不是一个整体,它有几大模块,JS 运行时(V8),渲染引擎(blink),主线程(处理插件、自动更新等),所以你在 JS 里操作 DOM,并不会直接反应到 DOM 上,而是由 V8 通知 blink。

重新渲染很花时间,如果需要 relayout 和 repaint 就更花时间。所以,为了避免 a.classList.add('add') -> a.classList.remove('add') -> a.classList.add('add') -> a.classList.remove('add') -> .... 这样反复循环浪费渲染效率,浏览器会缓存你对 DOM 的修改,在当前 JS 栈执行完毕后,再一次性修改完,一次性 relayout + repaint。

所以你会看到,虽然你已经在 JS 里给某个 DOM 节点加上了样式,但实际你只修改了它在 V8 里的映像,它的本体其实还没变。直到 JS 执行完才变。

第二个问题,我觉得是你实现的问题。作为前端,很忌讳让某个操作的时间能够被用户感知,如果这个操作需要很久,那么一方面你可以像楼上所说,通过 setTimeout 把它放到下个时间节点;另一方面,你应该好好考虑从算法的角度,是不是真有必要跑这么久的循环。

  1. jquery操作dom的动作是异步的,必须等到当前同步命令console全部执行完才开始执行。
  2. 若click内的逻辑中,前面给li添加add的动作不受后面的循环的影响,那么可以用setTimeout将循环包起来,变成异步执行。

      <script>
          $('.btn').on('click', function () {
              $('li').addClass('add');
              setTimeout(function() {
                  for (var i = 0; i < 100000; i++) {
                  console.log(i)
                  }
              }, 0);
    
          });
      </script>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题