基于另一个元素的状态,切换DOM元素的值的最有效方式是什么?

3

假设我们有一个文本字段,在每次键按下事件时执行以下代码:

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 时避免调用。 - Denys Séguret
SOMESTRING == "" 可以被替换为 !SOMESTRING - Ian
哈。每个答案都经过了100次迭代,基于评论和比较,现在它们几乎完全相同。多么有趣啊。 - Lee Meador
哈,确实 :) 我总是在接受答案之间留出一些时间,因为答案会越来越好 :P。不过我觉得这个问题的标题还可以更好一些。有没有人能想到一个更通用的标题,请编辑一下。 - BIOS
我认为你在试图过度概括这个问题时应该小心。目前,这个问题严重依赖于DOM,而查询和操作DOM是非常“昂贵”的,因此有更有效的方法。如果你只是简单地操作字符串,那么你的代码优化可能会非常不同,因为“缓存”和存储“状态”可能只会增加复杂性,使方法变得不够高效。 - MrWhite
我最初是在寻找更一般的最佳实践答案,但我想这个例子提供了一个非常具体的方法,应该对其他人有用。我的唯一担忧是它是否是一个非常易于搜索的标题,虽然为这个主题想出简洁的标题并不容易。这就是为什么我最初的标题如此模糊!哦,谢谢您对当前标题的编辑。显然,这是一个很好的改进 :) - BIOS
7个回答

7

我会缓存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,这样在获取事件之前就会更改输入框的值。

4

这个方法甚至可以在你一直按住键盘的情况下工作;)

想要更高的性能?使用纯JS。 示例

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


4
这是我能想到的最快、最好的方式:

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/

这个例子使用闭包在初始化后存储输入和span元素。你可以像使用普通函数一样使用它,可以将其绑定到多个事件上,并且仍然有效。 请注意,在绑定事件时必须执行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/


这也是一个不错的解决方案。点赞了。感谢您的回答。 - BIOS
你应该在第二个例子中也保留闭包的思想。这是一个非常好的方法!赞! - BIOS
谢谢!第二个例子使用了相同的闭包。我们只需要将它们作为参数传递,因为它们已经在闭包的本地作用域中可用,所以不必单独存储inputspan - 参见:keyUpEvent(input, span){ - greenish

3

这取决于代码执行的频率。如果它只在用户按下按钮或类似情况下执行,那么使用第一种方法就可以了;如果它在快速计时器上运行,那么可能不行。

可以这样做:

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语句”和变量“text”来解决问题,这两者在你的情况下都是不必要的。 - bugwheels94
@Ankit 这里有同样数量的“if”语句。每次执行代码时都会执行2个。 - Lee Meador
@LeeMeador在问题一中每次执行的都是代码。 - bugwheels94
@Ankit 你正在看第一个例子。我正在看更高效的第二个例子,它避免了每次都干扰DOM。用一个改变DOM来交换“if”似乎对我来说是一个很好的权衡。 - Lee Meador

1
你可以使用三目运算符来简化它。
$('#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元素将加快查找/写入速度。它将比仅写入慢。

1

初始化:

var isBlank = true;

$('#my_span').html('我的输入为空');

键盘按键抬起:

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。
此外,测试长度属性可能比将字符串与 "" 进行比较更快,因为解释器不必从字符串字面量中创建字符串对象。

-1
如何将当前值保存到变量中,只需测试变量即可查看是否需要更改它。在需要更改之前不要操作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”)。 - Lee Meador

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接