【问题标题】:How to use jQuery promise on multiple change events如何在多个更改事件上使用 jQuery Promise
【发布时间】:2017-06-23 12:03:58
【问题描述】:

我有以下代码,它使用基本的选项链父/子关系显示和隐藏选择菜单。一旦发生变化,我需要调用一个函数。

目前我正在这样做,如下所示,为每个<select> 触发更改事件,然后调用该函数。在现实生活中,这是一个 ajax 调用,但这与问题无关。

我下面的代码有效。我想解决的问题是如何在不依赖更改事件的情况下完成此操作,因为它会不必要地多次触发该功能。我想在父级更改时简单地在$.each() 循环的末尾放置一个函数调用,但由于涉及的动画,它会失败,因为有问题的元素有时仍然可见/隐藏。我通过在每个 slideUp()slideDown() 中放置一个回调来解决这个问题,但这正是我最终多次触发事件的确切原因。

我有一种强烈的感觉,我正在寻找的答案使用 jQuery promise(),其中指出:

.promise() 方法返回一个动态生成的 Promise,一旦绑定到集合的特定类型的所有操作(无论是否排队)都结束,该 Promise 就会被解析。

这正是我想要做的,但我完全被 promise() 弄糊涂了,这是正确的用法。我明白了这个概念,但我可以使用一些帮助来了解如何将所有这些更改事件排队到一个 promise() 中,然后在它们全部完成时调用我的函数。

$('select[data-parent="true"]').change(function() {
  var parentValue = +this.value;
  var parentId = $(this).data('parent_id');
  $('div[data-parent_id="' + parentId + '"]').each(function() {
    var parentValues = $(this).data('parent_values');
    if (parentValues.indexOf(parentValue) !== -1) {
      $(this).slideDown(1000, function() {
        $(this).find('select').change();
      });
    } else {
      $(this).slideUp(1000, function() {
        $(this).find('select').change();
      });
    }
  });
});
$('select').on('change', function() {
  var results = '';
  $('div.option').each(function() {
    results += $(this).find('select').prop('name') + ' IS VISIBLE: ' + +$(this).is(':visible') + '<br>';
  });
  $('#results').html(results);
});
div {
  padding: 5px 10px;
}

div.child {
  display: none
}

select {
  width: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div class="option">
  <select name="container" data-parent="true" data-parent_id="10">
  <option value="">--- Select a container---</option>
		<option value="1">Box</option>
		<option value="2">Bag</option>
		<option value="3">Pallet</option>
	</select>
</div>
<div class="child option" data-parent_id="10" data-parent_values="[1]">
  <select name="box color">
		<option value="100">Red</option>
		<option value="200">Blue</option>
	</select>
</div>
<div class="child option" data-parent_id="10" data-parent_values="[2]">
  <select name="bag color">
		<option value="300">Green</option>
		<option value="400">Yellow</option>
	</select>
</div>
<div class="child option" data-parent_id="10" data-parent_values="[3]">
  <select name="pallet color">
		<option value="500">Black</option>
		<option value="600">Yellow</option>
	</select>
</div>
<div id="results">
</div>

【问题讨论】:

    标签: jquery promise


    【解决方案1】:

    由于 jQuery 动画可以返回承诺 .promise() 并使用 .map 而不是 .each - 代码可以很简单:

    $('select[data-parent="true"]').change(function() {
        var parentValue = +this.value;
        var parentId = $(this).data('parent_id');
        $.when.apply($, $('div[data-parent_id="' + parentId + '"]').map(function(i, v) {
            var parentValues = $(this).data('parent_values');
            if (parentValues.indexOf(parentValue) !== -1) {
                return $(this).slideDown(1000).promise();
            } else {
                return $(this).slideUp(1000).promise();
            }
        })).done(function() {
            var results = '';
            $('div.option').each(function() {
                results += $(this).find('select').prop('name') + ' IS VISIBLE: ' + +$(this).is(':visible') + '<br>';
            });
            $('#results').html(results);
        });
    });
    div {
      padding: 5px 10px;
    }
    
    div.child {
      display: none
    }
    
    select {
      width: 200px;
      margin: 4px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <div class="option">
      <select name="container" data-parent="true" data-parent_id="10">
      <option value="">--- Select a container---</option>
    		<option value="1">Box</option>
    		<option value="2">Bag</option>
    		<option value="3">Pallet</option>
    	</select>
    </div>
    <div class="child option" data-parent_id="10" data-parent_values="[1]">
      <select name="box color">
    		<option value="100">Red</option>
    		<option value="200">Blue</option>
    	</select>
    </div>
    <div class="child option" data-parent_id="10" data-parent_values="[2]">
      <select name="bag color">
    		<option value="300">Green</option>
    		<option value="400">Yellow</option>
    	</select>
    </div>
    <div class="child option" data-parent_id="10" data-parent_values="[3]">
      <select name="pallet color">
    		<option value="500">Black</option>
    		<option value="600">Yellow</option>
    	</select>
    </div>
    <div id="results">
    </div>

    【讨论】:

    • 谢谢 - 我一直是 map() 的粉丝,它比我自己想出的更优雅和直观。
    • 我很惊讶关于这个主题的问答在 SO 上并不常见。我找到了几个很好的线程,但没有我预期的那么多,因为可能需要这个一直在上升。我认为很多人只是满足于它的工作原理,并且不介意在他们只需要一个时触发 20 个事件。
    【解决方案2】:

    经过一个小时的试验,得出了以下解决方案。创建一个$.Deferred() 对象数组并使用$.when 来解析它们。棘手的部分来自this answer,它解释了如何将数组传递给$.when。希望这可以帮助像我一样在 jQuery 延迟对象上苦苦挣扎的人:

    $('select[data-parent="true"]').change(function() {
      var deferred = [];
      var parentValue = +this.value;
      var parentId = $(this).data('parent_id');
      $('div[data-parent_id="' + parentId + '"]').each(function(i, v) {
        deferred[i] = $.Deferred();
        var parentValues = $(this).data('parent_values');
        if (parentValues.indexOf(parentValue) !== -1) {
          $(this).slideDown(1000, function() {
            deferred[i].resolve();
          });
        } else {
          $(this).slideUp(1000, function() {
            deferred[i].resolve();
          });
        }
      });
      $.when.apply($, deferred).done(function() {
        var results = '';
        $('div.option').each(function() {
          results += $(this).find('select').prop('name') + ' IS VISIBLE: ' + +$(this).is(':visible') + '<br>';
        });
        $('#results').html(results);
      });
    });
    div {
      padding: 5px 10px;
    }
    
    div.child {
      display: none
    }
    
    select {
      width: 200px;
      margin: 4px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <div class="option">
      <select name="container" data-parent="true" data-parent_id="10">
      <option value="">--- Select a container---</option>
    		<option value="1">Box</option>
    		<option value="2">Bag</option>
    		<option value="3">Pallet</option>
    	</select>
    </div>
    <div class="child option" data-parent_id="10" data-parent_values="[1]">
      <select name="box color">
    		<option value="100">Red</option>
    		<option value="200">Blue</option>
    	</select>
    </div>
    <div class="child option" data-parent_id="10" data-parent_values="[2]">
      <select name="bag color">
    		<option value="300">Green</option>
    		<option value="400">Yellow</option>
    	</select>
    </div>
    <div class="child option" data-parent_id="10" data-parent_values="[3]">
      <select name="pallet color">
    		<option value="500">Black</option>
    		<option value="600">Yellow</option>
    	</select>
    </div>
    <div id="results">
    </div>

    【讨论】:

    • 当然,知道 jQuery 动画可以“本机”返回一个承诺 - 并且使用 .map 而不是 .each - 代码可以进一步简化为 jsfiddle.net/0ruud9xL
    • 嗯,这几乎是我一整天看到的最酷的东西。您应该将其作为另一个答案发布,我很乐意将其标记为已接受。谢谢!
    • 添加了答案 - 虽然我没有测试它 - 从头开始​​,现在测试它
    猜你喜欢
    • 1970-01-01
    • 2016-03-09
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 2016-02-15
    • 1970-01-01
    相关资源
    最近更新 更多