多个setTimeout和clearTimeout相互干扰问题

3
我想实现以下功能。我有多个带按钮的表单。当我点击该按钮时,我希望它在2秒内更改颜色和文本。如果在此时间范围内再次点击,则重置并重新开始旋转。
到目前为止,我的代码按预期工作。但是,在这2秒内单击另一个表单的按钮时,旋转会停止,因为timerId被clearTimeout清除。
请查看我的示例,当您单击“order1”并在2秒内单击“order2”时,“order1”不会返回其原始状态。是否有办法让其他表单不互相干扰?

var timerId, delay = 2000;

$(".product-form").submit(function(e){
 
var button = $(this).find('button[type=submit]'),
  order = button.find("span:eq(0)"),
  added = button.find("span:eq(1)");

  clearTimeout(timerId);
  added.hide();
  order.hide();
  button.addClass("green");
  added.slideDown("slow");

  timerId = setTimeout(function() {
    added.hide();
    order.show();
    button.removeClass("green");
  }, delay); 
  e.preventDefault();
});
form {
  float: left;
  padding: 10px;
}

.hide {
  display: none
}

#button {
  width: 200px;
  height: 40px;
  border: none;
  background: orange;
}

.green {
  background: green !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order1</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>
 
<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order2</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>

<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order2</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>


如果用户先点击第一个按钮,然后再点击第二个按钮,第一个按钮的计时器应该被取消吗?(我认为不应该,但是...) - T.J. Crowder
2个回答

1
你的代码为所有按钮使用了同一个 timerId,这就是它们互相影响的原因。你可以将 timerId 存储在每个按钮上,参见下面的 *** 行:

var delay = 2000; // *** No timerId here

$(".product-form").submit(function(e) {
  var $this = $(this),                               // ***
      timerId = $this.data("timerId"),               // ***
      button = $this.find('button[type=submit]'),    // ***
      order = button.find("span:eq(0)"),
      added = button.find("span:eq(1)");

  clearTimeout(timerId);
  added.hide();
  order.hide();
  button.addClass("green");
  added.slideDown("slow");

  $this.data("timerId", setTimeout(function() {      // ***
    added.hide();
    order.show();
    button.removeClass("green");
  }, delay));                                        // ***
  e.preventDefault();
});
form {
  float: left;
  padding: 10px;
}

.hide {
  display: none
}

#button {
  width: 200px;
  height: 40px;
  border: none;
  background: orange;
}

.green {
  background: green !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order1</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>
 
<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order2</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>

<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order2</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>


@AndrewSvietlichnyy - 谢谢,已修复! - T.J. Crowder
只有一个问题,使用 $this = $(this) 的优点是什么? - PHPhil
@PHPhil - $() 是一个分配对象的函数。如果你要在多个地方使用 $(this),最好只调用 一次 函数并重复使用结果对象,而不是反复创建和丢弃对象。在这个特定的代码中,这不是什么大问题,但这是一个好习惯。 - T.J. Crowder

0

你只需要做三个简单的更改:

  1. timerId 改为数组;
  2. 删除 clearTimeout 语句;
  3. 由于 timerId 现在是一个数组,所以使用 timerId[timerId.length] = setTimeout(function() {

工作示例:

var timerId = [];
var delay = 2000;

$(".product-form").submit(function(e){
 
var button = $(this).find('button[type=submit]'),
  order = button.find("span:eq(0)"),
  added = button.find("span:eq(1)");

  added.hide();
  order.hide();
  button.addClass("green");
  added.slideDown("slow");

  timerId[timerId.length] = setTimeout(function() {
    added.hide();
    order.show();
    button.removeClass("green");
  }, delay); 
  e.preventDefault();
});
form {
  float: left;
  padding: 10px;
}

.hide {
  display: none
}

#button {
  width: 200px;
  height: 40px;
  border: none;
  background: orange;
}

.green {
  background: green !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order1</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>
 
<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order2</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>

<form class="product-form" action="#cart" method="post">
  <button id="button" type="submit">
    <span>Order3</span>
    <span class="hide">&#10003; Added</span>
  </button>
</form>


两个问题:1. 这会创建一个无用的不断增长的数组(其值从未被使用)。2. 按钮上的两次点击不会取消该按钮的先前计时器,正如 OP 明确所期望的那样。 - T.J. Crowder
是的,很好的观点。1)可以通过确保数组永远不超过x个元素来轻松解决:if (array.length > x) {array.shift();} - Rounin
  1. 通过构建第二个与第一个数组平行的数组很容易解决。如果数组的最新元素与前一个元素相同,则可以调用 clearTimeoutif (buttonNameArray[(array-length - 1)] === buttonNameArray[(array.length - 2)]) {clearTimeout(timerId[(timerId.length - 1)]);}
- Rounin
由于现在有两个并行数组,用一个嵌套的单一数组来替换可能更简单,其中每个元素都是一个包含计时器和按钮名称字符串的双元素数组。 - Rounin

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