【问题标题】:What is the most efficient way of toggling a DOM elements value based on the state of another element?根据另一个元素的状态切换 DOM 元素值的最有效方法是什么?
【发布时间】:2013-06-14 01:44:26
【问题描述】:

假设我们有一个文本字段,它在每个 keydown 事件上执行以下代码:

if( $('#my_input').val() == '' )
  $('#my_span').html('my input is blank');
else
  $('#my_span').html('My input is not blank');

显然,我们正在执行的代码可能会将某些东西(#my_span 元素)的状态设置为它已经存在的状态,这似乎效率低下。但我很好奇替代方案是什么?添加更多检查,例如:

if ( $('#my_input').val() == '' ){
  if ( $('#my_span').html() != 'my input is blank' )
       $('#my_span').html('my input is blank');
}
else if ( $('#my_span').html() != 'My input is not blank' )
          $('#myspan').html('My input is not blank');

后者更有效吗?在我看来,这似乎更“正确”,但显然它是更多代码,我不确定它比第一个示例效率多少。

我知道前者总是涉及 DOM 操作,这将影响计算相对效率,但我之前在非 DOM 相关代码中遇到过类似的情况,所以想知道在所有情况下最好的方法是什么。在将某项设置为新值之前,是否应该始终对某项的值进行额外检查?

编辑:

我的最终版本实际上使用了此处的答案组合,因此感谢所有人的出色回复。总而言之,我现在:

  • 在闭包中缓存 jquery 对象
  • 使用状态来确定新状态的分配

顺便说一句,keydown 上的 setTimeout 是一种立即获取输入字段值的好方法。再次感谢。

【问题讨论】:

  • 您可以 1) 缓存元素,2) 使用布尔值来存储状态,而不是在不需要时调用 html
  • SOMESTRING == "" 可以替换为!SOMESTRING
  • 哈。每个答案都基于 cmets 和比较经历了 100 次迭代,现在它们几乎完全相同。真有趣。
  • 哈确实 :) 随着答案逐渐变得更好,我总是在接受之间留下差距:P。我觉得这个问题仍然可以有一个更好的标题。一个更通用的?如果有人能想到更好的,请编辑。
  • 我认为你在试图概括这一点时应该小心。就目前而言,问题严重依赖于 DOM,它的查询和操作昂贵,因此需要更有效的方法。例如,如果您只是简单地操作字符串,那么您的代码优化可能会非常不同,因为“缓存”和存储“状态”只会增加复杂性并使方法效率降低。

标签: javascript jquery performance


【解决方案1】:

我会缓存 jQuery 对象并使用布尔值来存储状态,而不是在您不需要时调用 html

(function(){
   var i = $('#my_input'), s=$('#my_span'), blank, check = function() {
      if (i.val()=='') {
         if (blank!==true) s.html('my input is blank');
         blank = true;
      } else {
         if (blank!==false) s.html('my input is not blank');
         blank = false;
      }
   };
   i.keyup(check);
   check(); // so that the span is initially filled
})();

请注意,您需要的不是keydown,而是keyup,以便在您获得事件之前更改输入的值。

【讨论】:

    【解决方案2】:

    如果你一直按这个键,这个方法甚至可以工作;)

    性能?去纯 JS。 Fiddle

    //before event binding
    var my_input = document.getElementById('my_input'),
      my_span = document.getElementById('my_span');
    
    $(my_input).on('keydown', function () {
      //inside event handler
      var value = my_input.value
      , prevVal = my_input.prevVal
      ;
    
      if (value && prevVal && prevVal !== value) {
        return;
      }
    
      //timeout to return event handler execution early 
      //(ie: differ DOM manipulation from the event handler. 
      //So, UX will extra smooth ;) )
      setTimeout(function () {
        fieldStatusUpdater(my_input.value);
      }, 1);
    
    });
    
    function fieldStatusUpdater(value) {
      if (my_input.value === '') {
        my_span.innerHTML = 'my input is blank';
      } else {
        my_span.innerHTML = 'My input is not blank';
      }
      my_input.prevVal = value;
    }
    

    【讨论】:

      【解决方案3】:

      这是我能想到的最快最好的:

      function keyUpEvent(){
          var state = null,
              input = $('#my_input'),
              span = $('#my_span');
      
          return function(){
              var test = input.val() === '';
              if( test === state) return;
              if(test)
                span.html('my input is blank');
              else
                span.html('My input is not blank');
              state = test;
          }
      }
      $('#my_input').keyup(keyUpEvent());
      

      http://jsfiddle.net/TMb8T/

      这使用闭包在初始化后存储输入和跨度元素。而且您可以(几乎)像使用普通功能一样使用它,因此您可以绑定它执行多个事件并且它仍然有效。 注意,绑定事件时必须执行keyUpEvent

      补充: 您现在还可以执行以下操作:

      function keyUpEvent(input, span){
          var state = null;
      
          return function(){
              var test = input.val() === '';
              if( test === state) return;
              if(test)
                  span.html('My input is blank');
              else
                  span.html('My input is not blank');
              state = test;
          }
      }
      
      
      $('#my_input').keyup( keyUpEvent($('#my_input'), $('#my_span')) );
      $('#my_input2').keyup( keyUpEvent($('#my_input2'), $('#my_span2')) );
      

      http://jsfiddle.net/TMb8T/2/

      这样,您可以使用一个事件处理程序轻松检查整个表单的每个输入。

      附加 2:如果您想让版本 2 即使在按键被按下的情况下也能正常工作;)

      替换这个:

      $('#my_input').keyup( keyUpEvent($('#my_input'), $('#my_span')) );
      

      有了这个:

      $('#my_input').keydown(function(){
          setTimeout(keyUpEvent($('#my_input'), $('#my_span')),1);
      });
      

      http://jsfiddle.net/TMb8T/4/

      【讨论】:

      • 这也是一个不错的解决方案。赞成。感谢您的回答。
      • 你也应该保留第二个例子中的闭包思想。这是一个非常好的方法!荣誉。
      • 谢谢!第二个示例使用相同的闭包。我们不必单独存储inputspan,因为通过将它们作为参数传递,它们已经在闭包的本地范围内可用 - 请参阅:keyUpEvent(input, span){
      【解决方案4】:

      这实际上取决于您执行代码的频率。如果它只在用户按下按钮或类似的东西时执行,使用第一个按钮就可以了,它会在快速计时器上运行,那么它可能不会。

      这样做:

      var text;
      if ( $('#my_input').val() == '' )
          text = 'my input is blank';
      else 
          text = 'My input is not blank';
      
      if ( $(#my_span).html() != text )
          $('#my_span').html(text);
      

      【讨论】:

      • 您已经通过添加一个“if 语句”和 var 文本(在您的情况下都不需要)来解决这个问题
      • @Ankit 它的“if”语句数量相同。 2 每次通过代码执行。
      • @LeeMeador 有问题,每次通过代码执行一次
      • @Ankit 您正在查看第一个示例。我正在查看更有效的第二个示例,它可以避免每次都干扰 DOM。对我来说,用“如果”来改变 DOM 似乎是一个很好的权衡。
      【解决方案5】:

      初始化:

      var isBlank = true;

      $('#my_span').html('my input is blank');

      键位:

      if(!isBlank){
          if( $('#my_input').val().length == 0){
              isBlank = true;
              $('#my_span').html('my input is blank');
          }
      } else {
          if($('#my_input').val().length){
              isBlank = false;
              $('#my_span').html('my input is not blank');
          }
      }
      

      这样你只在状态改变时操作 DOM。

      另外,测试长度属性实际上可能比针对 "" 测试字符串更快,因为解释器不必从字符串文字创建 String 对象。

      【讨论】:

        【解决方案6】:

        你可以用三元来简化它

        $('#my_span').html( $('#my_input').val() == '' ? 'my input is blank' : 'My input is not blank' );
        

        更具可读性

        var text = $('#my_input').val() == '' ? 'my input is blank' : 'My input is not blank';
        $('#my_span').html(text);
        

        如果您关心速度,那么 DOM 重绘会比读取内容更快。这真的取决于页面结构/大小,浏览器。如果您想查看通过 1000 次迭代可以节省多少毫秒,JSPerf 是您的朋友。如果您发现性能问题,您真的应该寻找最快的。

        1. 没有检查,正在写入内容
          • 如果数据更改或未更改,您将面临更新 DOM 的惩罚
        2. 检查,写入内容
          • 阅读 HTML 会受到惩罚
          • 更新 DOM 会受到惩罚
        3. 检查,无需写入
          • 阅读 HTML 会受到惩罚

        现在 HTML 最有可能是不同的还是相同的等等?

        解决方案取决于您想要做什么。缓存 jQuery 元素将加快查找/写入速度。它会比写入慢。

        【讨论】:

          【解决方案7】:

          如何将当前值保存到变量中,然后只测试变量以查看是否必须更改它。除非您需要更改它,否则不要弄乱 DOM。 (你也可以在这里使用一个名为 isBlank 的布尔值来获得良好的效果):

          var currValue;
          if ( $('#my_input').val() == '' ) {
            if ( currValue != 'my input is blank' ) {
                 currValue = 'my input is blank';
                 $('#my_span').html(currValue);
            }
          } else if ( currValue != 'My input is not blank' ) {
                 currValue = 'My input is not blank';
                 $('#my_span').html(currValue);
          }
          

          您还提到这是在 keydown 事件处理程序中。

          1. 不要忘记在开始时运行一次此代码,以便将显示字段设置为显示输入字段开始时为空白。
          2. 不要忘记用户可以选择文本并右键单击以选择“剪切”并清空该字段或选择“粘贴”以将文本添加到该字段。同样,拖放操作也可以添加或删除文本。

          另类思路

          使用定期检查的定时事件可能会更好。有些人每秒可以键入 3 或 4 个按键。如果您将其定时为每 1 秒查看一次该字段,您可以减少由于此代码运行而导致的短期减速,并将其替换为长期持续使用 CPU 周期。但请记住,如果计算机没有做任何有趣的事情,就没有理由不使用 CPU 周期。

          【讨论】:

          • @Ankit 如果内部的“if”语句为真,则只有对html() 的调用。仅当保存的显示值错误时才会发生这种情况。例如,如果有内容,现在没有(因为输入了退格键),或者如果没有内容,现在有(因为输入了字母“a”。)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-09-23
          • 2020-05-06
          • 1970-01-01
          • 2023-03-22
          • 1970-01-01
          • 2018-11-27
          • 2014-11-29
          相关资源
          最近更新 更多